Files
mai-bot/dashboard/dist/assets/index-V_LLuoFO.js

2 lines
16 KiB
JavaScript

import{j as e,r as a,i as Ge}from"./router-zNjPR4CY.js";import{E as Ke,$ as Ve,N as M,a0 as He,B as x,A as I,m as xe,n as z,o as E,q as $,r as L,s as U,t as W,v as J,X as qe,Y as Xe,S as ge,a1 as ie,a2 as Ye}from"./index-CuOHsLf7.js";import{M as q,m as Ze,S as Qe,c as A,P as es,a as ss}from"./ProviderList-4mOft0xL.js";import{R as ts,u as rs,a as is}from"./restart-overlay-B44c6hlE.js";import{v as je,_ as we,w as ye,ah as Se,a6 as Ne,ai as ns}from"./icons-DTcdLw9j.js";import"./misc-BwRzHX8c.js";import"./radix-C-ZuImoP.js";import"./utils-DjBw3JGv.js";import"./popover-DSuRLFDH.js";import"./select-DGqIoF9r.js";import"./badge-CDs67obV.js";import"./checkbox-DWiVrbnx.js";import"./table-DFeDhBY_.js";import"./progress-C6-hh8fF.js";import"./system-api-DeeJapvB.js";function B(o){return o&&typeof o=="object"&&"config"in o?o.config:o}function Ss(){return e.jsx(ts,{children:e.jsx(as,{})})}function as(){const[o,S]=a.useState([]),[Pe,X]=a.useState(!0),[C,m]=a.useState(!1),[D,F]=a.useState(!1),[p,g]=a.useState(!1),[_e,b]=a.useState(!1),[Ce,G]=a.useState(null),[De,Y]=a.useState(null),[Te,Z]=a.useState(!1),[K,ne]=a.useState(null),[h,O]=a.useState(new Set),[ke,Q]=a.useState(!1),[f,R]=a.useState({isOpen:!1,providersToDelete:[],affectedModels:[],pendingProviders:[],context:"auto",oldProviders:[]}),[N,ae]=a.useState(new Set),[Ae,be]=a.useState(new Map),[Oe,Re]=a.useState(()=>localStorage.getItem("model-provider-restart-notice-dismissed")!=="true"),{toast:l}=Ke(),oe=Ge(),{state:c,goToStep:ee,registerTour:le}=Ve(),{triggerRestart:Me,isRestarting:T}=rs(),j=a.useRef(null),se=a.useRef(!0),w=a.useRef(null),ce=a.useRef(c.stepIndex);a.useEffect(()=>{le(q,Ze)},[le]),a.useEffect(()=>{if(c.activeTourId===q&&c.isRunning){const s=Qe[c.stepIndex];s&&!window.location.pathname.endsWith(s.replace("/config/",""))&&oe({to:s})}},[c.stepIndex,c.activeTourId,c.isRunning,oe]),a.useEffect(()=>{if(c.activeTourId===q&&c.isRunning){const s=ce.current,r=c.stepIndex;s>=3&&s<=9&&r<3&&b(!1),s>=10&&r>=3&&r<=9&&(G({name:"",base_url:"",api_key:"",client_type:"openai",max_retry:2,timeout:30,retry_interval:10}),Y(null),b(!0)),ce.current=r}},[c.stepIndex,c.activeTourId,c.isRunning]),a.useEffect(()=>{if(c.activeTourId!==q||!c.isRunning)return;const s=r=>{const t=r.target,i=c.stepIndex;i===2&&t.closest('[data-tour="add-provider-button"]')?setTimeout(()=>ee(3),300):i===9&&t.closest('[data-tour="provider-cancel-button"]')&&setTimeout(()=>ee(10),300)};return document.addEventListener("click",s,!0),()=>document.removeEventListener("click",s,!0)},[c,ee]),a.useEffect(()=>{Ie()},[]);const Ie=async()=>{try{X(!0);const s=await M();if(!s.success){l({title:"加载失败",description:s.error,variant:"destructive"}),X(!1);return}const r=B(s.data),t=Array.isArray(r.api_providers)?r.api_providers:[];S(t),w.current=JSON.stringify(t.map(A)),g(!1),se.current=!1}catch(s){console.error("加载配置失败:",s)}finally{X(!1)}},V=async()=>{await Me()},ze=()=>{localStorage.setItem("model-provider-restart-notice-dismissed","true"),Re(!1)},de=async()=>{try{m(!0),j.current&&clearTimeout(j.current);const s=o.map(n=>({...n,max_retry:n.max_retry??2,timeout:n.timeout??30,retry_interval:n.retry_interval??10})),{shouldProceed:r}=await k(s,"restart");if(!r){m(!1);return}const t=await M();if(!t.success){l({title:"保存失败",description:t.error,variant:"destructive"}),m(!1);return}const i=B(t.data),P=new Set(s.map(n=>n.name)),v=(Array.isArray(i.models)?i.models:[]).filter(n=>typeof n=="object"&&n!==null&&"api_provider"in n&&P.has(n.api_provider));i.api_providers=s,i.models=v;const y=await ie(i);if(!y.success){l({title:"保存失败",description:y.error,variant:"destructive"}),m(!1);return}w.current=JSON.stringify(s),g(!1),l({title:"保存成功",description:"正在重启麦麦..."}),await V()}catch(s){console.error("保存配置失败:",s),l({title:"保存失败",description:s.message,variant:"destructive"}),m(!1)}},k=a.useCallback(async(s,r="auto")=>{try{const t=await M();if(!t.success)return console.error("加载配置失败:",t.error),{shouldProceed:!0,providers:s};const i=B(t.data),P=new Set(o.map(u=>u.name)),_=new Set(s.map(u=>u.name)),v=Array.from(P).filter(u=>!_.has(u));if(v.length===0)return{shouldProceed:!0,providers:s};const n=(Array.isArray(i.models)?i.models:[]).filter(u=>typeof u=="object"&&u!==null&&"api_provider"in u&&v.includes(u.api_provider));return n.length===0?{shouldProceed:!0,providers:s}:(R({isOpen:!0,providersToDelete:v,affectedModels:n,pendingProviders:s,context:r,oldProviders:[...o]}),{shouldProceed:!1,providers:s})}catch(t){return console.error("检查删除影响失败:",t),{shouldProceed:!0,providers:s}}},[o]),Ee=async()=>{try{const s=f.context==="auto"?F:m;s(!0),R(d=>({...d,isOpen:!1}));const r=await M();if(!r.success){l({title:"加载失败",description:r.error,variant:"destructive"}),s(!1);return}const t=B(r.data),i=f.pendingProviders.map(A),P=new Set(i.map(d=>d.name)),v=(Array.isArray(t.models)?t.models:[]).filter(d=>typeof d=="object"&&d!==null&&"api_provider"in d&&P.has(d.api_provider)),y=new Set(f.affectedModels.map(d=>typeof d=="object"&&d!==null&&"name"in d?d.name:"")),n=t.model_task_config;n&&typeof n=="object"&&Object.keys(n).forEach(d=>{const H=n[d];if(H&&typeof H=="object"&&"model_list"in H){const re=H;Array.isArray(re.model_list)&&(re.model_list=re.model_list.filter(ve=>typeof ve=="string"&&!y.has(ve)))}}),t.api_providers=i,t.models=v,t.model_task_config=n;const u=await ie(t);if(!u.success){l({title:"保存失败",description:u.error,variant:"destructive"}),s(!1);return}S(f.pendingProviders),w.current=JSON.stringify(i),g(!1),l({title:"删除成功",description:`已删除 ${f.providersToDelete.length} 个提供商和 ${f.affectedModels.length} 个关联模型`}),R({isOpen:!1,providersToDelete:[],affectedModels:[],pendingProviders:[],context:"auto",oldProviders:[]}),O(new Set),f.context==="restart"&&await V()}catch(s){console.error("删除失败:",s),l({title:"删除失败",description:s.message,variant:"destructive"})}finally{f.context==="auto"?F(!1):m(!1)}},$e=()=>{f.oldProviders.length>0&&S(f.oldProviders),R({isOpen:!1,providersToDelete:[],affectedModels:[],pendingProviders:[],context:"auto",oldProviders:[]}),g(!1)},ue=a.useCallback(async s=>{if(se.current)return;const{shouldProceed:r}=await k(s,"auto");if(!r){g(!0);return}try{F(!0);const t=s.map(A),i=await He("api_providers",t);if(!i.success){console.error("自动保存失败:",i.error),l({title:"自动保存失败",description:i.error,variant:"destructive"}),g(!0);return}w.current=JSON.stringify(t),g(!1)}catch(t){console.error("自动保存失败:",t),l({title:"自动保存失败",description:t.message,variant:"destructive"}),g(!0)}finally{F(!1)}},[o,k]);a.useEffect(()=>{if(se.current)return;const s=JSON.stringify(o.map(A));if(w.current===null){w.current=s;return}if(s!==w.current)return g(!0),j.current&&clearTimeout(j.current),j.current=setTimeout(()=>{ue(o)},2e3),()=>{j.current&&clearTimeout(j.current)}},[o,ue]);const fe=async()=>{try{m(!0),j.current&&clearTimeout(j.current);const s=o.map(A),{shouldProceed:r}=await k(s,"manual");if(!r){m(!1);return}const t=await M();if(!t.success){l({title:"保存失败",description:t.error,variant:"destructive"}),m(!1);return}const i=B(t.data),P=new Set(s.map(n=>n.name)),_=Array.isArray(i.models)?i.models:[],v=_.filter(n=>{if(typeof n!="object"||n===null||!("api_provider"in n))return!1;const u=n,d=P.has(u.api_provider);return d||console.warn(`模型 "${u.name}" 引用了已删除的提供商 "${u.api_provider}"、将被移除`),d});if(_.length!==v.length){const n=_.length-v.length;l({title:"注意",description:`已自动移除 ${n} 个引用已删除提供商的模型`,variant:"default"})}console.log("发送的 providers 数据:",s),i.api_providers=s,i.models=v,console.log("完整配置数据:",i);const y=await ie(i);if(!y.success){l({title:"保存失败",description:y.error,variant:"destructive"}),m(!1);return}w.current=JSON.stringify(s),g(!1),l({title:"保存成功",description:"模型提供商配置已保存"})}catch(s){console.error("保存配置失败:",s),l({title:"保存失败",description:s.message,variant:"destructive"})}finally{m(!1)}},te=(s,r)=>{G(s||{name:"",base_url:"",api_key:"",client_type:"openai",max_retry:2,timeout:30,retry_interval:10}),Y(r),b(!0)},Le=(s,r)=>{const t=A(s);if(r!==null){const i=[...o];i[r]=t,S(i)}else S([...o,t]);b(!1),G(null),Y(null)},Ue=s=>{ne(s),Z(!0)},We=async()=>{if(K!==null){const s=o.filter((t,i)=>i!==K),{shouldProceed:r}=await k(s,"manual");r&&(S(s),l({title:"删除成功",description:"提供商已从列表中移除"}))}Z(!1),ne(null)},Je=s=>{const r=new Set(h);r.has(s)?r.delete(s):r.add(s),O(r)},Be=()=>{if(h.size===o.length)O(new Set);else{const s=o.map((r,t)=>t);O(new Set(s))}},me=()=>{if(h.size===0){l({title:"提示",description:"请先选择要删除的提供商",variant:"default"});return}Q(!0)},Fe=async()=>{const s=o.filter((t,i)=>!h.has(i)),{shouldProceed:r}=await k(s,"manual");r&&(S(s),O(new Set),l({title:"批量删除成功",description:`已删除 ${h.size} 个提供商`})),Q(!1)},pe=async s=>{ae(r=>new Set(r).add(s));try{const r=await Ye(s);if(!r.success){l({title:"测试失败",description:r.error,variant:"destructive"});return}const t=r.data;be(i=>new Map(i).set(s,t)),t.network_ok?t.api_key_valid===!0?l({title:"连接正常",description:`${s} 网络连接正常、API Key 有效 (${t.latency_ms}ms)`}):t.api_key_valid===!1?l({title:"连接正常但 Key 无效",description:`${s} 网络连接正常、但 API Key 无效或已过期`,variant:"destructive"}):l({title:"网络连接正常",description:`${s} 可以访问 (${t.latency_ms}ms)`}):l({title:"连接失败",description:t.error||"无法连接到提供商",variant:"destructive"})}catch(r){l({title:"测试失败",description:r.message,variant:"destructive"})}finally{ae(r=>{const t=new Set(r);return t.delete(s),t})}},he=async()=>{for(const s of o)await pe(s.name)};return Pe?e.jsx("div",{className:"space-y-4 sm:space-y-6 p-4 sm:p-6",children:e.jsx("div",{className:"flex items-center justify-center h-64",children:e.jsx("p",{className:"text-muted-foreground",children:"加载中..."})})}):e.jsxs("div",{className:"space-y-4 sm:space-y-6 p-4 sm:p-6",children:[e.jsxs("div",{className:"flex flex-col sm:flex-row sm:items-center justify-between gap-4",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"text-2xl sm:text-3xl font-bold",children:"模型厂商设置"}),e.jsx("p",{className:"text-muted-foreground mt-1 sm:mt-2 text-sm sm:text-base",children:"管理 AI 模型厂商的 API 配置"})]}),e.jsxs("div",{className:"hidden",children:[h.size>0&&e.jsxs(x,{onClick:me,size:"sm",variant:"destructive",className:"w-full sm:w-auto",children:[e.jsx(je,{className:"mr-2 h-4 w-4",strokeWidth:2,fill:"none"}),"批量删除 (",h.size,")"]}),e.jsxs(x,{onClick:he,size:"sm",variant:"outline",className:"w-full sm:w-auto",disabled:o.length===0||N.size>0,children:[e.jsx(we,{className:"mr-2 h-4 w-4"}),N.size>0?`测试中 (${N.size})`:"测试全部"]}),e.jsxs(x,{onClick:()=>te(null,null),size:"sm",className:"w-full sm:w-auto","data-tour":"add-provider-button",children:[e.jsx(ye,{className:"mr-2 h-4 w-4",strokeWidth:2,fill:"none"}),"添加提供商"]}),e.jsxs(x,{onClick:fe,disabled:C||D||!p||T,size:"sm",variant:"outline",className:"w-full sm:w-auto sm:min-w-[120px]",children:[e.jsx(Se,{className:"mr-2 h-4 w-4",strokeWidth:2,fill:"none"}),C?"保存中...":D?"自动保存中...":p?"保存配置":"已保存"]}),e.jsxs(I,{children:[e.jsx(xe,{asChild:!0,children:e.jsxs(x,{disabled:C||D||T,size:"sm",className:"w-full sm:w-auto sm:min-w-[120px]",children:[e.jsx(Ne,{className:"mr-2 h-4 w-4"}),T?"重启中...":p?"保存并重启":"重启麦麦"]})}),e.jsxs(z,{children:[e.jsxs(E,{children:[e.jsx($,{children:"确认重启麦麦?"}),e.jsx(L,{asChild:!0,children:e.jsx("div",{children:e.jsx("p",{children:p?"当前有未保存的配置更改。点击确认将先保存配置,然后重启麦麦使新配置生效。重启过程中麦麦将暂时离线。":"即将重启麦麦主程序。重启过程中麦麦将暂时离线,配置将在重启后生效。"})})})]}),e.jsxs(U,{children:[e.jsx(W,{children:"取消"}),e.jsx(J,{onClick:p?de:V,children:p?"保存并重启":"确认重启"})]})]})]})]})]}),Oe&&e.jsxs(qe,{children:[e.jsx(ns,{className:"h-4 w-4"}),e.jsxs(Xe,{className:"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",children:[e.jsxs("span",{children:["配置更新后需要",e.jsx("strong",{children:"重启麦麦"}),'才能生效。你可以点击右上角的"保存并重启"按钮一键完成保存和重启。']}),e.jsx(x,{type:"button",variant:"outline",size:"sm",onClick:ze,children:"我知道了"})]})]}),e.jsx(ge,{className:"h-[calc(100vh-260px)]",children:e.jsx(es,{providers:o,testingProviders:N,testResults:Ae,selectedProviders:h,toolbarActions:e.jsxs(e.Fragment,{children:[h.size>0&&e.jsxs(x,{onClick:me,size:"sm",variant:"destructive",className:"w-full sm:w-auto",children:[e.jsx(je,{className:"mr-2 h-4 w-4",strokeWidth:2,fill:"none"}),e.jsxs("span",{className:"text-sm",children:["批量删除 (",h.size,")"]})]}),e.jsxs(x,{onClick:he,size:"sm",variant:"outline",className:"w-full sm:w-auto",disabled:o.length===0||N.size>0,children:[e.jsx(we,{className:"mr-2 h-4 w-4"}),e.jsx("span",{className:"text-sm",children:N.size>0?`测试中 (${N.size})`:"测试全部连接"})]}),e.jsxs(x,{onClick:()=>te(null,null),size:"sm",className:"w-full sm:w-auto","data-tour":"add-provider-button",children:[e.jsx(ye,{className:"mr-2 h-4 w-4",strokeWidth:2,fill:"none"}),e.jsx("span",{className:"text-sm",children:"添加厂商"})]}),e.jsxs(x,{onClick:fe,disabled:C||D||!p||T,size:"sm",variant:"outline",className:"w-full sm:w-auto sm:min-w-[120px]",children:[e.jsx(Se,{className:"mr-2 h-4 w-4",strokeWidth:2,fill:"none"}),e.jsx("span",{className:"text-sm",children:C?"保存中...":D?"自动保存中...":p?"保存配置":"已保存"})]}),e.jsxs(I,{children:[e.jsx(xe,{asChild:!0,children:e.jsxs(x,{disabled:C||D||T,size:"sm",className:"w-full sm:w-auto sm:min-w-[120px]",children:[e.jsx(Ne,{className:"mr-2 h-4 w-4"}),T?"重启中...":p?"保存并重启":"重启麦麦"]})}),e.jsxs(z,{children:[e.jsxs(E,{children:[e.jsx($,{children:"确认重启麦麦?"}),e.jsx(L,{asChild:!0,children:e.jsx("div",{children:e.jsx("p",{children:p?"当前有未保存的配置更改。确认后会先保存配置,然后重启麦麦使新配置生效。":"即将重启麦麦主程序。配置将在重启后生效。"})})})]}),e.jsxs(U,{children:[e.jsx(W,{children:"取消"}),e.jsx(J,{onClick:p?de:V,children:p?"保存并重启":"确认重启"})]})]})]})]}),onEdit:te,onDelete:Ue,onTest:pe,onToggleSelect:Je,onToggleSelectAll:Be})}),e.jsx(ss,{open:_e,onOpenChange:b,editingProvider:Ce,editingIndex:De,providers:o,onSave:Le,tourState:c}),e.jsx(I,{open:Te,onOpenChange:Z,children:e.jsxs(z,{children:[e.jsxs(E,{children:[e.jsx($,{children:"确认删除"}),e.jsxs(L,{children:['确定要删除提供商 "',K!==null?o[K]?.name:"",'" 吗? 此操作无法撤销。']})]}),e.jsxs(U,{children:[e.jsx(W,{children:"取消"}),e.jsx(J,{onClick:We,children:"删除"})]})]})}),e.jsx(I,{open:ke,onOpenChange:Q,children:e.jsxs(z,{children:[e.jsxs(E,{children:[e.jsx($,{children:"确认批量删除"}),e.jsxs(L,{children:["确定要删除选中的 ",h.size," 个提供商吗? 此操作无法撤销。"]})]}),e.jsxs(U,{children:[e.jsx(W,{children:"取消"}),e.jsx(J,{onClick:Fe,className:"bg-destructive text-destructive-foreground hover:bg-destructive/90",children:"批量删除"})]})]})}),e.jsx(I,{open:f.isOpen,onOpenChange:s=>R(r=>({...r,isOpen:s})),children:e.jsxs(z,{className:"max-w-2xl",children:[e.jsxs(E,{children:[e.jsx($,{children:"确认删除提供商"}),e.jsx(L,{asChild:!0,children:e.jsxs("div",{className:"space-y-3",children:[e.jsxs("p",{children:["您即将删除以下提供商:",e.jsx("strong",{className:"text-foreground ml-1",children:f.providersToDelete.join(", ")})]}),e.jsxs("p",{className:"text-yellow-600 dark:text-yellow-500 font-medium",children:["⚠️ 此操作将同时删除 ",f.affectedModels.length," 个关联的模型:"]}),e.jsx(ge,{className:"h-32 w-full rounded border p-3",children:e.jsx("div",{className:"space-y-1",children:f.affectedModels.map((s,r)=>e.jsxs("div",{className:"text-sm",children:[e.jsx("span",{className:"font-mono text-muted-foreground",children:"•"}),e.jsx("span",{className:"ml-2 font-medium",children:s.name}),e.jsxs("span",{className:"ml-2 text-xs text-muted-foreground",children:["(",s.model_identifier,")"]})]},r))})}),e.jsx("p",{className:"text-sm text-muted-foreground",children:"这些模型将从模型列表和所有任务分配中移除。此操作无法撤销。"})]})})]}),e.jsxs(U,{children:[e.jsx(W,{onClick:$e,children:"取消"}),e.jsx(J,{onClick:Ee,className:"bg-destructive text-destructive-foreground hover:bg-destructive/90",children:"确认删除"})]})]})}),e.jsx(is,{})]})}export{Ss as ModelProviderConfigPage};