Files
projectAIpopular/gateway/static/assets/SettingsView-CSj0jpFo.js
tzt 747d85c3ba feat(v3): T29 收尾 + T31 dsh 功能对齐
T29 流式收尾:
- _stream_partial 每次流式调用前复位(防上次成功置位导致本次失败误抛不回退)

T31 dsh(deepseek-harness)功能对齐:
- OpenAICompatChat 重试退避:传输错误/408/429/5xx 指数退避(max_retries=2),4xx 不重试
- web_fetch 工具:公网 http/https 抓取,SSRF 防护(DNS 后拒绝私网/环回/链路本地/NAT64,
  512KB/15s/12k 上限,二进制嗅探拒绝),agent.allow_net 开关(默认开)
- 原子写入:write_file/edit_file 临时文件 + os.replace(Windows EPERM 退避)
- 慢工具线程卸载:run_command/web_fetch/search_files 独立线程 + asyncio.sleep 轮询
- search_files:os.walk 修剪依赖目录(替代 rglob 全量物化),不跟随符号链接
- run_command:危险命令黑名单独立拦截 + 显式 COMSPEC/sh 解释器执行
- 重复调用提醒:同工具同参数第 3 次起回喂系统提示 + repeat_warning 事件
- 会话重命名:PATCH /agent/sessions/{sid} + 前端 ✎
- 前端:SettingsView 适配密钥打码(留空保留),SPA 重新构建
- 新增 tests/test_agent_features.py(9 项);全量 318 测试通过
2026-09-02 00:02:24 +08:00

