diff --git a/docs/index.html b/docs/index.html index 7d7fa6b8..756b19d8 100644 --- a/docs/index.html +++ b/docs/index.html @@ -424,7 +424,6 @@ footer{padding:12px 18px 16px;padding-bottom:max(16px,env(safe-area-inset-bottom
-
🔒 API 密钥仅本次会话有效,关闭标签页即自动清除
@@ -445,27 +444,40 @@ footer{padding:12px 18px 16px;padding-bottom:max(16px,env(safe-area-inset-bottom
-
- - - 输入你的 API 提供商端点地址(OpenAI 兼容格式) -
-
- -
- - -
- - -
-
- - - 输入端点和密钥后,点击「🔍 检测模型」自动获取可用模型列表 + +
+
🚀 一键检测可用模型
+
系统自动检测你的网络环境(国内/国际),连接服务器代理获取可用模型。无需输入 API 密钥。
+ +
- +
+ + + 点击上方「🔍 自动检测」一键获取可用模型列表 +
+ + +
+ 🔧 自定义 API(使用自己的密钥) +
+
+ + + 输入你的 API 提供商端点地址(OpenAI 兼容格式) +
+
+ + + + 🔒 密钥仅本次会话有效,关闭标签页即自动清除 +
+ +
+
+ +
@@ -490,9 +502,9 @@ footer{padding:12px 18px 16px;padding-bottom:max(16px,env(safe-area-inset-bottom @@ -616,23 +628,36 @@ footer{padding:12px 18px 16px;padding-bottom:max(16px,env(safe-area-inset-bottom
-
- - -
-
- - -
-
- -
- - + +
+
+
- 🔒 密钥仅本次会话有效,关闭标签页自动清除 + 自动检测网络环境,连接服务器代理获取可用模型(无需密钥)
+ +
+ + +
+ + +
+ 🔧 自定义 API(使用自己的密钥) +
+
+ + +
+
+ + + 🔒 密钥仅本次会话有效,关闭标签页自动清除 +
+
+
+
身份设置
@@ -665,11 +690,11 @@ footer{padding:12px 18px 16px;padding-bottom:max(16px,env(safe-area-inset-bottom
使用说明

- 1. 输入你的 API 端点和密钥,点击检测模型
- 2. 铸渊自动切换 💬 对话 / 🔨 构建 / 📋 审查 / 🧠 大脑 模式
- 3. 支持上传图片和文件(点击 📎 按钮或拖拽到对话区域)
- 4. 点 ⬇️ 下载单文件,放桌面直接打开使用
- 5. 支持任意 OpenAI 兼容接口(输入端点 + 密钥即可) + 1. 点击「🔍 一键检测可用模型」自动获取模型列表(无需密钥)
+ 2. 如服务器代理可用,选择模型后直接对话
+ 3. 也可展开「🔧 自定义 API」输入自己的端点和密钥
+ 4. 铸渊自动切换 💬 对话 / 🔨 构建 / 📋 审查 / 🧠 大脑 模式
+ 5. 支持上传图片和文件(点击 📎 按钮或拖拽到对话区域)

⚠️ 危险操作
@@ -822,6 +847,25 @@ const KNOWN_ENDPOINTS = [ {label:'智谱 AI', base:'https://open.bigmodel.cn/api/paas/v4'}, ]; +// ═══════════════════════════════════════════════════════ +// 内置模型目录(按网络区域分类) +// 一键检测时,根据用户网络环境自动推荐可用模型 +// ═══════════════════════════════════════════════════════ +const MODEL_CATALOG = { + domestic: [ + {provider:'yunwu', label:'云雾 AI(推荐·聚合)', models:['deepseek-chat','deepseek-reasoner','gpt-4o','claude-sonnet-4-20250514','gemini-2.5-pro-preview-05-06'], endpoint:'https://api.yunwu.ai/v1'}, + {provider:'deepseek', label:'DeepSeek(国内直连)', models:['deepseek-chat','deepseek-reasoner'], endpoint:'https://api.deepseek.com/v1'}, + {provider:'qwen', label:'通义千问 Qwen', models:['qwen-max','qwen-plus','qwen-turbo'], endpoint:'https://dashscope.aliyuncs.com/compatible-mode/v1'}, + {provider:'moonshot', label:'Moonshot / Kimi', models:['moonshot-v1-8k','moonshot-v1-32k','moonshot-v1-128k'], endpoint:'https://api.moonshot.cn/v1'}, + {provider:'zhipu', label:'智谱 AI', models:['glm-4','glm-4-flash'], endpoint:'https://open.bigmodel.cn/api/paas/v4'}, + ], + international: [ + {provider:'openai', label:'OpenAI', models:['gpt-4o','gpt-4-turbo','gpt-4o-mini'], endpoint:'https://api.openai.com/v1'}, + {provider:'anthropic', label:'Anthropic', models:['claude-sonnet-4-20250514','claude-3-5-sonnet-20241022'], endpoint:'https://api.anthropic.com/v1'}, + {provider:'google', label:'Google Gemini', models:['gemini-2.5-pro','gemini-2.0-flash'], endpoint:'https://generativelanguage.googleapis.com/v1beta/openai'}, + ] +}; + const MODES = { chat: {e:'💬',l:'对话模式', c:'var(--mc)'}, build: {e:'🔨',l:'构建模式', c:'var(--mb)'}, @@ -938,6 +982,29 @@ const HOLOLAKE_ENV = (function(){ })(); window.HOLOLAKE_ENV = HOLOLAKE_ENV; +// ═══════════════════════════════════════════════════════ +// 服务器代理基址(staging/local → 调用生产服务器已配置的 API 密钥) +// ═══════════════════════════════════════════════════════ +const SERVER_PROXY_BASE = HOLOLAKE_ENV === 'production' ? '' : 'https://guanghulab.com'; + +// Patch proxy URLs for staging/local: point to production server +if(HOLOLAKE_ENV !== 'production'){ + GUEST_API.proxyUrl = SERVER_PROXY_BASE + '/api/chat'; + TEAM_API.proxyUrl = SERVER_PROXY_BASE + '/api/chat'; +} + +// 检测用户网络环境(国内/国际) +function detectNetworkRegion(){ + var tz = ''; + try{ tz = Intl.DateTimeFormat().resolvedOptions().timeZone || ''; }catch(e){} + var lang = (navigator.language || navigator.userLanguage || '').toLowerCase(); + var chinaZones = ['Asia/Shanghai','Asia/Chongqing','Asia/Harbin','Asia/Urumqi','Asia/Hong_Kong','Asia/Macau','Asia/Taipei']; + if(chinaZones.some(function(z){ return tz.indexOf(z) >= 0; }) || lang.indexOf('zh') === 0){ + return 'domestic'; + } + return 'international'; +} + function ls(k){return localStorage.getItem(k)} function lss(k,v){localStorage.setItem(k,v)} @@ -1414,20 +1481,23 @@ function switchLoginMode(mode){ // ═══════════════════════════════════════════════════════ // AUTO-DETECT PROVIDER & MODELS +// v4.0: 无密钥一键检测 + 网络环境自适应 + 服务器代理 +// 有密钥 → 原逻辑探测端点;无密钥 → 服务器代理 / 内置目录 // ═══════════════════════════════════════════════════════ async function autoDetectAPI(ctx){ const keyEl = document.getElementById(ctx==='s'?'sk':'ck'); const statusEl = document.getElementById(ctx==='s'?'sdet-status':'cdet-status'); const btnEl = document.getElementById(ctx==='s'?'sdet-btn':'cdet-btn'); const mdlSel = document.getElementById(ctx==='s'?'sm':'cm'); - const key = keyEl.value.trim(); + const key = keyEl ? keyEl.value.trim() : ''; + // 无密钥 → 一键自动检测(服务器代理 / 内置目录) if(!key){ - statusEl.innerHTML='⚠️ 请先输入 API 密钥'; + await autoDetectModels(ctx); return; } - // Build list of endpoints to probe: user's endpoint first, then known fallbacks + // 有密钥 → 探测用户端点获取模型列表 const userBase = (document.getElementById(ctx==='s'?'sep':'cep')?.value||'').trim(); const candidates = []; if(userBase) candidates.push({label:userBase, base:userBase}); @@ -1455,11 +1525,9 @@ async function autoDetectAPI(ctx){ const mdls = (data.data||[]).map(function(m){return m.id;}).filter(Boolean).sort(); if(!mdls.length) continue; - // Fill endpoint field with matched endpoint const epEl = document.getElementById(ctx==='s'?'sep':'cep'); if(epEl) epEl.value = base; - // Fill model dropdown with detected models if(mdlSel){ mdlSel.innerHTML = mdls.map(function(m){return '';}).join(''); mdlSel.disabled = false; @@ -1477,12 +1545,106 @@ async function autoDetectAPI(ctx){ } btnEl.disabled = false; - btnEl.textContent = '🔍 检测模型'; + btnEl.textContent = '🔍 自动检测'; +} + +// ═══════════════════════════════════════════════════════ +// 一键检测可用模型(无需 API 密钥) +// 1. 检测网络环境(国内/国际) +// 2. 尝试连接服务器代理获取实时模型列表 +// 3. 降级使用内置模型目录 +// ═══════════════════════════════════════════════════════ +async function autoDetectModels(ctx){ + const statusEl = document.getElementById(ctx==='s'?'sdet-status':'cdet-status'); + const btnEl = document.getElementById(ctx==='s'?'sdet-btn':'cdet-btn'); + const mdlSel = document.getElementById(ctx==='s'?'sm':'cm'); + const epEl = document.getElementById(ctx==='s'?'sep':'cep'); + const hintEl = document.getElementById(ctx==='s'?'sm-hint':''); + + btnEl.disabled = true; + btnEl.textContent = '⏳ 检测中…'; + statusEl.innerHTML = '🌐 正在检测网络环境…'; + + const region = detectNetworkRegion(); + const regionLabel = region === 'domestic' ? '🇨🇳 国内网络' : '🌍 国际网络'; + statusEl.innerHTML = ''+regionLabel+' · 正在获取可用模型…'; + + // 尝试连接服务器代理获取实时模型列表 + let serverModels = null; + const proxyBase = SERVER_PROXY_BASE || location.origin; + try{ + const ctrl = new AbortController(); + const tid = setTimeout(function(){ctrl.abort();}, 5000); + const res = await fetch(proxyBase+'/api/models',{signal:ctrl.signal}); + clearTimeout(tid); + if(res.ok){ + const data = await res.json(); + const list = data.data || data.models || []; + serverModels = list.map(function(m){return typeof m==='string'?m:m.id;}).filter(Boolean).sort(); + } + }catch(e){console.debug('[AutoDetect] Server probe failed:',e.message);} + + let source, totalCount = 0; + + if(serverModels && serverModels.length > 0){ + // 服务器代理可用 → 使用服务器模型列表 + source = '服务器代理'; + totalCount = serverModels.length; + if(mdlSel){ + mdlSel.innerHTML = serverModels.map(function(m){return '';}).join(''); + mdlSel.disabled = false; + } + if(epEl) epEl.value = proxyBase + '/api/chat'; + window._autoDetectServerProxy = true; + } else { + // 降级使用内置模型目录(按网络区域排序) + source = '内置模型库'; + const primary = region === 'domestic' ? MODEL_CATALOG.domestic : MODEL_CATALOG.international; + const secondary = region === 'domestic' ? MODEL_CATALOG.international : MODEL_CATALOG.domestic; + const catalog = primary.concat(secondary); + + if(mdlSel){ + let html = ''; + for(let i=0;i'; + for(let j=0;j'+p.models[j]+''; + totalCount++; + } + html += ''; + } + mdlSel.innerHTML = html; + mdlSel.disabled = false; + // 自动选择第一个模型并设置对应端点 + onModelSelectChange(ctx); + } + window._autoDetectServerProxy = false; + } + + statusEl.innerHTML = '✅ 检测成功('+source+' · '+regionLabel+')· 发现 '+totalCount+' 个可用模型' + + (serverModels ? '
🔑 已连接服务器代理,无需输入 API 密钥' : '
⚠️ 服务器暂不可达,请输入对应提供商的 API 密钥后使用'); + + if(hintEl) hintEl.textContent = serverModels ? '已连接服务器代理,选择模型后可直接开始对话' : '选择模型后,请输入对应提供商的 API 密钥'; + + btnEl.disabled = false; + btnEl.textContent = '🔍 自动检测'; +} + +// 模型选择变更 → 自动切换端点 +function onModelSelectChange(ctx){ + const mdlSel = document.getElementById(ctx==='s'?'sm':'cm'); + const epEl = document.getElementById(ctx==='s'?'sep':'cep'); + if(!mdlSel || !epEl) return; + const opt = mdlSel.options[mdlSel.selectedIndex]; + if(!opt) return; + const ep = opt.getAttribute('data-endpoint'); + if(ep) epEl.value = ep; } function doSetup(){ const un = document.getElementById('suid').value; - const k = document.getElementById('sk').value.trim(); + const k = document.getElementById('sk')?.value?.trim() || ''; const base = (document.getElementById('sep')?.value||'').trim(); const md = document.getElementById('sm').value; const errEl = document.getElementById('sk-err'); @@ -1491,14 +1653,35 @@ function doSetup(){ alert('请选择你的开发者身份'); return; } - if(!k){errEl.style.display='block';return;} - errEl.style.display='none'; - if(!base){ - alert('请输入 API 端点 URL'); + if(!md){ + alert('请先点击「🔍 自动检测」检测可用模型,然后选择一个模型'); return; } - if(!md){ - alert('请先点击「🔍 检测模型」检测可用模型,然后选择一个模型'); + + // 服务器代理模式(一键检测,无需密钥) + if(window._autoDetectServerProxy && !k){ + A.demo = true; + A.teamMode = true; + A.key = ''; + A.prov = 'custom'; + A.mdl = md; + A.base = base; + lss('zy_teammode','1'); + lss('zy_base',base); lss('zy_mdl',md); lss('zy_prov','proxy'); + setIdentity(un, ''); + showApp(); + return; + } + + // 自定义 API 模式(需要密钥) + if(!k){ + if(errEl) errEl.style.display='block'; + alert('请输入 API 密钥,或点击「🔍 自动检测」使用服务器代理'); + return; + } + if(errEl) errEl.style.display='none'; + if(!base){ + alert('请输入 API 端点 URL'); return; }