ellm-proxy 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +60 -0
  3. package/bin/commands/autostart.js +213 -0
  4. package/bin/commands/config.js +8 -0
  5. package/bin/commands/pm2.js +28 -0
  6. package/bin/commands/service.js +107 -0
  7. package/bin/ellm-proxy.js +61 -0
  8. package/dist/package.json +1 -0
  9. package/dist/server.js +12176 -0
  10. package/dist/web-ui/assets/DashboardView-CQdlgirS.js +1 -0
  11. package/dist/web-ui/assets/DashboardView-qrQrkgLA.css +1 -0
  12. package/dist/web-ui/assets/LayoutView-BaY4Pz9W.js +1 -0
  13. package/dist/web-ui/assets/LoginView-CPvz1Ylx.js +1 -0
  14. package/dist/web-ui/assets/LoginView-Dg0mB6ok.css +1 -0
  15. package/dist/web-ui/assets/LogsView-B-bIj36j.js +9 -0
  16. package/dist/web-ui/assets/LogsView-Wl3_adHm.css +1 -0
  17. package/dist/web-ui/assets/ProcessView-0x_ShSwV.js +1 -0
  18. package/dist/web-ui/assets/ProcessView-DzMqBJmd.css +1 -0
  19. package/dist/web-ui/assets/ProvidersView-BxAxDRFH.css +1 -0
  20. package/dist/web-ui/assets/ProvidersView-GQENcvJS.js +31 -0
  21. package/dist/web-ui/assets/SettingsView-BDkymsrS.css +1 -0
  22. package/dist/web-ui/assets/SettingsView-BrAiaskZ.js +1 -0
  23. package/dist/web-ui/assets/config-DNn5cjYY.js +1 -0
  24. package/dist/web-ui/assets/index-BecfPmag.css +1 -0
  25. package/dist/web-ui/assets/index-DFlUx18F.js +96 -0
  26. package/dist/web-ui/assets/providers-CGEmOxne.js +1 -0
  27. package/dist/web-ui/assets/useApi-BRPinb_2.js +1 -0
  28. package/dist/web-ui/assets/useManagerApi-BDWJz5o6.js +1 -0
  29. package/dist/web-ui/assets/useToast-D7xlwSg6.js +1 -0
  30. package/dist/web-ui/config.js +10 -0
  31. package/dist/web-ui/index.html +15 -0
  32. package/docs/DESIGN.md +99 -0
  33. package/ellm.config.example.json +33 -0
  34. package/manager/api/harness/backup.js +20 -0
  35. package/manager/api/harness/claude-code.js +54 -0
  36. package/manager/api/harness/codex.js +123 -0
  37. package/manager/api/harness/dsh.js +175 -0
  38. package/manager/api/harness/index.js +118 -0
  39. package/manager/api/service.js +157 -0
  40. package/manager/index.js +170 -0
  41. package/package.json +40 -0
  42. package/utils/interpreter.js +14 -0
  43. package/utils/paths.js +44 -0
  44. package/utils/pm2.js +130 -0
  45. package/utils/port.js +35 -0
  46. package/utils/service.js +189 -0
  47. package/utils/ui.js +59 -0