1 line
24 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import{A as e,B as t,D as n,E as r,F as i,G as a,I as o,L as s,M as c,N as ee,O as l,P as te,S as ne,T as u,U as d,V as f,W as re,_ as ie,a as ae,b as oe,c as se,d as ce,g as le,h as ue,i as de,j as p,k as m,l as fe,m as pe,o as me,p as he,r as ge,t as h,u as _e,w as ve,x as ye,y as be,z as g}from"./index-kbuKhaUa.js";var xe={class:`settings-view`},Se={class:`settings-section`},Ce={class:`section-body`},we={class:`role-bar`},Te=[`onUpdate:modelValue`],Ee=[`value`],De={key:0,class:`pool-table`},Oe={class:`mono`},ke=[`onUpdate:modelValue`,`onChange`],Ae={class:`row-actions`},je=[`disabled`,`onClick`],Me=[`onClick`],Ne=[`onClick`],Pe={key:1,class:`no-models`},Fe={key:3,class:`pool-edit`},Ie={class:`pool-edit-title`},Le={class:`form-grid`},Re={class:`field`},ze={class:`field`},Be={class:`field`},Ve={class:`field`},He=[`disabled`],Ue={id:`pool-model-options`},We=[`value`],Ge={key:0,class:`field full-width`},Ke={key:1,class:`field`},qe={class:`field`},Je={class:`field`},Ye={class:`field`},Xe={class:`field`},Ze={class:`pool-edit-actions`},Qe={class:`checkbox-label`},$e=[`disabled`],et={class:`settings-section`},tt={class:`section-title`},nt={class:`title-right`},rt={class:`section-body`},it={class:`server-control-bar`},at={class:`server-info`},ot={class:`model-chip`},st={class:`model-chip port-chip`},ct={class:`model-chip pid-chip`},lt={key:1,class:`server-idle`},ut={class:`server-actions`},dt=[`disabled`],ft=[`disabled`],pt={key:0,class:`error-msg llama-error`},mt={class:`local-models`},ht={class:`local-models-header`},gt=[`disabled`],_t={key:0,class:`no-models`},vt={key:1,class:`model-list`},yt=[`onClick`],bt={class:`model-item-name`},xt={class:`model-item-size`},St={class:`download-section`},Ct={class:`download-form`},wt=[`disabled`],Tt=[`disabled`],Et={key:0,class:`progress-wrap`},Dt={class:`progress-bar`},Ot={class:`progress-info`},kt={key:1,class:`error-msg download-error`},At={key:2,class:`done-msg`},jt={class:`form-grid`,style:{"margin-top":`16px`}},Mt={class:`field full-width`},Nt={key:0,class:`field full-width`},Pt=[`disabled`],Ft={class:`input-row`},It=[`placeholder`],Lt={key:1,class:`field full-width`},Rt=[`disabled`],zt=[`value`],Bt=[`value`],Vt={key:3,class:`field-hint warn`},Ht={key:2,class:`field`},Ut={class:`field`},Wt={class:`field`},Gt={class:`field`},Kt={class:`settings-section`},qt={class:`section-title`},Jt={class:`title-right`},Yt={class:`section-body`},Xt={class:`form-grid`},Zt={class:`field full-width`},Qt=[`disabled`],$t={class:`field full-width`},en=[`placeholder`],tn={class:`field full-width`},nn=[`disabled`],rn=[`value`],an={key:0,class:`field-hint warn`},on={class:`settings-section`},sn={class:`section-body`},cn={class:`form-grid`},ln={class:`field full-width`},un={class:`checkbox-label`},dn={class:`field`},fn={class:`field`},pn={class:`field full-width`},mn={class:`actions`},hn=[`disabled`],gn=[`disabled`],_=h(ee({__name:`SettingsView`,setup(ee){let h=f(!1),_=f(``),v=f(null),_n=[{key:`architect`,label:`🧠 Architect(决策/终审)`},{key:`worker`,label:`🔧 Worker(实现/自验证)`},{key:`agent`,label:`🤖 Agent(智能体工具)`}],vn={local:`本地/零成本`,budget:`低价 API`,premium:`旗舰 API`},y=f({roles:{architect:``,worker:``,agent:``},entries:[]}),b=t({architect:``,worker:``,agent:``}),x=f(null),S=f(!1),C=f(``),w=f(``),T=f(!1),E=f(!1),D=f(!1),yn=f([]),bn=l(()=>y.value.entries.filter(e=>e.enabled));function xn(e){return vn[e]??e}async function Sn(){try{y.value=await se(),b.architect=y.value.roles.architect||``,b.worker=y.value.roles.worker||``,b.agent=y.value.roles.agent||``}catch{}}async function Cn(){try{y.value=await ue({...b}),v.value={ok:!0,msg:`✅ 角色已更新,管线已重建`}}catch(e){v.value={ok:!1,msg:`❌ 角色指派失败:`+(e?.response?.data?.detail||e?.message||e)},await Sn()}setTimeout(()=>v.value=null,3e3)}function wn(){S.value=!0,x.value={name:``,tier:`budget`,backend:`openai`,base_url:``,model:``,api_key:``,price_in:.1,price_out:.1,temperature:.3,max_tokens:4096,enabled:!0}}function Tn(e){S.value=!1,x.value={...e,api_key:``}}async function En(){if(x.value){E.value=!0;try{y.value=await ye(x.value),x.value=null,v.value={ok:!0,msg:`✅ 模型条目已保存`}}catch(e){v.value={ok:!1,msg:`❌ 保存失败:`+(e?.response?.data?.detail||e?.message||e)}}finally{E.value=!1,setTimeout(()=>v.value=null,3e3)}}}async function Dn(e){confirm(`删除模型条目「${e.name}」?相关角色指派会一并清空。`)&&(y.value=await ge(e.id))}async function On(e){y.value=await ye({...e,api_key:``})}async function kn(e){C.value=e.id,w.value=``;try{let t=await be(e.id);T.value=t.ok,w.value=t.ok?`✅ ${e.name} 连接正常`:`❌ ${e.name}: ${t.detail||`连接失败`}`}catch(e){T.value=!1,w.value=`❌ `+(e?.message||String(e))}finally{C.value=``}}async function An(){if(x.value){D.value=!0;try{let e;e=!S.value&&x.value.id?await ce(x.value.id):x.value.backend===`mock`?{models:[{id:`mock`,name:`mock(内置模拟)`}]}:await _e(x.value.backend||`openai`,x.value.base_url||``,x.value.api_key||``),e.error?(T.value=!1,w.value=e.error,yn.value=[]):yn.value=e.models||[]}finally{D.value=!1}}}let O=f({running:!1}),k=f(!1),A=f(!1),j=f([]),M=f(!1),N=f(``),P=f(!1),F=f(``),I=f(0),L=f(``),jn=f(``),Mn=f(``),R=f(null),Nn=f(``),z=f(``),Pn=f(!1),B=null,V=f([]),H=f([]),U=t({worker:!1,architect:!1}),W=t({worker:``,architect:``}),G=t({worker:!1,architect:!1}),K=t({worker:null,architect:null}),q=t({worker:``,architect:``}),J=[{id:`deepseek-v4-flash`,name:`deepseek-v4-flash(推荐)`},{id:`deepseek-v4-pro`,name:`deepseek-v4-pro(推理模式)`},{id:`deepseek-coder`,name:`deepseek-coder`},{id:`gpt-4o-mini`,name:`gpt-4o-mini`},{id:`gpt-4o`,name:`gpt-4o`},{id:`claude-3-5-sonnet-20241022`,name:`claude-3.5-sonnet`},{id:`qwen-plus`,name:`qwen-plus`},{id:`qwen-max`,name:`qwen-max`}],Y=f(null),X=t({worker:{backend:`mock`,model:``,base_url:``,port:8901,temperature:.3,max_fix_attempts:2,code_timeout_s:10},architect:{model:`deepseek-v4-flash`,base_url:`https://api.deepseek.com`,api_key:``},pipeline:{fast_path:!0,rounds_cap:6,api_token_cap:8e3,breach_policy:`architect_do`}}),Fn=l(()=>X.worker.backend===`llama_server`?`http://127.0.0.1:${X.worker.port}/v1`:`http://127.0.0.1:11434/v1`),In=l(()=>Y.value?JSON.stringify(X)!==JSON.stringify(Y.value):!1);function Ln(e){return e>=1e9?(e/1e9).toFixed(1)+` GB`:e>=1e6?(e/1e6).toFixed(1)+` MB`:e>=1e3?(e/1e3).toFixed(0)+` KB`:e+` B`}async function Rn(){try{let e=await me();return O.value=e,e}catch{return O.value={running:!1},{running:!1}}}async function Z(){M.value=!0;try{j.value=await fe()}catch{j.value=[]}finally{M.value=!1}}async function zn(){if(!X.worker.model){alert(`请先从上方「本地模型」列表中选择一个模型,再启动服务`);return}k.value=!0;try{let e=await le(X.worker.model,X.worker.port,99,4096);O.value=e,e.running&&(X.worker.backend=`llama_server`,X.worker.base_url=`http://127.0.0.1:${e.port}/v1`,await Z(),await Q())}catch(e){O.value={running:!1,error:String(e?.message||e)}}finally{k.value=!1}}async function Bn(){A.value=!0;try{await ie(),O.value={running:!1}}catch{O.value={running:!1}}finally{A.value=!1}}function Vn(e){N.value=e.id,X.worker.model=e.id}function Hn(){F.value.trim()&&(z.value=``,Pn.value=!1,I.value=0,L.value=``,jn.value=``,Mn.value=`0 B`,R.value=null,P.value=!0,de(F.value.trim()).then(e=>{if(e.error){z.value=e.error,P.value=!1;return}if(e.done){Un(e);return}B=ne(F.value.trim()),B.subscribe({onProgress:Un,onDone:e=>{Un(e),B?.close(),B=null}})}))}function Un(e){I.value=Math.round(e.progress_pct),L.value=e.speed,jn.value=e.eta,Mn.value=Ln(e.downloaded_bytes),R.value=e.total_bytes?Ln(e.total_bytes):null,Nn.value=e.dest,e.done&&(P.value=!1,e.error?z.value=e.error:(Pn.value=!0,Z()))}async function Wn(){W.worker=``,V.value=[],X.worker.backend!==`mock`&&await Q()}async function Gn(){W.architect=``,H.value=[],await Kn()}async function Q(){if(X.worker.backend!==`mock`){U.worker=!0,W.worker=``;try{let e=await _e(X.worker.backend,X.worker.base_url,``);e.error?X.worker.backend===`llama_server`?W.worker=`💡 llama-server 未启动,请先点击上方「启动服务」`:W.worker=e.error||`连接失败`:V.value=e.models||[]}catch{W.worker=`获取模型列表失败`}finally{U.worker=!1}}}async function Kn(){if(U.architect=!0,W.architect=``,!X.architect.base_url){H.value=[...J],U.architect=!1;return}try{let e=await _e(`openai`,X.architect.base_url,X.architect.api_key||``);e.error?(W.architect=e.error,H.value=[...J]):(e.models||[]).length>0?H.value=e.models||[]:H.value=[...J]}catch{W.architect=`获取模型列表失败`,H.value=[...J]}finally{U.architect=!1}}async function qn(){if(X.worker.backend!==`mock`){G.worker=!0,K.worker=null,q.worker=``;try{let e=await he(X.worker.backend,X.worker.base_url,``);K.worker=e.ok,e.ok||(q.worker=e.detail||`连接失败`)}catch(e){K.worker=!1,q.worker=e?.message||String(e)}finally{G.worker=!1}}}async function Jn(){if(!X.architect.base_url){K.architect=!1,q.architect=`请先填写 API 端点`;return}G.architect=!0,K.architect=null,q.architect=``;try{let e=await he(`openai`,X.architect.base_url,X.architect.api_key||``);K.architect=e.ok,e.ok||(q.architect=e.detail||`连接失败`)}catch(e){K.architect=!1,q.architect=e?.message||String(e)}finally{G.architect=!1}}function $(e,t){if(t)for(let n of Object.keys(e))t[n]!==void 0&&(typeof e[n]==`object`&&typeof t[n]==`object`&&e[n]!==null?$(e[n],t[n]):e[n]=t[n])}function Yn(e){if(!e)return;let{api_key:t,api_key_set:n,...r}=e;$(X.architect,r),X.architect.api_key_set=!!n,X.architect.api_key=``}async function Xn(){_.value=``;try{let[e,t]=await Promise.all([ae(),Rn()]);O.value=t,Sn(),e.worker&&$(X.worker,e.worker),e.architect&&Yn(e.architect),e.pipeline&&$(X.pipeline,e.pipeline),Y.value=JSON.parse(JSON.stringify(X)),await Z(),H.value=[...J],X.worker.backend!==`mock`&&Q(),X.worker.model&&(N.value=X.worker.model)}catch(e){_.value=`加载配置失败:`+(e?.message||String(e))}}async function Zn(){h.value=!0,v.value=null;try{let e=JSON.parse(JSON.stringify(X));e.architect.api_key||delete e.architect.api_key;let t=await oe(e);$(X.worker,t.worker||{}),Yn(t.architect||{}),$(X.pipeline,t.pipeline||{}),Y.value=JSON.parse(JSON.stringify(X)),v.value={ok:!0,msg:`✅ 设置已保存,管线已重建`},setTimeout(()=>v.value=null,3e3)}catch(e){v.value={ok:!1,msg:`❌ 保存失败:`+(e?.message||String(e))}}finally{h.value=!1}}async function Qn(){if(confirm(`确定恢复默认设置?`)){h.value=!0,v.value=null;try{let e=await pe();$(X.worker,e.worker||{}),Yn(e.architect||{}),$(X.pipeline,e.pipeline||{}),Y.value=JSON.parse(JSON.stringify(X)),V.value=[],H.value=[...J],K.worker=null,K.architect=null,v.value={ok:!0,msg:`✅ 已恢复默认设置`},setTimeout(()=>v.value=null,3e3)}catch(e){v.value={ok:!1,msg:`❌ 重置失败:`+(e?.message||String(e))}}finally{h.value=!1}}}return te(Xn),i(()=>{B?.close()}),(t,i)=>(o(),p(`div`,xe,[i[67]||=m(`header`,{class:`settings-header`},[m(`h2`,null,`⚙️ 模型设置`),m(`p`,{class:`subtitle`},`配置小模型(Worker)与大模型(Architect)参数,保存后实时生效`)],-1),m(`section`,Se,[m(`div`,{class:`section-title`},[i[29]||=m(`span`,null,`🗄️ 模型池(多价位异构模型)`,-1),m(`div`,{class:`title-right`},[m(`button`,{class:`btn-refresh`,onClick:wn},` 添加模型`)])]),m(`div`,Ce,[m(`div`,we,[(o(),p(n,null,s(_n,e=>m(`div`,{class:`role-item`,key:e.key},[m(`label`,null,a(e.label),1),g(m(`select`,{"onUpdate:modelValue":t=>b[e.key]=t,onChange:Cn},[i[30]||=m(`option`,{value:``},`经典设置(下方表单)`,-1),(o(!0),p(n,null,s(bn.value,e=>(o(),p(`option`,{key:e.id,value:e.id},a(e.name),9,Ee))),128))],40,Te),[[u,b[e.key]]])])),64))]),i[44]||=m(`p`,{class:`role-hint`},` 按价位组织模型:本地 llama.cpp 为零成本档,低价 API 干批量活,旗舰 API 做决策—— 随时切换,保存后管线自动重建。 `,-1),y.value.entries.length?(o(),p(`table`,De,[i[31]||=m(`thead`,null,[m(`tr`,null,[m(`th`,null,`名称`),m(`th`,null,`档位`),m(`th`,null,`后端`),m(`th`,null,`模型`),m(`th`,null,`单价 $/1M(入/出)`),m(`th`,null,`启用`),m(`th`,null,`操作`)])],-1),m(`tbody`,null,[(o(!0),p(n,null,s(y.value.entries,e=>(o(),p(`tr`,{key:e.id},[m(`td`,null,a(e.name),1),m(`td`,null,[m(`span`,{class:d([`tier-badge`,`tier-`+e.tier])},a(xn(e.tier)),3)]),m(`td`,null,a(e.backend),1),m(`td`,Oe,a(e.model||`—`),1),m(`td`,null,a(e.price_in)+` / `+a(e.price_out),1),m(`td`,null,[g(m(`input`,{type:`checkbox`,"onUpdate:modelValue":t=>e.enabled=t,onChange:t=>On(e)},null,40,ke),[[ve,e.enabled]])]),m(`td`,Ae,[m(`button`,{class:`btn-refresh`,disabled:C.value===e.id,onClick:t=>kn(e)},a(C.value===e.id?`…`:`测试`),9,je),m(`button`,{class:`btn-refresh`,onClick:t=>Tn(e)},`编辑`,8,Me),m(`button`,{class:`btn-refresh danger`,onClick:t=>Dn(e)},`删除`,8,Ne)])]))),128))])])):(o(),p(`div`,Pe,` 池为空——可添加本地 llama.cpp 模型(零成本档)、低价 API、旗舰 API 等,随时指派给 Architect / Worker / Agent。 `)),w.value?(o(),p(`div`,{key:2,class:d([`field-hint`,T.value?`ok`:`warn`])},a(w.value),3)):e(``,!0),x.value?(o(),p(`div`,Fe,[m(`div`,Ie,a(S.value?`添加模型`:`编辑模型`),1),m(`div`,Le,[m(`div`,Re,[i[32]||=m(`label`,null,`名称`,-1),g(m(`input`,{"onUpdate:modelValue":i[0]||=e=>x.value.name=e,placeholder:`如:DeepSeek 旗舰 / 本地 Qwen4B`},null,512),[[r,x.value.name]])]),m(`div`,ze,[i[34]||=m(`label`,null,`档位`,-1),g(m(`select`,{"onUpdate:modelValue":i[1]||=e=>x.value.tier=e},[...i[33]||=[m(`option`,{value:`local`},`local(本地 / 零边际成本)`,-1),m(`option`,{value:`budget`},`budget(低价 API`,-1),m(`option`,{value:`premium`},`premium(旗舰 API`,-1)]],512),[[u,x.value.tier]])]),m(`div`,Be,[i[36]||=m(`label`,null,`后端`,-1),g(m(`select`,{"onUpdate:modelValue":i[2]||=e=>x.value.backend=e},[...i[35]||=[m(`option`,{value:`openai`},`openai / 兼容 API`,-1),m(`option`,{value:`llama_server`},`llama_server(内置本地)`,-1),m(`option`,{value:`mock`},`mock(模拟)`,-1)]],512),[[u,x.value.backend]])]),m(`div`,Ve,[m(`label`,null,[i[37]||=c(` 模型 `,-1),m(`button`,{class:`btn-refresh btn-inline`,disabled:D.value,onClick:An},a(D.value?`…`:`🔄 刷新列表`),9,He)]),g(m(`input`,{"onUpdate:modelValue":i[3]||=e=>x.value.model=e,list:`pool-model-options`,placeholder:`模型名`},null,512),[[r,x.value.model]]),m(`datalist`,Ue,[(o(!0),p(n,null,s(yn.value,e=>(o(),p(`option`,{key:e.id,value:e.id},null,8,We))),128))])]),x.value.backend===`mock`?e(``,!0):(o(),p(`div`,Ge,[i[38]||=m(`label`,null,`API 地址`,-1),g(m(`input`,{"onUpdate:modelValue":i[4]||=e=>x.value.base_url=e,placeholder:`https://api.deepseek.com 或 http://127.0.0.1:8901/v1`},null,512),[[r,x.value.base_url]])])),x.value.backend===`openai`?(o(),p(`div`,Ke,[m(`label`,null,`API Key`+a(x.value.api_key_set?`(已设置,留空保留)`:``),1),g(m(`input`,{"onUpdate:modelValue":i[5]||=e=>x.value.api_key=e,type:`password`,autocomplete:`off`,placeholder:`sk-…`},null,512),[[r,x.value.api_key]])])):e(``,!0),m(`div`,qe,[i[39]||=m(`label`,null,`输入单价 $/1M`,-1),g(m(`input`,{"onUpdate:modelValue":i[6]||=e=>x.value.price_in=e,type:`number`,min:`0`,step:`0.01`},null,512),[[r,x.value.price_in,void 0,{number:!0}]])]),m(`div`,Je,[i[40]||=m(`label`,null,`输出单价 $/1M`,-1),g(m(`input`,{"onUpdate:modelValue":i[7]||=e=>x.value.price_out=e,type:`number`,min:`0`,step:`0.01`},null,512),[[r,x.value.price_out,void 0,{number:!0}]])]),m(`div`,Ye,[i[41]||=m(`label`,null,`Temperature`,-1),g(m(`input`,{"onUpdate:modelValue":i[8]||=e=>x.value.temperature=e,type:`number`,min:`0`,max:`2`,step:`0.05`},null,512),[[r,x.value.temperature,void 0,{number:!0}]])]),m(`div`,Xe,[i[42]||=m(`label`,null,`Max Tokens`,-1),g(m(`input`,{"onUpdate:modelValue":i[9]||=e=>x.value.max_tokens=e,type:`number`,min:`256`,max:`32768`},null,512),[[r,x.value.max_tokens,void 0,{number:!0}]])])]),m(`div`,Ze,[m(`label`,Qe,[g(m(`input`,{type:`checkbox`,"onUpdate:modelValue":i[10]||=e=>x.value.enabled=e},null,512),[[ve,x.value.enabled]]),i[43]||=c(` 启用 `,-1)]),m(`button`,{class:`btn-primary`,disabled:E.value,onClick:En},a(E.value?`保存中…`:`保存条目`),9,$e),m(`button`,{class:`btn-secondary`,onClick:i[11]||=e=>x.value=null},`取消`)])])):e(``,!0)])]),m(`section`,et,[m(`div`,tt,[i[45]||=m(`span`,null,`🔧 小模型 (Worker · 本地 llama.cpp)`,-1),m(`div`,nt,[m(`span`,{class:d([`server-badge`,O.value.running?`badge-running`:`badge-stopped`])},a(O.value.running?`● 运行中`:`○ 已停止`),3)])]),m(`div`,rt,[m(`div`,it,[m(`div`,at,[O.value.running?(o(),p(n,{key:0},[m(`span`,ot,a(O.value.model?.split(/[/\\]/).pop()),1),m(`span`,st,`:`+a(O.value.port),1),m(`span`,ct,`PID `+a(O.value.pid),1)],64)):(o(),p(`span`,lt,`llama-server 未运行 — 点击「启动服务」即可启用`))]),m(`div`,ut,[O.value.running?(o(),p(`button`,{key:1,class:`btn-stop`,disabled:k.value||A.value,onClick:Bn},a(A.value?`停止中…`:`■ 停止服务`),9,ft)):(o(),p(`button`,{key:0,class:`btn-start`,disabled:k.value||A.value,onClick:zn},a(k.value?`启动中…`:`▶ 启动服务`),9,dt))])]),O.value.error?(o(),p(`div`,pt,a(O.value.error),1)):e(``,!0),m(`div`,mt,[m(`div`,ht,[m(`span`,null,`📁 本地模型 (`+a(j.value.length)+`)`,1),m(`button`,{class:`btn-refresh`,disabled:M.value,onClick:Z},a(M.value?`…`:`🔄`),9,gt)]),j.value.length===0&&!M.value?(o(),p(`div`,_t,` 暂无本地模型,请下载或放置 .gguf 文件到 models/ 目录 `)):(o(),p(`div`,vt,[(o(!0),p(n,null,s(j.value,e=>(o(),p(`div`,{key:e.id,class:d([`model-item`,N.value===e.id?`selected`:``]),onClick:t=>Vn(e)},[m(`span`,bt,a(e.name),1),m(`span`,xt,a(e.size_mb)+` MB`,1)],10,yt))),128))]))]),m(`div`,St,[i[46]||=m(`div`,{class:`download-header`},`⬇️ 下载模型`,-1),m(`div`,Ct,[g(m(`input`,{"onUpdate:modelValue":i[12]||=e=>F.value=e,class:`download-input`,placeholder:`HuggingFace 路径或直链,如:Qwen/Qwen3-4B-GGUF/Qwen3-4B-Q4_K_M.gguf`,disabled:P.value},null,8,wt),[[r,F.value]]),m(`button`,{class:`btn-download`,disabled:P.value||!F.value.trim(),onClick:Hn},a(P.value?I.value+`%`:`下载`),9,Tt)]),P.value?(o(),p(`div`,Et,[m(`div`,Dt,[m(`div`,{class:`progress-fill`,style:re({width:I.value+`%`})},null,4)]),m(`div`,Ot,[m(`span`,null,a(L.value),1),m(`span`,null,a(jn.value),1),m(`span`,null,a(Mn.value)+`/`+a(R.value||`?`),1)])])):e(``,!0),z.value?(o(),p(`div`,kt,a(z.value),1)):e(``,!0),Pn.value&&!z.value?(o(),p(`div`,At,` ✅ 下载完成:`+a(Nn.value),1)):e(``,!0)]),m(`div`,jt,[m(`div`,Mt,[i[48]||=m(`label`,null,`后端类型`,-1),g(m(`select`,{"onUpdate:modelValue":i[13]||=e=>X.worker.backend=e,onChange:Wn},[...i[47]||=[m(`option`,{value:`mock`},`mock(模拟,不真实调用)`,-1),m(`option`,{value:`llama_server`},`llama_server(内置本地服务,推荐)`,-1),m(`option`,{value:`openai`},`openai / 兼容 APIOllama 等)`,-1)]],544),[[u,X.worker.backend]])]),X.worker.backend===`mock`?e(``,!0):(o(),p(`div`,Nt,[m(`label`,null,[i[49]||=c(` API 地址 `,-1),m(`button`,{class:`btn-refresh btn-inline`,disabled:G.worker,onClick:qn},a(G.worker?`…`:`🔗 连接测试`),9,Pt)]),m(`div`,Ft,[g(m(`input`,{"onUpdate:modelValue":i[14]||=e=>X.worker.base_url=e,placeholder:Fn.value,onBlur:Wn},null,40,It),[[r,X.worker.base_url]]),K.worker===null?e(``,!0):(o(),p(`span`,{key:0,class:d([`ping-badge`,K.worker?`ping-ok`:`ping-err`])},a(K.worker?`✅ 正常`:`❌ `+q.worker),3))])])),X.worker.backend===`mock`?e(``,!0):(o(),p(`div`,Lt,[m(`label`,null,[i[50]||=c(` 模型 `,-1),m(`button`,{class:`btn-refresh btn-inline`,disabled:U.worker,onClick:Q},a(U.worker?`…`:`🔄 刷新列表`),9,Rt)]),X.worker.backend===`llama_server`?g((o(),p(`select`,{key:0,"onUpdate:modelValue":i[15]||=e=>X.worker.model=e},[i[51]||=m(`option`,{value:``,disabled:``},`—— 从本地模型中选择 ——`,-1),(o(!0),p(n,null,s(j.value,e=>(o(),p(`option`,{key:e.id,value:e.id},a(e.name),9,zt))),128))],512)),[[u,X.worker.model]]):X.worker.backend===`openai`?g((o(),p(`select`,{key:1,"onUpdate:modelValue":i[16]||=e=>X.worker.model=e},[i[52]||=m(`option`,{value:``,disabled:``},`—— 从 API 获取 ——`,-1),(o(!0),p(n,null,s(V.value,e=>(o(),p(`option`,{key:e.id,value:e.id},a(e.name||e.id),9,Bt))),128))],512)),[[u,X.worker.model]]):g((o(),p(`input`,{key:2,"onUpdate:modelValue":i[17]||=e=>X.worker.model=e,placeholder:`输入模型名称…`},null,512)),[[r,X.worker.model]]),W.worker?(o(),p(`span`,Vt,a(W.worker),1)):e(``,!0)])),X.worker.backend===`llama_server`?(o(),p(`div`,Ht,[i[53]||=m(`label`,null,`端口`,-1),g(m(`input`,{"onUpdate:modelValue":i[18]||=e=>X.worker.port=e,type:`number`,min:`1`,max:`65535`},null,512),[[r,X.worker.port,void 0,{number:!0}]])])):e(``,!0),m(`div`,Ut,[i[54]||=m(`label`,null,`Temperature`,-1),g(m(`input`,{"onUpdate:modelValue":i[19]||=e=>X.worker.temperature=e,type:`number`,min:`0`,max:`2`,step:`0.05`},null,512),[[r,X.worker.temperature,void 0,{number:!0}]])]),m(`div`,Wt,[i[55]||=m(`label`,null,`最大修复轮次`,-1),g(m(`input`,{"onUpdate:modelValue":i[20]||=e=>X.worker.max_fix_attempts=e,type:`number`,min:`0`,max:`10`},null,512),[[r,X.worker.max_fix_attempts,void 0,{number:!0}]])]),m(`div`,Gt,[i[56]||=m(`label`,null,`代码执行超时 (秒)`,-1),g(m(`input`,{"onUpdate:modelValue":i[21]||=e=>X.worker.code_timeout_s=e,type:`number`,min:`1`,max:`300`},null,512),[[r,X.worker.code_timeout_s,void 0,{number:!0}]])])])])]),m(`section`,Kt,[m(`div`,qt,[i[57]||=m(`span`,null,`🧠 大模型 (Architect · API 云端)`,-1),m(`div`,Jt,[K.architect===null?e(``,!0):(o(),p(`span`,{key:0,class:d([`ping-badge`,K.architect?`ping-ok`:`ping-err`])},a(K.architect?`✅ 已连接`:`❌ `+q.architect),3))])]),m(`div`,Yt,[m(`div`,Xt,[m(`div`,Zt,[m(`label`,null,[i[58]||=c(` API 端点 `,-1),m(`button`,{class:`btn-refresh btn-inline`,disabled:G.architect,onClick:Jn},a(G.architect?`…`:`🔗 连接测试`),9,Qt)]),g(m(`input`,{"onUpdate:modelValue":i[22]||=e=>X.architect.base_url=e,placeholder:`https://api.deepseek.com`,onBlur:Gn},null,544),[[r,X.architect.base_url]])]),m(`div`,$t,[m(`label`,null,`API Key`+a(X.architect.api_key_set?`(已设置,留空保留)`:`(保存在本地 config/settings.json,不入代码库;优先级高于 .env)`),1),g(m(`input`,{"onUpdate:modelValue":i[23]||=e=>X.architect.api_key=e,type:`password`,placeholder:X.architect.api_key_set?`已设置,留空保留原值`:`sk-xxxxxxxxxxxxxxxx`,autocomplete:`off`},null,8,en),[[r,X.architect.api_key]])]),m(`div`,tn,[m(`label`,null,[i[59]||=c(` 模型 `,-1),m(`button`,{class:`btn-refresh btn-inline`,disabled:U.architect,onClick:Kn},a(U.architect?`…`:`🔄 刷新列表`),9,nn)]),g(m(`select`,{"onUpdate:modelValue":i[24]||=e=>X.architect.model=e,class:`model-select`},[i[60]||=m(`option`,{value:``,disabled:``},`—— 从 API 获取模型列表 ——`,-1),(o(!0),p(n,null,s(H.value,e=>(o(),p(`option`,{key:e.id,value:e.id},a(e.name||e.id),9,rn))),128))],512),[[u,X.architect.model]]),W.architect?(o(),p(`span`,an,a(W.architect),1)):e(``,!0)])])])]),m(`section`,on,[i[66]||=m(`div`,{class:`section-title`},[m(`span`,null,`⚙️ 协作管线 (Pipeline)`)],-1),m(`div`,sn,[m(`div`,cn,[m(`div`,ln,[m(`label`,un,[g(m(`input`,{type:`checkbox`,"onUpdate:modelValue":i[25]||=e=>X.pipeline.fast_path=e},null,512),[[ve,X.pipeline.fast_path]]),i[61]||=c(` 启用快路径(简单查询跳过本地模型,直接由大模型回答,省 API 费用) `,-1)])]),m(`div`,dn,[i[62]||=m(`label`,null,`最大协作轮次`,-1),g(m(`input`,{"onUpdate:modelValue":i[26]||=e=>X.pipeline.rounds_cap=e,type:`number`,min:`1`,max:`20`},null,512),[[r,X.pipeline.rounds_cap,void 0,{number:!0}]])]),m(`div`,fn,[i[63]||=m(`label`,null,`API Token 熔断上限`,-1),g(m(`input`,{"onUpdate:modelValue":i[27]||=e=>X.pipeline.api_token_cap=e,type:`number`,min:`100`,max:`100000`},null,512),[[r,X.pipeline.api_token_cap,void 0,{number:!0}]])]),m(`div`,pn,[i[65]||=m(`label`,null,`超限策略`,-1),g(m(`select`,{"onUpdate:modelValue":i[28]||=e=>X.pipeline.breach_policy=e},[...i[64]||=[m(`option`,{value:`architect_do`},`architect_do(超限交给大模型处理,推荐)`,-1),m(`option`,{value:`local_only`},`local_only(强制留在本地小模型)`,-1)]],512),[[u,X.pipeline.breach_policy]])])])])]),m(`div`,mn,[m(`button`,{class:`btn-primary`,disabled:h.value||!In.value,onClick:Zn},a(h.value?`保存中…`:`💾 保存设置`),9,hn),m(`button`,{class:`btn-secondary`,disabled:h.value,onClick:Qn},` 🔁 恢复默认 `,8,gn)]),v.value?(o(),p(`div`,{key:0,class:d([`toast`,v.value.ok?`toast-ok`:`toast-err`])},a(v.value.msg),3)):e(``,!0)]))}}),[[`__scopeId`,`data-v-f1fed4d2`]]);export{_ as default};