2026-02-09 15:50:20 +08:00
import { Fragment } from 'react'
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' ,
'deepseek-3.2' ,
2026-02-27 18:47:02 +08:00
'qwen3.5-plus' ,
2026-02-06 18:49:06 +08:00
])
// Models grouped by brand, newest first
const MODEL_GROUPS : Record < string , string [] > = {
OpenAI : [ 'gpt-5.2' , 'gpt-5.1' , 'gpt-5' , 'gpt-5-mini' , 'gpt-4.1' , 'gpt-4.1-mini' ],
2026-02-27 18:47:02 +08:00
Qwen : [
'qwen3.5-plus' ,
'qwen3.5-flash' ,
'qwen3-coder-next' ,
'qwen-3-max' ,
'qwen-3-plus' ,
'qwen3:14b (ollama)' ,
],
2026-02-06 18:49:06 +08:00
DeepSeek : [ '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' ,
],
xAI : [ 'grok-4.1-fast' , 'grok-4' , 'grok-code-fast' ],
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
{ /* Models Section */ }
< 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
< p className = "text-sm text-gray-600 dark:text-gray-400 mb-4" >
2026-01-30 15:13:31 +08:00
{ isZh
? '推荐使用 ToolCall 能力强的轻量级模型。'
2026-02-06 18:49:06 +08:00
: 'Recommended: Fast, lightweight models with strong ToolCall capabilities.' }
2025-12-22 19:41:47 +08:00
</ p >
< 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 >
{ /* Tips Section */ }
< section className = "mb-10" >
2026-01-30 15:13:31 +08:00
< h2 className = "text-2xl font-semibold mb-4" >{ isZh ? '提示' : 'Tips' }</ h2 >
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-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 >
{ /* Security Section */ }
< section className = "mb-10" >
2026-01-30 15:13:31 +08:00
< h2 className = "text-2xl font-semibold mb-4" >
{ isZh ? '🔐 生产环境鉴权建议' : '🔐 Production Authentication' }
</ h2 >
2025-12-22 19:41:47 +08:00
< div className = "bg-yellow-50 dark:bg-yellow-950/20 border-l-4 border-yellow-500 p-5 rounded-r-lg mb-4" >
< p className = "text-sm font-semibold text-yellow-900 dark:text-yellow-200" >
2026-01-30 15:13:31 +08:00
{ isZh
? '⚠️ 永远不要把真实的 LLM API Key 发布到前端代码'
: '⚠️ Never commit real LLM API Keys to your frontend code' }
2025-10-22 22:35:25 +08:00
</ p >
2025-09-29 16:33:15 +08:00
</ div >
2025-12-22 19:41:47 +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" >
2026-01-30 15:13:31 +08:00
{ isZh ? '后端代理转发' : 'Backend Proxy Pattern' }
2025-10-21 22:36:59 +08:00
</ h3 >
2025-12-22 19:41:47 +08:00
< p className = "text-sm text-gray-600 dark:text-gray-400 mb-3" >
2026-01-30 15:13:31 +08:00
{ isZh
? '在后端搭建一个 LLM 流量转发接口,该接口使用与你网站上其他接口相同的鉴权方式,例如:'
: 'Set up a backend LLM proxy endpoint that uses the same authentication method as other APIs in your website, such as:' }
2025-10-22 22:35:25 +08:00
</ p >
2025-12-22 19:41:47 +08:00
< ul className = "text-sm text-gray-600 dark:text-gray-400 space-y-1" >
2026-01-30 15:13:31 +08:00
< li >{ isZh ? '• Session/Cookie 会话认证' : '• Session/Cookie-based authentication' }</ li >
< li >
{ isZh ? '• OIDC (OpenID Connect) 单点登录' : '• OIDC (OpenID Connect) single sign-on' }
</ li >
< li >
{ isZh ? '• 临时 Access Key 或 JWT Token' : '• Temporary Access Key or JWT Token' }
</ li >
2025-12-22 19:41:47 +08:00
</ ul >
2025-10-21 22:36:59 +08:00
</ div >
2025-12-22 19:41:47 +08:00
</ section >
2025-09-29 16:33:15 +08:00
2025-12-22 19:41:47 +08:00
{ /* Configuration 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-12-22 19:41:47 +08:00
// Self-hosted models (e.g., Ollama)
2025-09-29 16:33:15 +08:00
const pageAgent = new PageAgent({
baseURL: 'http://localhost:11434/v1',
2026-02-02 16:51:51 +08:00
apiKey: 'NA',
model: 'qwen3:14b'
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
{ /* Free Testing API Section */ }
< 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
? '以下免费测试接口仅供 PageAgent.js 和 PageAgent Extension 的技术评估使用。有速率限制,可能随时变更。请勿用于生产环境。'
: 'The following free testing endpoints are provided for technical evaluation of PageAgent.js and PageAgent Extension only. Rate-limited, subject to change. Not for production use.' }
</ p >
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
code = { `# qwen3.5-plus (default for demos) or qwen3.5-flash (lighter)
LLM_BASE_URL="https://page-ag-testing-ohftxirgbn.cn-shanghai.fcapp.run"
2026-02-24 00:59:42 +08:00
LLM_MODEL_NAME="qwen3.5-plus"
LLM_API_KEY="NA"` }
2026-02-27 17:09:54 +08:00
/>
2026-02-24 00:59:42 +08:00
</ div >
< div className = "mt-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
2026-02-27 17:09:54 +08:00
? '⚠️ 仅供技术评估和研发用途,禁止用于生产环境。数据通过中国大陆服务器处理。请勿输入任何个人身份信息或敏感数据。使用即表示您同意'
: '⚠️ 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 >
2026-02-24 00:59:42 +08:00
</ p >
</ div >
</ section >
2026-02-02 16:51:51 +08:00
{ /* Ollama Section */ }
< section className = "mb-10" >
2026-02-27 20:36:00 +08:00
< Heading id = "ollama" > Ollama </ Heading >
2026-02-02 16:51:51 +08:00
< 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).' }
</ p >
< CodeEditor
code = { `LLM_BASE_URL="http://localhost:11434/v1"
LLM_API_KEY="NA"
LLM_MODEL_NAME="qwen3:14b"` }
/>
< 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
? '确保 OLLAMA_ORIGINS 设置为 * 以避免 403 错误'
: 'Add * to OLLAMA_ORIGINS to avoid 403 errors' }
</ li >
< li >
{ isZh
? '小于 10B 参数的模型通常效果不佳'
: 'Models smaller than 10B are unlikely to be strong enough' }
</ li >
< li >{ isZh ? '需要支持 tool_call 的模型' : 'Requires tool_call capable models' }</ li >
< li >
{ isZh
? '确保上下文长度大于输入 token 数,否则 Ollama 会静默截断 prompt。普通页面约需 15k token,随步骤增加。默认 4k 上下文长度无法正常工作'
: 'Ensure context length exceeds input tokens, or Ollama will silently truncate prompts. ~15k tokens for a typical page, increases with steps. Default 4k context length will NOT work' }
< ul className = "text-sm text-gray-700 dark:text-gray-300 space-y-2 list-disc pl-5" >
< li >
< code className = "text-xs bg-gray-200 dark:bg-gray-700 px-1 rounded" >
$env :OLLAMA_CONTEXT_LENGTH = 64000 ; ollama serve
</ code >
</ li >
</ ul >
</ li >
</ ul >
</ div >
</ section >
2025-09-29 16:33:15 +08:00
</ div >
)
}