@@ -0,0 +1 @@
1
+ import{A as T}from"./useApi-BRPinb_2.js";import{u as H}from"./providers-CGEmOxne.js";import{u as J}from"./useToast-D7xlwSg6.js";import{d as Q,i as W,c as h,a as o,w as s,j as V,k as B,l as y,r as _,m as X,o as c,n as Z,u as Y,t as u,b as l,e as p,F as ee,p as te,f as N,_ as ae}from"./index-DFlUx18F.js";import"./config-DNn5cjYY.js";const le={class:"flex-col-gap"},oe=["onClick"],se={key:1},ne={class:"flex-between"},de={class:"flex-gap-8"},ue={class:"usage-grid"},ie={class:"usage-cell"},re={class:"usage-num orange"},ce={class:"usage-cell"},ve={class:"usage-num blue"},_e={class:"usage-cell"},pe={class:"usage-num green"},ge={class:"usage-cell"},me={class:"usage-num purple"},fe={class:"usage-cell"},he={class:"usage-num purple"},ye={key:0,class:"usage-cell"},be={class:"usage-num gray"},we={key:0},ke=["onClick"],De=Q({__name:"DashboardView",setup(Ce){const k=H(),{toast:M}=J(),i=y(null),b=y(!0),D=y(""),v=y(null),w=y("today"),C=a=>{const e=a.getFullYear(),t=String(a.getMonth()+1).padStart(2,"0"),r=String(a.getDate()).padStart(2,"0");return`${e}-${t}-${r}`},F=a=>{const e=new Date;let t,r=e;if(a==="today")t=e;else if(a==="yesterday")t=new Date(e),t.setDate(e.getDate()-1),r=t;else if(a==="beforeYesterday")t=new Date(e),t.setDate(e.getDate()-2),r=t;else if(a==="last3days")t=new Date(e),t.setDate(e.getDate()-2);else if(a==="week"){t=new Date(e);const S=(e.getDay()+6)%7;t.setDate(e.getDate()-S)}else if(a==="month")t=new Date(e.getFullYear(),e.getMonth(),1);else return;w.value=a,v.value=[C(t),C(r)],f()},U=a=>{a?F(a):(w.value="",v.value=null,f())},A=()=>{w.value="",f()},I=N(()=>{var e;const a=new Set;for(const t of k.suppliers)for(const r of t.models)a.add(r.modelName);if((e=i.value)!=null&&e.models)for(const t of i.value.models)a.add(t.model);return[...a].sort()}),f=async()=>{var a;b.value=!0;try{const e=new URLSearchParams;D.value&&e.set("model",D.value),((a=v.value)==null?void 0:a.length)===2&&(e.set("startDate",v.value[0]),e.set("endDate",v.value[1]));const t=e.toString();i.value=await T.get(`/api/usage${t?`?${t}`:""}`)}catch(e){console.error(e)}finally{b.value=!1}},R=async()=>{var a;try{const[e]=await Promise.all([T.get("/api/usage"),k.loadProviders(),T.get("/api/status").then(t=>x.value=t.listenPort).catch(()=>x.value)]);if(i.value=e,!((a=v.value)!=null&&a.length)){const t=new Date;v.value=[C(t),C(t)],await f()}}catch(e){console.error(e)}finally{b.value=!1}},L=window.location.hostname,x=y(Number(window.location.port)||null),E=N(()=>`http://${L}${x.value?`:${x.value}`:""}`),g=a=>a.toLocaleString(),j=a=>a==null||a<=0?"-":a>=1e3?(a/1e3).toFixed(2)+"s":Math.round(a)+"ms",$=async a=>{try{await navigator.clipboard.writeText(a),M("已复制到剪贴板","success")}catch{const e=document.createElement("textarea");e.value=a,e.style.position="fixed",e.style.opacity="0",document.body.appendChild(e),e.select();try{document.execCommand("copy"),M("已复制到剪贴板","success")}catch{M("复制失败","error")}document.body.removeChild(e)}};return W(R),(a,e)=>{const t=_("el-table-column"),r=_("el-table"),S=_("el-card"),m=_("el-option"),P=_("el-select"),q=_("el-date-picker"),z=_("el-button"),K=_("el-tag"),O=_("el-empty"),G=X("loading");return c(),h("div",le,[o(S,{shadow:"never"},{header:s(()=>[...e[3]||(e[3]=[l("span",{class:"fw-600"},"服务信息",-1)])]),default:s(()=>[Z((c(),V(r,{border:"",data:[{key:"API 地址",value:E.value},{key:"供应商数量",value:String(Y(k).providerCount)},{key:"模型总数",value:String(Y(k).totalModelCount)}],"show-header":!1,class:"full-width"},{default:s(()=>[o(t,{prop:"key",width:"160"}),o(t,null,{default:s(({row:d})=>[d.key==="API 地址"?(c(),h("span",{key:0,class:"copy-text",onClick:n=>$(d.value)},u(d.value),9,oe)):(c(),h("span",se,u(d.value),1))]),_:1})]),_:1},8,["data"])),[[G,b.value]])]),_:1}),i.value?(c(),V(S,{key:0,shadow:"never"},{header:s(()=>[l("div",ne,[e[5]||(e[5]=l("span",{class:"fw-600"},"用量统计",-1)),l("div",de,[o(P,{modelValue:D.value,"onUpdate:modelValue":e[0]||(e[0]=d=>D.value=d),placeholder:"全部模型",clearable:"",filterable:"",style:{width:"220px"},onChange:f},{default:s(()=>[(c(!0),h(ee,null,te(I.value,d=>(c(),V(m,{key:d,label:d,value:d},null,8,["label","value"]))),128))]),_:1},8,["modelValue"]),o(P,{modelValue:w.value,"onUpdate:modelValue":e[1]||(e[1]=d=>w.value=d),placeholder:"时间范围",clearable:"",style:{width:"130px"},onChange:U},{default:s(()=>[o(m,{label:"今天",value:"today"}),o(m,{label:"昨天",value:"yesterday"}),o(m,{label:"前天",value:"beforeYesterday"}),o(m,{label:"近三天",value:"last3days"}),o(m,{label:"本周",value:"week"}),o(m,{label:"本月",value:"month"})]),_:1},8,["modelValue"]),o(q,{modelValue:v.value,"onUpdate:modelValue":e[2]||(e[2]=d=>v.value=d),type:"daterange","range-separator":"至","start-placeholder":"开始日期","end-placeholder":"结束日期","value-format":"YYYY-MM-DD",style:{width:"260px"},onChange:A},null,8,["modelValue"]),o(z,{loading:b.value,onClick:f},{default:s(()=>[...e[4]||(e[4]=[p("刷新",-1)])]),_:1},8,["loading"])]),o(K,{type:"info"},{default:s(()=>[p(u(i.value.total.calls)+" 次调用",1)]),_:1})])]),default:s(()=>{var d;return[l("div",ue,[l("div",ie,[l("div",re,u(g(i.value.total.total_tokens)),1),e[6]||(e[6]=l("div",{class:"usage-label"},"总 Token",-1))]),l("div",ce,[l("div",ve,u(g(i.value.total.input_tokens)),1),e[7]||(e[7]=l("div",{class:"usage-label"},"输入 Token",-1))]),l("div",_e,[l("div",pe,u(g(i.value.total.output_tokens)),1),e[8]||(e[8]=l("div",{class:"usage-label"},"输出 Token",-1))]),l("div",ge,[l("div",me,u(g(i.value.total.cache_read_tokens)),1),e[9]||(e[9]=l("div",{class:"usage-label"},"缓存读取",-1))]),l("div",fe,[l("div",he,u(i.value.total.cache_rate!=null?i.value.total.cache_rate+"%":"-"),1),e[10]||(e[10]=l("div",{class:"usage-label"},"缓存命中率",-1))]),i.value.logStorageMB!=null?(c(),h("div",ye,[l("div",be,u(i.value.logStorageMB),1),e[11]||(e[11]=l("div",{class:"usage-label"},"日志占用 (MB)",-1))])):B("",!0)]),(d=i.value.models)!=null&&d.length?(c(),h("div",we,[e[12]||(e[12]=l("div",{class:"section-title"},"模型用量",-1)),o(r,{data:i.value.models,border:"",stripe:"",class:"full-width"},{default:s(()=>[o(t,{prop:"model",label:"模型","min-width":"200"},{default:s(({row:n})=>[l("span",{class:"copy-text",onClick:xe=>$(n.model)},u(n.model),9,ke)]),_:1}),o(t,{label:"调用次数",width:"90",align:"center"},{default:s(({row:n})=>[p(u(n.calls),1)]),_:1}),o(t,{label:"平均耗时",width:"110",align:"center"},{default:s(({row:n})=>[p(u(j(n.avg_duration)),1)]),_:1}),o(t,{label:"总 Token",width:"140",align:"center"},{default:s(({row:n})=>[p(u(g(n.total_tokens)),1)]),_:1}),o(t,{label:"输入 Token",width:"140",align:"center"},{default:s(({row:n})=>[p(u(g(n.input_tokens)),1)]),_:1}),o(t,{label:"输出 Token",width:"140",align:"center"},{default:s(({row:n})=>[p(u(g(n.output_tokens)),1)]),_:1}),o(t,{label:"缓存读取",width:"130",align:"center"},{default:s(({row:n})=>[p(u(g(n.cache_read_tokens)),1)]),_:1}),o(t,{label:"缓存命中率",width:"110",align:"center"},{default:s(({row:n})=>[p(u(n.cache_rate!=null?n.cache_rate+"%":"-"),1)]),_:1})]),_:1},8,["data"])])):(c(),V(O,{key:1,description:"暂无调用记录","image-size":60}))]}),_:1})):B("",!0)])}}}),Pe=ae(De,[["__scopeId","data-v-a652207c"]]);export{Pe as default};
@@ -0,0 +1 @@
1
+ .copy-text[data-v-a652207c]{color:#409eff;cursor:pointer}.copy-text[data-v-a652207c]:hover{text-decoration:underline}
@@ -0,0 +1 @@
1
+ import{d as C,c as y,a as t,w as o,b as l,r as s,o as V,e as r,t as W,u as B,f as D,g as L,h as N}from"./index-DFlUx18F.js";const S={class:"layout"},A={class:"header-nav"},E={class:"header-actions"},I={class:"main"},R={class:"page"},M=C({__name:"LayoutView",setup(T){const d=N(),i=L(),u=(()=>{const n=localStorage.getItem("webui_token")||"";try{return atob(n).split(":")[0]||"用户"}catch{return"用户"}})(),_=D(()=>{const n=d.path;return n==="/"?"/":n.startsWith("/providers")?"/providers":n.startsWith("/settings")?"/settings":n.startsWith("/logs")?"/logs":n.startsWith("/process")?"/process":"/"}),c=()=>{localStorage.removeItem("webui_token"),i.push("/login")},m=n=>{n==="logout"&&c()};return(n,e)=>{const a=s("el-menu-item"),p=s("el-menu"),f=s("ArrowDown"),g=s("el-icon"),w=s("el-button"),v=s("el-dropdown-item"),h=s("el-dropdown-menu"),x=s("el-dropdown"),b=s("el-header"),k=s("router-view");return V(),y("div",S,[t(b,{class:"header"},{default:o(()=>[e[6]||(e[6]=l("h1",null,[l("span",null,"E"),r("LLM")],-1)),l("nav",A,[t(p,{mode:"horizontal","default-active":_.value,ellipsis:!1,router:"",class:"header-menu"},{default:o(()=>[t(a,{index:"/"},{default:o(()=>[...e[0]||(e[0]=[r("首页",-1)])]),_:1}),t(a,{index:"/providers"},{default:o(()=>[...e[1]||(e[1]=[r("供应商管理",-1)])]),_:1}),t(a,{index:"/logs"},{default:o(()=>[...e[2]||(e[2]=[r("日志",-1)])]),_:1}),t(a,{index:"/settings"},{default:o(()=>[...e[3]||(e[3]=[r("设置",-1)])]),_:1}),t(a,{index:"/process"},{default:o(()=>[...e[4]||(e[4]=[r("进程管理",-1)])]),_:1})]),_:1},8,["default-active"])]),l("div",E,[t(x,{trigger:"click",onCommand:m,class:"ml-12"},{dropdown:o(()=>[t(h,null,{default:o(()=>[t(v,{command:"logout"},{default:o(()=>[...e[5]||(e[5]=[r("退出",-1)])]),_:1})]),_:1})]),default:o(()=>[t(w,null,{default:o(()=>[r(W(B(u))+" ",1),t(g,{class:"el-icon--right"},{default:o(()=>[t(f)]),_:1})]),_:1})]),_:1})])]),_:1}),l("main",I,[l("div",R,[t(k)])])])}}});export{M as default};
@@ -0,0 +1 @@
1
+ import{d as b,c as h,a as o,w as l,r as a,o as _,b as k,Z as x,X as f,j as C,k as L,e as B,l as i,g as N,_ as I}from"./index-DFlUx18F.js";import{A as K}from"./useApi-BRPinb_2.js";const A={class:"login-wrap"},E=b({__name:"LoginView",setup(M){const v=N(),n=i(""),s=i(""),t=i(""),d=i(!1);async function r(){if(!n.value){t.value="请输入用户名";return}if(!s.value){t.value="请输入密码";return}d.value=!0,t.value="";try{const u=await K.post("/api/auth/login",{username:n.value.trim(),password:s.value});localStorage.setItem("webui_token",u.token),v.push("/")}catch(u){t.value=u.message||"登录失败"}finally{d.value=!1}}return(u,e)=>{const c=a("el-input"),m=a("el-form-item"),w=a("el-alert"),g=a("el-button"),V=a("el-form"),y=a("el-card");return _(),h("div",A,[o(y,{shadow:"hover",class:"login-card"},{default:l(()=>[e[3]||(e[3]=k("h2",{class:"login-title"},"ELLM",-1)),o(V,{"label-position":"top",onSubmit:x(r,["prevent"])},{default:l(()=>[o(m,{label:"用户名"},{default:l(()=>[o(c,{modelValue:n.value,"onUpdate:modelValue":e[0]||(e[0]=p=>n.value=p),placeholder:"请输入用户名",onKeyup:f(r,["enter"])},null,8,["modelValue"])]),_:1}),o(m,{label:"密码"},{default:l(()=>[o(c,{modelValue:s.value,"onUpdate:modelValue":e[1]||(e[1]=p=>s.value=p),type:"password",placeholder:"请输入密码","show-password":"",onKeyup:f(r,["enter"])},null,8,["modelValue"])]),_:1}),t.value?(_(),C(w,{key:0,title:t.value,type:"error","show-icon":"",class:"mb-16"},null,8,["title"])):L("",!0),o(g,{type:"primary",class:"full-width",loading:d.value,onClick:r},{default:l(()=>[...e[2]||(e[2]=[B(" 登录 ",-1)])]),_:1},8,["loading"])]),_:1})]),_:1})])}}}),j=I(E,[["__scopeId","data-v-d5b6a90c"]]);export{j as default};
@@ -0,0 +1 @@
1
+ .login-card[data-v-d5b6a90c]{max-width:400px;width:100%}.login-title[data-v-d5b6a90c]{text-align:center;margin-bottom:24px;color:#3b82f6}
@@ -0,0 +1,9 @@
1
+ import{d as he,i as xe,c,a as l,w as a,l as v,r as p,o as d,b as i,F as T,p as we,X as Ce,e as u,n as te,j as C,t as r,k as $,f as De,m as Ve,_ as $e}from"./index-DFlUx18F.js";import{A as J}from"./useApi-BRPinb_2.js";import{u as Se}from"./providers-CGEmOxne.js";import{u as Ie}from"./useToast-D7xlwSg6.js";import"./config-DNn5cjYY.js";const qe={class:"logs-page"},ze={class:"filter-bar"},Ue={class:"table-wrap"},je=["onClick"],Re={key:1},Te={class:"pagination-bar"},Ne={key:0,class:"detail-content"},Me={key:0,class:"ip-link"},Ye={class:"ip-pop"},Ee={class:"ip-pop-row"},Pe={key:0,class:"ip-pop-row"},Fe={key:1,class:"ip-pop-error"},Le={key:2},Be=["href"],Je={key:1},Ke={key:1},Oe={class:"error-text"},Ae={class:"pane-bar"},Ge={class:"json-block"},He={class:"pane-bar"},Xe={class:"json-block"},Qe={class:"pane-bar"},We={class:"json-block"},Ze={class:"pane-bar"},et={class:"json-block"},tt={class:"pane-bar"},lt={class:"json-block"},at={class:"pane-bar"},st={class:"json-block"},ot={class:"pane-bar"},nt={class:"json-block"},rt={class:"pane-bar"},ut={class:"json-block"},it=he({__name:"LogsView",setup(dt){const K=Se(),{toast:N}=Ie(),D=v(""),f=v(null),S=v("2xx"),I=v("model"),q=v(""),z=v(""),M=v([]),O=v(0),U=v(1),j=v(50),Y=v(!1),E=v(!1),s=v(null),P=v("info"),A=t=>{if(!t)return"-";const e=new Date(t),n=w=>String(w).padStart(2,"0");return`${e.getFullYear()}-${n(e.getMonth()+1)}-${n(e.getDate())} ${n(e.getHours())}:${n(e.getMinutes())}:${n(e.getSeconds())}`},b=t=>t?typeof t=="string"?t:JSON.stringify(t,null,2):"-",le=t=>{if(!t)return"-";try{return JSON.stringify(JSON.parse(t),null,2)}catch{return t}},V=t=>t.toLocaleString(),G=t=>t==null||t<=0?"-":t>=1e3?(t/1e3).toFixed(2)+"s":Math.round(t)+"ms",g=async t=>{try{await navigator.clipboard.writeText(t),N("已复制到剪贴板","success")}catch{const e=document.createElement("textarea");e.value=t,e.style.position="fixed",e.style.opacity="0",document.body.appendChild(e),e.select();try{document.execCommand("copy"),N("已复制到剪贴板","success")}catch{N("复制失败","error")}document.body.removeChild(e)}},H=t=>{const e=t.input_tokens||0,n=t.cache_read_tokens||0;return e<=0?"-":(n/e*100).toFixed(1)+"%"},F=v(!1),ae=async t=>{if(!(t._ipInfo||t._ipInfoError)){F.value=!0;try{const e=await J.get(`/api/logs/ipinfo?ip=${encodeURIComponent(t.ip)}`);e.error?t._ipInfoError=e.error:t._ipInfo=e}catch{t._ipInfoError="查询失败"}finally{F.value=!1}}},se=t=>[t.country,t.region,t.city].filter(Boolean).join(" · ")||"-",oe=async t=>{E.value=!0,s.value=null,P.value="info";try{const e=t.id?`?id=${t.id}`:`?_file=${encodeURIComponent(t._file)}`,n=await J.get("/api/logs/detail"+e);n&&n.detail?s.value={...n.detail,...n.meta}:s.value=n}catch(e){console.error(e)}},ne=De(()=>{const t=new Set;for(const e of K.suppliers)for(const n of e.models)t.add(n.modelName);for(const e of M.value)e.model&&t.add(e.model);return[...t].sort()}),X=t=>{const e=n=>String(n).padStart(2,"0");return`${t.getFullYear()}-${e(t.getMonth()+1)}-${e(t.getDate())}`},L=t=>{D.value=t;const e=new Date,n=new Date(e);if(t==="today")n.setDate(e.getDate());else if(t==="yesterday")n.setDate(e.getDate()-1),e.setDate(e.getDate()-1);else if(t==="beforeYesterday")n.setDate(e.getDate()-2),e.setDate(e.getDate()-2);else if(t==="last3days")n.setDate(e.getDate()-2);else if(t==="week"){const w=(e.getDay()+6)%7;n.setDate(e.getDate()-w)}else n.setDate(1);f.value=[X(n),X(e)],x()},re=t=>{t?L(t):(D.value="",f.value=null,x())},ue=t=>{D.value="",f.value=t||null,x()},B=async()=>{Y.value=!0;try{const t={page:String(U.value),pageSize:String(j.value),sort:"time",order:"desc"};f.value&&f.value.length===2&&(t.startDate=f.value[0],t.endDate=f.value[1]),S.value&&(t.status=S.value),I.value&&(t.pathGroup=I.value),q.value&&(t.model=q.value),z.value&&(t.keyword=z.value);const e=new URLSearchParams(t).toString(),n=await J.get("/api/logs?"+e);M.value=n.items||[],O.value=n.total||0}catch(t){console.error(t)}finally{Y.value=!1}},x=()=>{U.value=1,B()},ie=()=>{D.value="today",f.value=null,S.value="2xx",I.value="model",q.value="",z.value="",U.value=1,j.value=50,L("today")};return xe(()=>{K.loadProviders(),L("today")}),(t,e)=>{const n=p("el-option"),w=p("el-select"),de=p("el-date-picker"),pe=p("el-input"),m=p("el-button"),Q=p("el-card"),y=p("el-table-column"),W=p("el-tag"),ce=p("el-table"),_e=p("el-pagination"),_=p("el-descriptions-item"),ve=p("el-divider"),me=p("el-popover"),fe=p("el-descriptions"),be=p("el-alert"),h=p("el-tab-pane"),ge=p("el-tabs"),ye=p("el-dialog"),Z=Ve("loading");return d(),c("div",qe,[l(Q,{shadow:"never"},{default:a(()=>[i("div",ze,[l(w,{modelValue:D.value,"onUpdate:modelValue":e[0]||(e[0]=o=>D.value=o),placeholder:"时间范围",clearable:"",style:{width:"130px"},onChange:re},{default:a(()=>[l(n,{label:"今天",value:"today"}),l(n,{label:"昨天",value:"yesterday"}),l(n,{label:"前天",value:"beforeYesterday"}),l(n,{label:"近三天",value:"last3days"}),l(n,{label:"本周",value:"week"}),l(n,{label:"本月",value:"month"})]),_:1},8,["modelValue"]),l(de,{modelValue:f.value,"onUpdate:modelValue":e[1]||(e[1]=o=>f.value=o),type:"daterange","value-format":"YYYY-MM-DD","range-separator":"至","start-placeholder":"开始日期","end-placeholder":"结束日期",style:{width:"260px"},onChange:ue},null,8,["modelValue"]),l(w,{modelValue:S.value,"onUpdate:modelValue":e[2]||(e[2]=o=>S.value=o),placeholder:"全部状态",clearable:"",style:{width:"120px"},onChange:x},{default:a(()=>[l(n,{label:"成功 (2xx)",value:"2xx"}),l(n,{label:"3xx",value:"3xx"}),l(n,{label:"4xx",value:"4xx"}),l(n,{label:"5xx",value:"5xx"}),l(n,{label:"错误 (4xx+)",value:"err"})]),_:1},8,["modelValue"]),l(w,{modelValue:I.value,"onUpdate:modelValue":e[3]||(e[3]=o=>I.value=o),placeholder:"全部路径",clearable:"",style:{width:"130px"},onChange:x},{default:a(()=>[l(n,{label:"模型接口",value:"model"}),l(n,{label:"其他",value:"other"})]),_:1},8,["modelValue"]),l(w,{modelValue:q.value,"onUpdate:modelValue":e[4]||(e[4]=o=>q.value=o),placeholder:"全部模型",clearable:"",filterable:"",style:{width:"220px"},onChange:x},{default:a(()=>[(d(!0),c(T,null,we(ne.value,o=>(d(),C(n,{key:o,label:o,value:o},null,8,["label","value"]))),128))]),_:1},8,["modelValue"]),l(pe,{modelValue:z.value,"onUpdate:modelValue":e[5]||(e[5]=o=>z.value=o),class:"keyword-input",placeholder:"关键字搜索(请求/响应内容)",clearable:"",onKeyup:Ce(x,["enter"])},null,8,["modelValue"]),l(m,{type:"primary",class:"push-right",onClick:x},{default:a(()=>[...e[20]||(e[20]=[u("搜索",-1)])]),_:1}),l(m,{onClick:ie},{default:a(()=>[...e[21]||(e[21]=[u("重置",-1)])]),_:1})])]),_:1}),l(Q,{shadow:"never",class:"mt-16 table-card"},{default:a(()=>[i("div",Ue,[te((d(),C(ce,{data:M.value,border:"",stripe:"",height:"100%"},{default:a(()=>[l(y,{prop:"request_id",label:"请求ID","min-width":"140"},{default:a(({row:o})=>[o.request_id?(d(),c("span",{key:0,class:"request-id",title:"点击复制(可用于关联控制台日志与检索)",onClick:R=>g(o.request_id)},r(o.request_id),9,je)):(d(),c("span",Re,"-"))]),_:1}),l(y,{prop:"time",label:"时间",width:"180"},{default:a(({row:o})=>[u(r(A(o.time)),1)]),_:1}),l(y,{prop:"model",label:"模型","min-width":"180"}),l(y,{prop:"status",label:"状态",width:"70",align:"center"},{default:a(({row:o})=>[l(W,{type:o.status>=400?"danger":o.status>=300?"warning":"success"},{default:a(()=>[u(r(o.status),1)]),_:2},1032,["type"])]),_:1}),l(y,{label:"输入",width:"110",align:"center"},{default:a(({row:o})=>[u(r(V(o.input_tokens??0)),1)]),_:1}),l(y,{label:"输出",width:"110",align:"center"},{default:a(({row:o})=>[u(r(V(o.output_tokens??0)),1)]),_:1}),l(y,{label:"缓存读取",width:"90",align:"center"},{default:a(({row:o})=>[u(r(V(o.cache_read_tokens??0)),1)]),_:1}),l(y,{label:"缓存命中率",width:"105",align:"center"},{default:a(({row:o})=>[i("span",null,r(H(o)),1)]),_:1}),l(y,{label:"请求耗时",width:"90",align:"center"},{default:a(({row:o})=>[u(r(G(o.duration)),1)]),_:1}),l(y,{label:"详情",width:"70",align:"center"},{default:a(({row:o})=>[l(m,{link:"",type:"primary",onClick:R=>oe(o)},{default:a(()=>[...e[22]||(e[22]=[u("查看",-1)])]),_:1},8,["onClick"])]),_:1})]),_:1},8,["data"])),[[Z,Y.value]])]),i("div",Te,[l(_e,{"current-page":U.value,"onUpdate:currentPage":e[6]||(e[6]=o=>U.value=o),"page-size":j.value,"onUpdate:pageSize":e[7]||(e[7]=o=>j.value=o),total:O.value,"page-sizes":[20,50,100,200],layout:"total, sizes, prev, pager, next",onCurrentChange:B,onSizeChange:B},null,8,["current-page","page-size","total"])])]),_:1}),l(ye,{modelValue:E.value,"onUpdate:modelValue":e[19]||(e[19]=o=>E.value=o),title:"日志详情",width:"80%",top:"5vh","destroy-on-close":""},{default:a(()=>[s.value?(d(),c("div",Ne,[l(ge,{modelValue:P.value,"onUpdate:modelValue":e[18]||(e[18]=o=>P.value=o)},{default:a(()=>{var o,R,ee;return[l(h,{label:"基本信息",name:"info"},{default:a(()=>{var k;return[l(fe,{column:2,border:""},{default:a(()=>[l(_,{label:"时间"},{default:a(()=>[u(r(A(s.value.time)),1)]),_:1}),l(_,{label:"模型"},{default:a(()=>[u(r(s.value.model),1)]),_:1}),l(_,{label:"方法"},{default:a(()=>[u(r(s.value.method),1)]),_:1}),l(_,{label:"路径"},{default:a(()=>[u(r(s.value.path),1)]),_:1}),l(_,{label:"状态码"},{default:a(()=>[u(r(s.value.status),1)]),_:1}),l(_,{label:"流式"},{default:a(()=>[l(W,{type:s.value.stream?"warning":"info"},{default:a(()=>[u(r(s.value.stream?"是":"否"),1)]),_:1},8,["type"])]),_:1}),l(_,{label:"IP"},{default:a(()=>[s.value.ip?(d(),c("span",Me,[l(me,{placement:"right",width:"240",trigger:"click",onShow:e[8]||(e[8]=ke=>ae(s.value))},{reference:a(()=>[i("span",null,r(s.value.ip),1)]),default:a(()=>[te((d(),c("div",Ye,[s.value._ipInfo?(d(),c(T,{key:0},[i("div",Ee,[e[23]||(e[23]=i("span",{class:"ip-pop-label"},"归属地",-1)),i("span",null,r(se(s.value._ipInfo)),1)]),s.value._ipInfo.isp?(d(),c("div",Pe,[e[24]||(e[24]=i("span",{class:"ip-pop-label"},"运营商",-1)),i("span",null,r(s.value._ipInfo.isp),1)])):$("",!0)],64)):s.value._ipInfoError?(d(),c("div",Fe,r(s.value._ipInfoError),1)):(d(),c("span",Le,"查询中...")),l(ve,{style:{margin:"8px 0"}}),i("a",{class:"ip-pop-link",href:`https://m.ip138.com/iplookup.php?ip=${encodeURIComponent(s.value.ip)}`,target:"_blank"},"在 ip138 查看",8,Be)])),[[Z,F.value]])]),_:1})])):(d(),c("span",Je,"-"))]),_:1}),l(_,{label:"Session"},{default:a(()=>[u(r(s.value.session_id||"-"),1)]),_:1}),l(_,{label:"Request ID"},{default:a(()=>[s.value.request_id?(d(),c("span",{key:0,class:"request-id",title:"点击复制(可用于关联控制台日志与检索)",onClick:e[9]||(e[9]=ke=>g(s.value.request_id))},r(s.value.request_id),1)):(d(),c("span",Ke,"-"))]),_:1}),l(_,{label:"输入 Token(含缓存读取)"},{default:a(()=>[u(r(V(s.value.input_tokens??0)),1)]),_:1}),l(_,{label:"输出 Token"},{default:a(()=>[u(r(V(s.value.output_tokens??0)),1)]),_:1}),l(_,{label:"缓存读取"},{default:a(()=>[u(r(V(s.value.cache_read_tokens??0)),1)]),_:1}),l(_,{label:"请求耗时"},{default:a(()=>[u(r(G(s.value.duration)),1)]),_:1}),l(_,{label:"缓存命中率"},{default:a(()=>[u(r(H(s.value)),1)]),_:1}),s.value.error?(d(),C(_,{key:0,label:"错误",span:2},{default:a(()=>[i("span",Oe,r(s.value.error),1)]),_:1})):$("",!0)]),_:1}),(k=s.value.response)!=null&&k.truncated?(d(),C(be,{key:0,class:"mt-16",type:"warning",closable:!1,title:"内容超过单请求捕获上限,已截断"})):$("",!0)]}),_:1}),(o=s.value.response)!=null&&o._capture?(d(),c(T,{key:0},[l(h,{label:"客户端请求",name:"inbound"},{default:a(()=>[i("div",Ae,[l(m,{size:"small",class:"copy-btn",onClick:e[10]||(e[10]=k=>g(b(s.value.response.inbound_request)))},{default:a(()=>[...e[25]||(e[25]=[u(" 复制 ",-1)])]),_:1})]),i("pre",Ge,r(b(s.value.response.inbound_request)),1)]),_:1}),l(h,{label:"转发上游的请求",name:"upstreamReq"},{default:a(()=>[i("div",He,[l(m,{size:"small",class:"copy-btn",onClick:e[11]||(e[11]=k=>g(b(s.value.response.upstream_request)))},{default:a(()=>[...e[26]||(e[26]=[u(" 复制 ",-1)])]),_:1})]),i("pre",Xe,r(b(s.value.response.upstream_request)),1)]),_:1}),s.value.response.upstream_response_text?(d(),C(h,{key:0,label:"上游响应(原始)",name:"upstreamRes"},{default:a(()=>[i("div",Qe,[l(m,{size:"small",class:"copy-btn",onClick:e[12]||(e[12]=k=>g(s.value.response.upstream_response_text))},{default:a(()=>[...e[27]||(e[27]=[u(" 复制 ",-1)])]),_:1})]),i("pre",We,r(le(s.value.response.upstream_response_text)),1)]),_:1})):(R=s.value.response.upstream_stream_frames)!=null&&R.length?(d(),C(h,{key:1,label:`上游响应(原始流,${s.value.response.upstream_stream_frames.length} 帧)`,name:"upstreamStream"},{default:a(()=>[i("div",Ze,[l(m,{size:"small",class:"copy-btn",onClick:e[13]||(e[13]=k=>g(s.value.response.upstream_stream_frames.join(`
2
+
3
+ `)))},{default:a(()=>[...e[28]||(e[28]=[u(" 复制 ",-1)])]),_:1})]),i("pre",et,r(s.value.response.upstream_stream_frames.join(`
4
+
5
+ `)),1)]),_:1},8,["label"])):$("",!0),s.value.response.client_response!==void 0&&s.value.response.client_response!==null?(d(),C(h,{key:2,label:"回写客户端(转换后)",name:"clientRes"},{default:a(()=>[i("div",tt,[l(m,{size:"small",class:"copy-btn",onClick:e[14]||(e[14]=k=>g(b(s.value.response.client_response)))},{default:a(()=>[...e[29]||(e[29]=[u(" 复制 ",-1)])]),_:1})]),i("pre",lt,r(b(s.value.response.client_response)),1)]),_:1})):(ee=s.value.response.client_stream_frames)!=null&&ee.length?(d(),C(h,{key:3,label:`回写客户端(${s.value.response.client_stream_frames.length} 帧)`,name:"clientStream"},{default:a(()=>[i("div",at,[l(m,{size:"small",class:"copy-btn",onClick:e[15]||(e[15]=k=>g(s.value.response.client_stream_frames.join(`
6
+
7
+ `)))},{default:a(()=>[...e[30]||(e[30]=[u(" 复制 ",-1)])]),_:1})]),i("pre",st,r(s.value.response.client_stream_frames.join(`
8
+
9
+ `)),1)]),_:1},8,["label"])):$("",!0)],64)):(d(),c(T,{key:1},[l(h,{label:"请求体",name:"request"},{default:a(()=>[i("div",ot,[l(m,{size:"small",class:"copy-btn",onClick:e[16]||(e[16]=k=>g(b(s.value.request)))},{default:a(()=>[...e[31]||(e[31]=[u(" 复制 ",-1)])]),_:1})]),i("pre",nt,r(b(s.value.request)),1)]),_:1}),l(h,{label:"响应体",name:"response"},{default:a(()=>[i("div",rt,[l(m,{size:"small",class:"copy-btn",onClick:e[17]||(e[17]=k=>g(b(s.value.response)))},{default:a(()=>[...e[32]||(e[32]=[u(" 复制 ",-1)])]),_:1})]),i("pre",ut,r(b(s.value.response)),1)]),_:1})],64))]}),_:1},8,["modelValue"])])):$("",!0)]),_:1},8,["modelValue"])])}}}),ft=$e(it,[["__scopeId","data-v-f853bf75"]]);export{ft as default};
@@ -0,0 +1 @@
1
+ .logs-page[data-v-f853bf75]{height:calc(100vh - 124px);display:flex;flex-direction:column;overflow:hidden}.request-id[data-v-f853bf75]{cursor:pointer;font-family:ui-monospace,Consolas,monospace;display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.request-id[data-v-f853bf75]:hover{color:var(--el-color-primary)}.filter-bar[data-v-f853bf75]{display:flex;gap:12px;align-items:center;flex-wrap:nowrap}.filter-bar .keyword-input[data-v-f853bf75]{flex:1;min-width:160px}.filter-bar .push-right[data-v-f853bf75]{margin-left:auto}.filter-bar[data-v-f853bf75] .el-button+.el-button{margin-left:0}.mt-16[data-v-f853bf75]{margin-top:16px}.table-card[data-v-f853bf75]{flex:1;min-height:0;overflow:hidden}.table-card[data-v-f853bf75] .el-card__body{height:100%;display:flex;flex-direction:column}.table-wrap[data-v-f853bf75]{flex:1;min-height:0}.pagination-bar[data-v-f853bf75]{margin-top:16px;display:flex;justify-content:flex-end;flex-shrink:0}.ip-link[data-v-f853bf75]{color:#409eff;cursor:pointer}.ip-link[data-v-f853bf75]:hover{text-decoration:underline}.ip-pop[data-v-f853bf75]{font-size:13px;min-height:20px}.ip-pop-row[data-v-f853bf75]{display:flex;gap:8px;margin-bottom:4px}.ip-pop-label[data-v-f853bf75]{color:#909399;flex-shrink:0}.ip-pop-error[data-v-f853bf75]{color:#f56c6c}.ip-pop-link[data-v-f853bf75]{color:#409eff;font-size:13px}.session-text[data-v-f853bf75]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block;max-width:100%}.error-text[data-v-f853bf75]{color:#f56c6c}.pane-bar[data-v-f853bf75]{display:flex;justify-content:flex-end;margin-bottom:8px;flex-shrink:0}.json-block[data-v-f853bf75]{background:#f5f7fa;border:1px solid #e4e7ed;border-radius:4px;padding:12px;font-size:13px;line-height:1.5;overflow:auto;white-space:pre-wrap;word-break:break-all;flex:1;min-height:0}.detail-content[data-v-f853bf75]{height:78vh;display:flex;flex-direction:column;overflow:hidden}.detail-content[data-v-f853bf75] .el-tabs{flex:1;min-height:0;display:flex;flex-direction:column}.detail-content[data-v-f853bf75] .el-tabs__content{flex:1;min-height:0}.detail-content[data-v-f853bf75] .el-tab-pane{height:100%;display:flex;flex-direction:column;overflow:auto}.copy-btn[data-v-f853bf75]{margin-left:8px;padding:2px 10px;font-size:12px}
@@ -0,0 +1 @@
1
+ import{d as X,i as Y,a0 as q,n as H,c as D,a as n,w as l,l as i,r as c,m as J,o as w,b as d,Y as Q,e as o,t as r,j as W,$ as Z,_ as ee}from"./index-DFlUx18F.js";import{m as I}from"./useManagerApi-BDWJz5o6.js";import{u as te}from"./useToast-D7xlwSg6.js";const ae={class:"flex-col-gap"},le={class:"flex-between"},ne={class:"proc-name"},se={class:"proc-sub"},oe=["onClick"],re={key:1,class:"muted-sm"},ie={class:"desc-full"},de=X({__name:"ProcessView",setup(ue){const{toast:b}=te(),v=i(!0),g=i(!1),y=i([]),x=i(null),p=i(null),h=i(Date.now()),P={daemon:"pm2 守护进程",manager:"管理服务",main:"代理服务"},C=e=>P[e.role]??e.role,k={daemon:"pm2 守护进程:托管代理服务与管理服务的后台守护,进程崩溃时自动拉起;自身不监听端口、不处理业务请求,不允许远程操控(停止它将失去自动恢复能力)。",manager:"管理服务(ellm-proxy-manager):独立常驻的轻量服务,提供进程启停接口与代理开关接口;代理服务停止时保持存活,保证还能在本页远程拉起代理服务。",main:"代理服务(ellm-proxy):网关本体,转发 /v1/* 模型请求,同时承载管理台页面与 API;本页的重启操作即作用于该进程,重启期间网关与管理台 API 短暂不可用(代理服务停止时重启等效于启动)。"},A=e=>k[e.role],_=i(!1),T=i(""),$=i(""),E=e=>{T.value=`${C(e)}(${M(e)})`,$.value=k[e.role],_.value=!0},M=e=>e.role==="daemon"?"pm2":e.role==="manager"?"ellm-proxy-manager":"ellm-proxy",O=e=>e.status==="online",N=e=>e.status?e.status==="online"?"running":e.status==="errored"?"abnormal":"stopped":"stopped",V=e=>{switch(e.status){case"online":return{text:"运行中",type:"success"};case"stopped":return{text:"已停止",type:"info"};case"errored":return{text:"异常",type:"danger"};case"waiting restart":case"launching":return{text:"重启中",type:"warning"};default:return{text:e.status||"未知",type:"info"}}},R=e=>{if(!O(e)||!e.startedAt)return"-";let t=Math.max(0,Math.floor((h.value-e.startedAt)/1e3));const m=Math.floor(t/86400);t-=m*86400;const u=Math.floor(t/3600);t-=u*3600;const s=Math.floor(t/60);return t-=s*60,[m?`${m}天`:"",u?`${u}时`:"",s?`${s}分`:"",`${t}秒`].filter(Boolean).join(" ")},S=e=>e?e>=1024**3?(e/1024**3).toFixed(1)+"G":e>=1024**2?(e/1024**2).toFixed(1)+"M":Math.round(e/1024)+"K":"-",f=async e=>{e||(y.value.length?g.value=!0:v.value=!0);try{y.value=await I.processes(),h.value=Date.now(),x.value=null}catch(t){e||(x.value=t.message)}finally{e||(v.value=!1,g.value=!1)}};let B;const j=()=>{p.value||document.hidden||f(!0)};Y(()=>{f(!1),B=window.setInterval(j,5e3)}),q(()=>window.clearInterval(B));const z=async()=>{try{await Z.confirm("确定重启 ellm-proxy 代理服务?重启期间网关与管理台 API 将短暂不可用(代理服务已停止时重启等效于启动)。","重启确认",{confirmButtonText:"重启",cancelButtonText:"取消",type:"warning"})}catch{return}p.value="restart";try{await I.restart(),b("重启完成","success")}catch(e){b(e.message||"操作失败","error")}finally{p.value=null,await f(!0)}};return(e,t)=>{const m=c("el-tag"),u=c("el-button"),s=c("el-table-column"),F=c("el-empty"),L=c("el-table"),U=c("el-card"),G=c("el-dialog"),K=J("loading");return H((w(),D("div",ae,[n(U,{shadow:"never"},{header:l(()=>[d("div",le,[t[6]||(t[6]=d("span",{class:"fw-600"},"进程管理",-1)),d("div",null,[n(m,{type:"info",class:"mr-8"},{default:l(()=>[...t[4]||(t[4]=[o("刷新间隔 5s",-1)])]),_:1}),n(u,{loading:g.value,onClick:t[0]||(t[0]=a=>f(!1))},{default:l(()=>[...t[5]||(t[5]=[o("刷新",-1)])]),_:1},8,["loading"])])])]),default:l(()=>[n(L,{data:y.value,border:"",stripe:"",class:"full-width"},{empty:l(()=>[n(F,{description:x.value||"暂无进程数据"},{default:l(()=>[n(u,{type:"primary",onClick:t[1]||(t[1]=a=>f(!1))},{default:l(()=>[...t[8]||(t[8]=[o("重试",-1)])]),_:1})]),_:1},8,["description"])]),default:l(()=>[n(s,{label:"进程","min-width":"180"},{default:l(({row:a})=>[d("div",ne,[d("span",{class:Q(["status-dot",N(a)])},null,2),o(" "+r(C(a))+" ",1),d("span",se,r(M(a)),1)])]),_:1}),n(s,{label:"运行状态",width:"100",align:"center"},{default:l(({row:a})=>[n(m,{type:V(a).type,"disable-transitions":""},{default:l(()=>[o(r(V(a).text),1)]),_:2},1032,["type"])]),_:1}),n(s,{label:"PID",width:"90",align:"center"},{default:l(({row:a})=>[o(r(a.pid??"-"),1)]),_:1}),n(s,{label:"端口",width:"90",align:"center"},{default:l(({row:a})=>[o(r(a.port??"-"),1)]),_:1}),n(s,{label:"运行时长",width:"130",align:"center"},{default:l(({row:a})=>[o(r(R(a)),1)]),_:1}),n(s,{label:"地址","min-width":"170"},{default:l(({row:a})=>[o(r(a.address??"-"),1)]),_:1}),n(s,{label:"内存",width:"90",align:"center"},{default:l(({row:a})=>[o(r(S(a.memory)),1)]),_:1}),n(s,{label:"描述","min-width":"220"},{default:l(({row:a})=>[d("span",{class:"desc-cell",title:"点击查看详情",onClick:ce=>E(a)},r(A(a)),9,oe)]),_:1}),n(s,{label:"操作",width:"90",align:"center"},{default:l(({row:a})=>[a.role==="main"?(w(),W(u,{key:0,type:"primary",size:"small",loading:p.value==="restart",disabled:p.value!==null,onClick:z},{default:l(()=>[...t[7]||(t[7]=[o(" 重启 ",-1)])]),_:1},8,["loading","disabled"])):(w(),D("span",re,"不可操作"))]),_:1})]),_:1},8,["data"]),t[9]||(t[9]=d("div",{class:"muted-sm"}," 进程控制由管理服务(manager)执行,仅代理服务(ellm-proxy)可重启(已停止时重启等效于启动): 重启期间网关与管理台 API 短暂不可用。 pm2 守护进程与管理服务不允许远程操控(停止它们将失去远程恢复能力)。 ",-1))]),_:1}),n(G,{modelValue:_.value,"onUpdate:modelValue":t[3]||(t[3]=a=>_.value=a),title:T.value,width:"520px"},{footer:l(()=>[n(u,{type:"primary",onClick:t[2]||(t[2]=a=>_.value=!1)},{default:l(()=>[...t[10]||(t[10]=[o("知道了",-1)])]),_:1})]),default:l(()=>[d("p",ie,r($.value),1)]),_:1},8,["modelValue","title"])])),[[K,v.value]])}}}),_e=ee(de,[["__scopeId","data-v-1404bf7a"]]);export{_e as default};
@@ -0,0 +1 @@
1
+ @charset "UTF-8";.proc-name[data-v-1404bf7a]{display:flex;align-items:center;gap:8px;font-weight:600}.proc-name .proc-sub[data-v-1404bf7a]{font-weight:400;color:var(--el-text-color-secondary);font-size:12px}.status-dot[data-v-1404bf7a]{display:inline-block;width:8px;height:8px;border-radius:50%}.status-dot.running[data-v-1404bf7a]{background:var(--el-color-success);box-shadow:0 0 4px var(--el-color-success)}.status-dot.stopped[data-v-1404bf7a]{background:var(--el-color-info)}.status-dot.abnormal[data-v-1404bf7a]{background:var(--el-color-danger)}.desc-cell[data-v-1404bf7a]{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:pointer}.desc-full[data-v-1404bf7a]{margin:0;line-height:1.8}.mr-8[data-v-1404bf7a]{margin-right:8px}
@@ -0,0 +1 @@
1
+ .dialog-body[data-v-3a42733f]{max-height:80vh;overflow-y:auto;overflow-x:hidden;padding-bottom:100px}.test-ok-text[data-v-3a42733f]{font-size:12px;color:#16a34a;max-width:140px;display:inline-block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:middle}.test-fail-text[data-v-3a42733f]{font-size:12px;color:#dc2626;max-width:140px;display:inline-block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:middle}.model-select-wrap[data-v-3a42733f]{width:400px;flex-shrink:0}.flex-gap-8.full-width[data-v-3a42733f]{width:100%}.key-input[data-v-3a42733f]{flex:1;min-width:0}.key-test-btn[data-v-3a42733f]{flex-shrink:0}.test-result-link[data-v-3a42733f]{font-size:12px}.detail-body[data-v-3a42733f]{max-height:70vh;overflow-y:auto}.detail-head[data-v-3a42733f]{display:flex;align-items:center;gap:12px;margin-bottom:12px}.detail-name[data-v-3a42733f]{font-weight:600;font-size:14px}.detail-pre[data-v-3a42733f]{background:#f5f7fa;border:1px solid #e4e7ed;border-radius:6px;padding:12px;font-size:12px;line-height:1.6;color:#303133;max-height:300px;overflow:auto;margin:0;white-space:pre-wrap;word-break:break-all}.ghost[data-v-7afb29d5]{opacity:.4}.provider-grid[data-v-7afb29d5]{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}.provider-card[data-v-7afb29d5]{min-width:0}.provider-card[data-v-7afb29d5] .el-card__body{padding:14px 16px}.provider-card.is-expanded[data-v-7afb29d5]{grid-column:1/-1}.card-header[data-v-7afb29d5]{display:flex;justify-content:space-between;align-items:center;min-width:0}.card-title[data-v-7afb29d5]{min-width:0}.card-title .alias-title[data-v-7afb29d5]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.drag-grip[data-v-7afb29d5]{flex-shrink:0;color:#c0c4cc;cursor:grab;padding:4px}.drag-grip[data-v-7afb29d5]:hover{color:#909399}.card-tags[data-v-7afb29d5]{margin-top:8px}.card-actions[data-v-7afb29d5]{margin-top:12px;padding-top:10px;border-top:1px solid #f0f2f5}.line-ellipsis[data-v-7afb29d5]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.rotate-90[data-v-7afb29d5]{transform:rotate(90deg)}.error-tag[data-v-7afb29d5]{max-width:100px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cursor-pointer[data-v-7afb29d5]{cursor:pointer}