ellm-proxy 0.0.1 → 0.0.3
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.
- package/dist/web-ui/assets/{DashboardView-CQdlgirS.js → DashboardView-B71Dy3px.js} +1 -1
- package/dist/web-ui/assets/{LayoutView-BaY4Pz9W.js → LayoutView-C03GVB3X.js} +1 -1
- package/dist/web-ui/assets/LoginView-XTF5dHvd.js +1 -0
- package/dist/web-ui/assets/LogsView-B2ctrpv4.js +9 -0
- package/dist/web-ui/assets/LogsView-Dl50XQhG.css +1 -0
- package/dist/web-ui/assets/ProcessView-DmbVABQV.js +1 -0
- package/dist/web-ui/assets/ProvidersView-Dpr8MI-p.css +1 -0
- package/dist/web-ui/assets/ProvidersView-mb0KU16n.js +31 -0
- package/dist/web-ui/assets/SettingsView-BgLQVC2H.js +1 -0
- package/dist/web-ui/assets/{config-DNn5cjYY.js → config-BRaXq0LL.js} +1 -1
- package/dist/web-ui/assets/{index-DFlUx18F.js → index-dPeMJjrz.js} +2 -2
- package/dist/web-ui/assets/{providers-CGEmOxne.js → providers-Dmz2p4Wd.js} +1 -1
- package/dist/web-ui/assets/{useApi-BRPinb_2.js → useApi-C1Ngt_o5.js} +1 -1
- package/dist/web-ui/assets/{useToast-D7xlwSg6.js → useToast-B3quFZyv.js} +1 -1
- package/dist/web-ui/index.html +1 -1
- package/package.json +41 -40
- package/dist/web-ui/assets/LoginView-CPvz1Ylx.js +0 -1
- package/dist/web-ui/assets/LogsView-B-bIj36j.js +0 -9
- package/dist/web-ui/assets/LogsView-Wl3_adHm.css +0 -1
- package/dist/web-ui/assets/ProcessView-0x_ShSwV.js +0 -1
- package/dist/web-ui/assets/ProvidersView-BxAxDRFH.css +0 -1
- package/dist/web-ui/assets/ProvidersView-GQENcvJS.js +0 -31
- package/dist/web-ui/assets/SettingsView-BrAiaskZ.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{A as T}from"./useApi-
|
|
1
|
+
import{A as T}from"./useApi-C1Ngt_o5.js";import{u as H}from"./providers-Dmz2p4Wd.js";import{u as J}from"./useToast-B3quFZyv.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-dPeMJjrz.js";import"./config-BRaXq0LL.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};
|
|
@@ -1 +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-
|
|
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-dPeMJjrz.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,W as x,a0 as f,j as C,k as L,e as B,l as i,g as N,_ as I}from"./index-dPeMJjrz.js";import{A as K}from"./useApi-C1Ngt_o5.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,9 @@
|
|
|
1
|
+
import{d as G,r as b,m as H,o as _,j as q,w as a,c as g,a as e,e as i,t as u,u as m,n as Q,F as P,b as d,k as j,l as y,_ as X,i as pe,p as ce,a0 as _e,f as ve}from"./index-dPeMJjrz.js";import{A}from"./useApi-C1Ngt_o5.js";import{u as me}from"./providers-Dmz2p4Wd.js";import{u as Z}from"./useToast-B3quFZyv.js";import"./config-BRaXq0LL.js";async function ee(p){try{if(navigator.clipboard&&window.isSecureContext)return await navigator.clipboard.writeText(p),!0}catch{}try{const f=document.createElement("textarea");f.value=p,f.style.position="fixed",f.style.opacity="0",document.body.appendChild(f),f.select();const k=document.execCommand("copy");return document.body.removeChild(f),k}catch{return!1}}const te=p=>{if(!p)return"-";const f=new Date(p),k=w=>String(w).padStart(2,"0");return`${f.getFullYear()}-${k(f.getMonth()+1)}-${k(f.getDate())} ${k(f.getHours())}:${k(f.getMinutes())}:${k(f.getSeconds())}`},V=p=>p?typeof p=="string"?p:JSON.stringify(p,null,2):"-",fe=p=>{if(!p)return"-";try{return JSON.stringify(JSON.parse(p),null,2)}catch{return p}},T=p=>p.toLocaleString(),le=p=>p==null||p<=0?"-":p>=1e3?(p/1e3).toFixed(2)+"s":Math.round(p)+"ms",ae=p=>{const f=p.input_tokens||0,k=p.cache_read_tokens||0;return f<=0?"-":(k/f*100).toFixed(1)+"%"},be={key:0,class:"detail-content"},ge={key:0,class:"ip-link"},ye={class:"ip-pop"},ke={class:"ip-pop-row"},he={key:0,class:"ip-pop-row"},xe={key:1,class:"ip-pop-error"},we={key:2},De=["href"],Ce={key:1},$e={key:1},Ve={class:"error-text"},Se={class:"pane-bar"},Ie={class:"json-block"},qe={class:"pane-bar"},ze={class:"json-block"},Re={class:"pane-bar"},Ue={class:"json-block"},je={class:"pane-bar"},Te={class:"json-block"},Ne={class:"pane-bar"},Le={class:"json-block"},Me={class:"pane-bar"},Ye={class:"json-block"},Ee={class:"pane-bar"},Pe={class:"json-block"},Fe={class:"pane-bar"},Be={class:"json-block"},Je=G({__name:"LogDetailDialog",setup(p,{expose:f}){const{toast:k}=Z(),w=y(!1),t=y(null),S=y("info"),I=y(!1);f({open:async v=>{w.value=!0,t.value=null,S.value="info";try{const l=v.id?`?id=${v.id}`:`?_file=${encodeURIComponent(v._file)}`,c=await A.get("/api/logs/detail"+l);c&&c.detail?t.value={...c.detail,...c.meta}:t.value=c}catch(l){console.error(l)}}});const h=async v=>{await ee(v)?k("已复制到剪贴板","success"):k("复制失败","error")},N=async v=>{if(!(v._ipInfo||v._ipInfoError)){I.value=!0;try{const l=await A.get(`/api/logs/ipinfo?ip=${encodeURIComponent(v.ip)}`);l.error?v._ipInfoError=l.error:v._ipInfo=l}catch{v._ipInfoError="查询失败"}finally{I.value=!1}}},M=v=>[v.country,v.region,v.city].filter(Boolean).join(" · ")||"-";return(v,l)=>{const c=b("el-descriptions-item"),Y=b("el-tag"),F=b("el-divider"),B=b("el-popover"),J=b("el-descriptions"),E=b("el-alert"),x=b("el-tab-pane"),D=b("el-button"),K=b("el-tabs"),L=b("el-dialog"),C=H("loading");return _(),q(L,{modelValue:w.value,"onUpdate:modelValue":l[11]||(l[11]=z=>w.value=z),title:"日志详情",width:"80%",top:"5vh","destroy-on-close":""},{default:a(()=>[t.value?(_(),g("div",be,[e(K,{modelValue:S.value,"onUpdate:modelValue":l[10]||(l[10]=z=>S.value=z)},{default:a(()=>{var z,r,s;return[e(x,{label:"基本信息",name:"info"},{default:a(()=>{var n;return[e(J,{column:2,border:""},{default:a(()=>[e(c,{label:"时间"},{default:a(()=>[i(u(m(te)(t.value.time)),1)]),_:1}),e(c,{label:"模型"},{default:a(()=>[i(u(t.value.model),1)]),_:1}),e(c,{label:"方法"},{default:a(()=>[i(u(t.value.method),1)]),_:1}),e(c,{label:"路径"},{default:a(()=>[i(u(t.value.path),1)]),_:1}),e(c,{label:"状态码"},{default:a(()=>[i(u(t.value.status),1)]),_:1}),e(c,{label:"流式"},{default:a(()=>[e(Y,{type:t.value.stream?"warning":"info"},{default:a(()=>[i(u(t.value.stream?"是":"否"),1)]),_:1},8,["type"])]),_:1}),e(c,{label:"IP"},{default:a(()=>[t.value.ip?(_(),g("span",ge,[e(B,{placement:"right",width:"240",trigger:"click",onShow:l[0]||(l[0]=R=>N(t.value))},{reference:a(()=>[d("span",null,u(t.value.ip),1)]),default:a(()=>[Q((_(),g("div",ye,[t.value._ipInfo?(_(),g(P,{key:0},[d("div",ke,[l[12]||(l[12]=d("span",{class:"ip-pop-label"},"归属地",-1)),d("span",null,u(M(t.value._ipInfo)),1)]),t.value._ipInfo.isp?(_(),g("div",he,[l[13]||(l[13]=d("span",{class:"ip-pop-label"},"运营商",-1)),d("span",null,u(t.value._ipInfo.isp),1)])):j("",!0)],64)):t.value._ipInfoError?(_(),g("div",xe,u(t.value._ipInfoError),1)):(_(),g("span",we,"查询中...")),e(F,{style:{margin:"8px 0"}}),d("a",{class:"ip-pop-link",href:`https://m.ip138.com/iplookup.php?ip=${encodeURIComponent(t.value.ip)}`,target:"_blank"},"在 ip138 查看",8,De)])),[[C,I.value]])]),_:1})])):(_(),g("span",Ce,"-"))]),_:1}),e(c,{label:"Session"},{default:a(()=>[i(u(t.value.session_id||"-"),1)]),_:1}),e(c,{label:"Request ID"},{default:a(()=>[t.value.request_id?(_(),g("span",{key:0,class:"request-id",title:"点击复制(可用于关联控制台日志与检索)",onClick:l[1]||(l[1]=R=>h(t.value.request_id))},u(t.value.request_id),1)):(_(),g("span",$e,"-"))]),_:1}),e(c,{label:"输入 Token(含缓存读取)"},{default:a(()=>[i(u(m(T)(t.value.input_tokens??0)),1)]),_:1}),e(c,{label:"输出 Token"},{default:a(()=>[i(u(m(T)(t.value.output_tokens??0)),1)]),_:1}),e(c,{label:"缓存读取"},{default:a(()=>[i(u(m(T)(t.value.cache_read_tokens??0)),1)]),_:1}),e(c,{label:"请求耗时"},{default:a(()=>[i(u(m(le)(t.value.duration)),1)]),_:1}),e(c,{label:"缓存命中率"},{default:a(()=>[i(u(m(ae)(t.value)),1)]),_:1}),t.value.error?(_(),q(c,{key:0,label:"错误",span:2},{default:a(()=>[d("span",Ve,u(t.value.error),1)]),_:1})):j("",!0)]),_:1}),(n=t.value.response)!=null&&n.truncated?(_(),q(E,{key:0,class:"mt-16",type:"warning",closable:!1,title:"内容超过单请求捕获上限,已截断"})):j("",!0)]}),_:1}),(z=t.value.response)!=null&&z._capture?(_(),g(P,{key:0},[e(x,{label:"客户端请求",name:"inbound"},{default:a(()=>[d("div",Se,[e(D,{size:"small",class:"copy-btn",onClick:l[2]||(l[2]=n=>h(m(V)(t.value.response.inbound_request)))},{default:a(()=>[...l[14]||(l[14]=[i(" 复制 ",-1)])]),_:1})]),d("pre",Ie,u(m(V)(t.value.response.inbound_request)),1)]),_:1}),e(x,{label:"转发上游的请求",name:"upstreamReq"},{default:a(()=>[d("div",qe,[e(D,{size:"small",class:"copy-btn",onClick:l[3]||(l[3]=n=>h(m(V)(t.value.response.upstream_request)))},{default:a(()=>[...l[15]||(l[15]=[i(" 复制 ",-1)])]),_:1})]),d("pre",ze,u(m(V)(t.value.response.upstream_request)),1)]),_:1}),t.value.response.upstream_response_text?(_(),q(x,{key:0,label:"上游响应(原始)",name:"upstreamRes"},{default:a(()=>[d("div",Re,[e(D,{size:"small",class:"copy-btn",onClick:l[4]||(l[4]=n=>h(t.value.response.upstream_response_text))},{default:a(()=>[...l[16]||(l[16]=[i(" 复制 ",-1)])]),_:1})]),d("pre",Ue,u(m(fe)(t.value.response.upstream_response_text)),1)]),_:1})):(r=t.value.response.upstream_stream_frames)!=null&&r.length?(_(),q(x,{key:1,label:`上游响应(原始流,${t.value.response.upstream_stream_frames.length} 帧)`,name:"upstreamStream"},{default:a(()=>[d("div",je,[e(D,{size:"small",class:"copy-btn",onClick:l[5]||(l[5]=n=>h(t.value.response.upstream_stream_frames.join(`
|
|
2
|
+
|
|
3
|
+
`)))},{default:a(()=>[...l[17]||(l[17]=[i(" 复制 ",-1)])]),_:1})]),d("pre",Te,u(t.value.response.upstream_stream_frames.join(`
|
|
4
|
+
|
|
5
|
+
`)),1)]),_:1},8,["label"])):j("",!0),t.value.response.client_response!==void 0&&t.value.response.client_response!==null?(_(),q(x,{key:2,label:"回写客户端(转换后)",name:"clientRes"},{default:a(()=>[d("div",Ne,[e(D,{size:"small",class:"copy-btn",onClick:l[6]||(l[6]=n=>h(m(V)(t.value.response.client_response)))},{default:a(()=>[...l[18]||(l[18]=[i(" 复制 ",-1)])]),_:1})]),d("pre",Le,u(m(V)(t.value.response.client_response)),1)]),_:1})):(s=t.value.response.client_stream_frames)!=null&&s.length?(_(),q(x,{key:3,label:`回写客户端(${t.value.response.client_stream_frames.length} 帧)`,name:"clientStream"},{default:a(()=>[d("div",Me,[e(D,{size:"small",class:"copy-btn",onClick:l[7]||(l[7]=n=>h(t.value.response.client_stream_frames.join(`
|
|
6
|
+
|
|
7
|
+
`)))},{default:a(()=>[...l[19]||(l[19]=[i(" 复制 ",-1)])]),_:1})]),d("pre",Ye,u(t.value.response.client_stream_frames.join(`
|
|
8
|
+
|
|
9
|
+
`)),1)]),_:1},8,["label"])):j("",!0)],64)):(_(),g(P,{key:1},[e(x,{label:"请求体",name:"request"},{default:a(()=>[d("div",Ee,[e(D,{size:"small",class:"copy-btn",onClick:l[8]||(l[8]=n=>h(m(V)(t.value.request)))},{default:a(()=>[...l[20]||(l[20]=[i(" 复制 ",-1)])]),_:1})]),d("pre",Pe,u(m(V)(t.value.request)),1)]),_:1}),e(x,{label:"响应体",name:"response"},{default:a(()=>[d("div",Fe,[e(D,{size:"small",class:"copy-btn",onClick:l[9]||(l[9]=n=>h(m(V)(t.value.response)))},{default:a(()=>[...l[21]||(l[21]=[i(" 复制 ",-1)])]),_:1})]),d("pre",Be,u(m(V)(t.value.response)),1)]),_:1})],64))]}),_:1},8,["modelValue"])])):j("",!0)]),_:1},8,["modelValue"])}}}),Ke=X(Je,[["__scopeId","data-v-f9373118"]]),Oe={class:"logs-page"},Ae={class:"filter-bar"},We={class:"table-wrap"},Ge=["onClick"],He={key:1},Qe={class:"pagination-bar"},Xe=G({__name:"LogsView",setup(p){const f=me(),{toast:k}=Z(),w=y(""),t=y(null),S=y("2xx"),I=y("model"),U=y(""),h=y(""),N=y([]),M=y(0),v=y(1),l=y(50),c=y(!1),Y=y(null),F=r=>{var s;(s=Y.value)==null||s.open(r)},B=ve(()=>{const r=new Set;for(const s of f.suppliers)for(const n of s.models)r.add(n.modelName);for(const s of N.value)s.model&&r.add(s.model);return[...r].sort()}),J=async r=>{await ee(r)?k("已复制到剪贴板","success"):k("复制失败","error")},E=r=>{const s=n=>String(n).padStart(2,"0");return`${r.getFullYear()}-${s(r.getMonth()+1)}-${s(r.getDate())}`},x=r=>{w.value=r;const s=new Date,n=new Date(s);if(r==="today")n.setDate(s.getDate());else if(r==="yesterday")n.setDate(s.getDate()-1),s.setDate(s.getDate()-1);else if(r==="beforeYesterday")n.setDate(s.getDate()-2),s.setDate(s.getDate()-2);else if(r==="last3days")n.setDate(s.getDate()-2);else if(r==="week"){const R=(s.getDay()+6)%7;n.setDate(s.getDate()-R)}else n.setDate(1);t.value=[E(n),E(s)],C()},D=r=>{r?x(r):(w.value="",t.value=null,C())},K=r=>{w.value="",t.value=r||null,C()},L=async()=>{c.value=!0;try{const r={page:String(v.value),pageSize:String(l.value),sort:"time",order:"desc"};t.value&&t.value.length===2&&(r.startDate=t.value[0],r.endDate=t.value[1]),S.value&&(r.status=S.value),I.value&&(r.pathGroup=I.value),U.value&&(r.model=U.value),h.value&&(r.keyword=h.value);const s=new URLSearchParams(r).toString(),n=await A.get("/api/logs?"+s);N.value=n.items||[],M.value=n.total||0}catch(r){console.error(r)}finally{c.value=!1}},C=()=>{v.value=1,L()},z=()=>{w.value="today",t.value=null,S.value="2xx",I.value="model",U.value="",h.value="",v.value=1,l.value=50,x("today")};return pe(()=>{f.loadProviders(),x("today")}),(r,s)=>{const n=b("el-option"),R=b("el-select"),se=b("el-date-picker"),oe=b("el-input"),O=b("el-button"),W=b("el-card"),$=b("el-table-column"),ne=b("el-tag"),re=b("el-table"),ue=b("el-pagination"),ie=H("loading");return _(),g("div",Oe,[e(W,{shadow:"never"},{default:a(()=>[d("div",Ae,[e(R,{modelValue:w.value,"onUpdate:modelValue":s[0]||(s[0]=o=>w.value=o),placeholder:"时间范围",clearable:"",style:{width:"130px"},onChange:D},{default:a(()=>[e(n,{label:"今天",value:"today"}),e(n,{label:"昨天",value:"yesterday"}),e(n,{label:"前天",value:"beforeYesterday"}),e(n,{label:"近三天",value:"last3days"}),e(n,{label:"本周",value:"week"}),e(n,{label:"本月",value:"month"})]),_:1},8,["modelValue"]),e(se,{modelValue:t.value,"onUpdate:modelValue":s[1]||(s[1]=o=>t.value=o),type:"daterange","value-format":"YYYY-MM-DD","range-separator":"至","start-placeholder":"开始日期","end-placeholder":"结束日期",style:{width:"260px"},onChange:K},null,8,["modelValue"]),e(R,{modelValue:S.value,"onUpdate:modelValue":s[2]||(s[2]=o=>S.value=o),placeholder:"全部状态",clearable:"",style:{width:"120px"},onChange:C},{default:a(()=>[e(n,{label:"成功 (2xx)",value:"2xx"}),e(n,{label:"3xx",value:"3xx"}),e(n,{label:"4xx",value:"4xx"}),e(n,{label:"5xx",value:"5xx"}),e(n,{label:"错误 (4xx+)",value:"err"})]),_:1},8,["modelValue"]),e(R,{modelValue:I.value,"onUpdate:modelValue":s[3]||(s[3]=o=>I.value=o),placeholder:"全部路径",clearable:"",style:{width:"130px"},onChange:C},{default:a(()=>[e(n,{label:"模型接口",value:"model"}),e(n,{label:"其他",value:"other"})]),_:1},8,["modelValue"]),e(R,{modelValue:U.value,"onUpdate:modelValue":s[4]||(s[4]=o=>U.value=o),placeholder:"全部模型",clearable:"",filterable:"",style:{width:"220px"},onChange:C},{default:a(()=>[(_(!0),g(P,null,ce(B.value,o=>(_(),q(n,{key:o,label:o,value:o},null,8,["label","value"]))),128))]),_:1},8,["modelValue"]),e(oe,{modelValue:h.value,"onUpdate:modelValue":s[5]||(s[5]=o=>h.value=o),class:"keyword-input",placeholder:"关键字搜索(请求/响应内容)",clearable:"",onKeyup:_e(C,["enter"])},null,8,["modelValue"]),e(O,{type:"primary",class:"push-right",onClick:C},{default:a(()=>[...s[8]||(s[8]=[i("搜索",-1)])]),_:1}),e(O,{onClick:z},{default:a(()=>[...s[9]||(s[9]=[i("重置",-1)])]),_:1})])]),_:1}),e(W,{shadow:"never",class:"mt-16 table-card"},{default:a(()=>[d("div",We,[Q((_(),q(re,{data:N.value,border:"",stripe:"",height:"100%"},{default:a(()=>[e($,{prop:"request_id",label:"请求ID","min-width":"140"},{default:a(({row:o})=>[o.request_id?(_(),g("span",{key:0,class:"request-id",title:"点击复制(可用于关联控制台日志与检索)",onClick:de=>J(o.request_id)},u(o.request_id),9,Ge)):(_(),g("span",He,"-"))]),_:1}),e($,{prop:"time",label:"时间",width:"180"},{default:a(({row:o})=>[i(u(m(te)(o.time)),1)]),_:1}),e($,{prop:"model",label:"模型","min-width":"180"}),e($,{prop:"status",label:"状态",width:"70",align:"center"},{default:a(({row:o})=>[e(ne,{type:o.status>=400?"danger":o.status>=300?"warning":"success"},{default:a(()=>[i(u(o.status),1)]),_:2},1032,["type"])]),_:1}),e($,{label:"输入",width:"110",align:"center"},{default:a(({row:o})=>[i(u(m(T)(o.input_tokens??0)),1)]),_:1}),e($,{label:"输出",width:"110",align:"center"},{default:a(({row:o})=>[i(u(m(T)(o.output_tokens??0)),1)]),_:1}),e($,{label:"缓存读取",width:"90",align:"center"},{default:a(({row:o})=>[i(u(m(T)(o.cache_read_tokens??0)),1)]),_:1}),e($,{label:"缓存命中率",width:"105",align:"center"},{default:a(({row:o})=>[d("span",null,u(m(ae)(o)),1)]),_:1}),e($,{label:"请求耗时",width:"90",align:"center"},{default:a(({row:o})=>[i(u(m(le)(o.duration)),1)]),_:1}),e($,{label:"详情",width:"70",align:"center"},{default:a(({row:o})=>[e(O,{link:"",type:"primary",onClick:de=>F(o)},{default:a(()=>[...s[10]||(s[10]=[i("查看",-1)])]),_:1},8,["onClick"])]),_:1})]),_:1},8,["data"])),[[ie,c.value]])]),d("div",Qe,[e(ue,{"current-page":v.value,"onUpdate:currentPage":s[6]||(s[6]=o=>v.value=o),"page-size":l.value,"onUpdate:pageSize":s[7]||(s[7]=o=>l.value=o),total:M.value,"page-sizes":[20,50,100,200],layout:"total, sizes, prev, pager, next",onCurrentChange:L,onSizeChange:L},null,8,["current-page","page-size","total"])])]),_:1}),e(Ke,{ref_key:"logDetailRef",ref:Y},null,512)])}}}),st=X(Xe,[["__scopeId","data-v-7f41f12a"]]);export{st as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.ip-link[data-v-f9373118]{color:#409eff;cursor:pointer}.ip-link[data-v-f9373118]:hover{text-decoration:underline}.ip-pop[data-v-f9373118]{font-size:13px;min-height:20px}.ip-pop-row[data-v-f9373118]{display:flex;gap:8px;margin-bottom:4px}.ip-pop-label[data-v-f9373118]{color:#909399;flex-shrink:0}.ip-pop-error[data-v-f9373118]{color:#f56c6c}.ip-pop-link[data-v-f9373118]{color:#409eff;font-size:13px}.error-text[data-v-f9373118]{color:#f56c6c}.pane-bar[data-v-f9373118]{display:flex;justify-content:flex-end;margin-bottom:8px;flex-shrink:0}.json-block[data-v-f9373118]{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-f9373118]{height:78vh;display:flex;flex-direction:column;overflow:hidden}.detail-content[data-v-f9373118] .el-tabs{flex:1;min-height:0;display:flex;flex-direction:column}.detail-content[data-v-f9373118] .el-tabs__content{flex:1;min-height:0}.detail-content[data-v-f9373118] .el-tab-pane{height:100%;display:flex;flex-direction:column;overflow:auto}.copy-btn[data-v-f9373118]{margin-left:8px;padding:2px 10px;font-size:12px}.logs-page[data-v-7f41f12a]{height:calc(100vh - 124px);display:flex;flex-direction:column;overflow:hidden}.request-id[data-v-7f41f12a]{cursor:pointer;font-family:ui-monospace,Consolas,monospace;display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.request-id[data-v-7f41f12a]:hover{color:var(--el-color-primary)}.filter-bar[data-v-7f41f12a]{display:flex;gap:12px;align-items:center;flex-wrap:nowrap}.filter-bar .keyword-input[data-v-7f41f12a]{flex:1;min-width:160px}.filter-bar .push-right[data-v-7f41f12a]{margin-left:auto}.filter-bar[data-v-7f41f12a] .el-button+.el-button{margin-left:0}.mt-16[data-v-7f41f12a]{margin-top:16px}.table-card[data-v-7f41f12a]{flex:1;min-height:0;overflow:hidden}.table-card[data-v-7f41f12a] .el-card__body{height:100%;display:flex;flex-direction:column}.table-wrap[data-v-7f41f12a]{flex:1;min-height:0}.pagination-bar[data-v-7f41f12a]{margin-top:16px;display:flex;justify-content:flex-end;flex-shrink:0}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{d as K,i as q,a6 as H,n as J,c as D,a as n,w as l,l as i,r as c,m as Q,o as w,b as d,X as W,e as o,t as r,j as Y,a1 as Z,_ as ee}from"./index-dPeMJjrz.js";import{m as I}from"./useManagerApi-BDWJz5o6.js";import{u as te}from"./useToast-B3quFZyv.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=K({__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(""),M=i(""),E=e=>{T.value=`${C(e)}(${$(e)})`,M.value=k[e.role],_.value=!0},$=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)};q(()=>{f(!1),B=window.setInterval(j,5e3)}),H(()=>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"),X=c("el-dialog"),G=Q("loading");return J((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:W(["status-dot",N(a)])},null,2),o(" "+r(C(a))+" ",1),d("span",se,r($(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(),Y(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(X,{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(M.value),1)]),_:1},8,["modelValue","title"])])),[[G,v.value]])}}}),_e=ee(de,[["__scopeId","data-v-1404bf7a"]]);export{_e as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@charset "UTF-8";.provider-card[data-v-e96d8595]{min-width:0;cursor:grab;transition:box-shadow .2s,border-color .2s}.provider-card[data-v-e96d8595] .el-card__header{padding-bottom:10px}.provider-card[data-v-e96d8595] .el-card__body{padding-top:12px}.provider-card[data-v-e96d8595]:hover{border-color:#b1d2ff;box-shadow:0 4px 16px #3b82f61a}.provider-card.dragging[data-v-e96d8595]{cursor:grabbing}.card-header[data-v-e96d8595]{display:flex;justify-content:space-between;align-items:center;gap:12px}.card-title[data-v-e96d8595]{display:flex;align-items:center;gap:8px;min-width:0}.provider-name[data-v-e96d8595]{font-size:15px;font-weight:600;color:#303133;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.card-actions[data-v-e96d8595]{display:flex;gap:0;flex-shrink:0;cursor:auto}.card-meta[data-v-e96d8595]{display:flex;flex-direction:column;gap:6px;padding:0}.meta-row[data-v-e96d8595]{display:flex;align-items:center;gap:10px;font-size:13px;line-height:20px}.meta-label[data-v-e96d8595]{flex-shrink:0;width:60px;color:#909399}.meta-value[data-v-e96d8595]{flex:1;min-width:0;color:#606266;font-family:ui-monospace,SF Mono,Consolas,monospace;font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.model-tags[data-v-e96d8595]{display:flex;flex-wrap:wrap;gap:6px;margin-top:10px}.model-tag[data-v-e96d8595]{display:inline-flex;align-items:center;max-width:100%;padding:3px 10px;font-size:12px;line-height:18px;color:#606266;background:#f5f7fa;border:1px solid transparent;border-radius:999px;cursor:pointer;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;transition:color .15s,background .15s}.model-tag[data-v-e96d8595]:hover{color:#3b82f6;background:#eff6ff}.model-tag.ok[data-v-e96d8595]{color:#16a34a;background:#f0fdf4}.model-tag.error[data-v-e96d8595]{color:#dc2626;background:#fef2f2}.badge-mark[data-v-e96d8595]{margin-left:3px}.card-footer[data-v-e96d8595]{display:flex;justify-content:space-between;align-items:center;margin-top:14px;padding-top:12px;border-top:1px solid #f0f2f5}.model-count[data-v-e96d8595]{font-size:13px;color:#16a34a;font-weight:500;cursor:pointer;border-radius:4px}.model-count[data-v-e96d8595]:hover{text-decoration:underline}.drag-hint-icon[data-v-e96d8595]{flex-shrink:0;color:#c0c4cc;cursor:grab}.drag-hint-icon[data-v-e96d8595]:hover{color:#909399}.detail-body[data-v-08dfa117]{max-height:70vh;overflow-y:auto}.detail-head[data-v-08dfa117]{display:flex;align-items:center;gap:12px;margin-bottom:12px}.detail-name[data-v-08dfa117]{font-weight:600;font-size:14px}.detail-pre[data-v-08dfa117]{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}.protocol-help .help-intro[data-v-2248b61a]{font-size:13px;color:#606266;margin-bottom:14px}.protocol-help .help-item[data-v-2248b61a]{margin-bottom:12px}.protocol-help .help-title[data-v-2248b61a]{font-weight:600;font-size:14px;color:#303133;margin-bottom:4px;display:flex;align-items:center;gap:8px}.protocol-help .help-item p[data-v-2248b61a]{font-size:13px;color:#606266;line-height:1.7;margin:0}.protocol-help .help-table-note[data-v-2248b61a]{margin-bottom:8px}.protocol-help .help-table[data-v-2248b61a]{width:100%}.model-select-wrap[data-v-e64a6809]{width:400px;flex-shrink:0}.test-result-link[data-v-e64a6809]{font-size:12px}.col-help[data-v-e64a6809]{margin-right:4px}.col-help-icon[data-v-e64a6809]{color:#c0c4cc;cursor:help;vertical-align:-2px}.col-help-icon[data-v-e64a6809]:hover{color:#909399}.dialog-body[data-v-097689eb]{max-height:80vh;overflow-y:auto;overflow-x:hidden;padding-bottom:100px}.test-ok-text[data-v-097689eb]{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-097689eb]{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-097689eb]{width:400px;flex-shrink:0}.protocol-hint[data-v-097689eb]{line-height:1.8}.label-action-btn[data-v-097689eb]{margin-left:8px;font-size:12px;height:auto}.label-inline-toggle[data-v-097689eb]{margin-left:8px;display:inline-flex;align-items:center;gap:6px}.test-result-link[data-v-097689eb]{font-size:12px}.col-help[data-v-097689eb]{margin-right:4px}.col-help-icon[data-v-097689eb]{color:#c0c4cc;cursor:help;vertical-align:-2px}.col-help-icon[data-v-097689eb]:hover{color:#909399}.models-dialog-body[data-v-a79f8c7b]{display:flex;flex-direction:column;height:calc(94vh - 100px)}.models-dialog-body .models-table[data-v-a79f8c7b]{flex:1;min-height:0}.error-tag[data-v-a79f8c7b]{max-width:100px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ghost[data-v-e250879b]{opacity:.4}.provider-grid[data-v-e250879b]{display:grid;grid-template-columns:repeat(2,1fr);gap:16px}@media(max-width:992px){.provider-grid[data-v-e250879b]{grid-template-columns:1fr}}
|