ezllm 0.0.27 → 0.0.29

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 (50) hide show
  1. package/README.md +20 -0
  2. package/bin/ezllm.js +3 -0
  3. package/dist/cli.js +39 -24512
  4. package/dist/daemon.js +25 -0
  5. package/dist/server.js +202 -41858
  6. package/dist/web-ui/assets/DashboardView-5ujMkgws.js +1 -0
  7. package/dist/web-ui/assets/DashboardView-CZy4j9_m.css +1 -0
  8. package/dist/web-ui/assets/{LayoutView-CkUPhcPQ.js → LayoutView-C-x9LlJB.js} +1 -1
  9. package/dist/web-ui/assets/{LoginView-CzfNjlvz.js → LoginView-Dgr8SbIP.js} +1 -1
  10. package/dist/web-ui/assets/LogsView-BZCLikyn.css +1 -0
  11. package/dist/web-ui/assets/LogsView-DME2_Vhc.js +9 -0
  12. package/dist/web-ui/assets/ProvidersView-B__welRn.css +1 -0
  13. package/dist/web-ui/assets/ProvidersView-CcTikZnF.js +14 -0
  14. package/dist/web-ui/assets/ServiceStatusView-C4N2XQ_w.js +2 -0
  15. package/dist/web-ui/assets/ServiceStatusView-CwXVx_18.css +1 -0
  16. package/dist/web-ui/assets/SettingsView-0a5M4naA.css +1 -0
  17. package/dist/web-ui/assets/SettingsView-Cck08cQ7.js +1 -0
  18. package/dist/web-ui/assets/{SetupView-DpD5okpe.js → SetupView-DbiSodBy.js} +1 -1
  19. package/dist/web-ui/assets/clipboard-JYWVdyjq.js +1 -0
  20. package/dist/web-ui/assets/{config-C6eC0CWg.js → config-DcxW5IHq.js} +1 -1
  21. package/dist/web-ui/assets/format-OBe8peUU.js +1 -0
  22. package/dist/web-ui/assets/{index-DRdS47Vm.js → index-Bxg3HL8C.js} +1 -1
  23. package/dist/web-ui/assets/{index-BNieAChJ.css → index-DmoPyGio.css} +1 -1
  24. package/dist/web-ui/assets/{message-D5w4qfHY.js → message-BfNKbnQ4.js} +1 -1
  25. package/dist/web-ui/assets/providers-DBzc7lYt.js +1 -0
  26. package/dist/web-ui/assets/{router-DjNMXIJT.js → router-BbFt1lFf.js} +2 -2
  27. package/dist/web-ui/assets/useCopyToast-SPDhUFJc.js +1 -0
  28. package/dist/web-ui/assets/{useManagerApi-COiXXii9.js → useManagerApi-B65ileRW.js} +1 -1
  29. package/dist/web-ui/assets/useToast-D0wtmNdH.js +1 -0
  30. package/dist/web-ui/assets/{version-quqH5oMc.js → version-DegxwpkG.js} +1 -1
  31. package/dist/web-ui/index.html +4 -4
  32. package/hook.example.cjs +176 -0
  33. package/package.json +8 -2
  34. package/bin/ellm-proxy.js +0 -3
  35. package/dist/web-ui/assets/DashboardView-DOtZtkUN.css +0 -1
  36. package/dist/web-ui/assets/DashboardView-DTF4RzgZ.js +0 -1
  37. package/dist/web-ui/assets/LogsView-BYA6dgGO.js +0 -9
  38. package/dist/web-ui/assets/LogsView-D7xJOKSv.css +0 -1
  39. package/dist/web-ui/assets/ProvidersView-DYUaYhO-.js +0 -14
  40. package/dist/web-ui/assets/ProvidersView-ufXnqFGi.css +0 -1
  41. package/dist/web-ui/assets/ServiceStatusView-DXbn--55.css +0 -1
  42. package/dist/web-ui/assets/ServiceStatusView-c0jJPnb2.js +0 -2
  43. package/dist/web-ui/assets/SettingsView-BY2QIyXL.css +0 -1
  44. package/dist/web-ui/assets/SettingsView-BZsuqac5.js +0 -1
  45. package/dist/web-ui/assets/clipboard-CorU9X8e.js +0 -1
  46. package/dist/web-ui/assets/format-DrbP4_cY.js +0 -1
  47. package/dist/web-ui/assets/providers-BM4fqFJi.js +0 -1
  48. package/dist/web-ui/assets/useLoadError-qYTOtUW0.js +0 -1
  49. package/dist/web-ui/assets/useToast-DKg4wBxA.js +0 -1
  50. package/docs/DESIGN.md +0 -98
@@ -0,0 +1 @@
1
+ import{$ as e,A as t,Et as n,F as r,Ft as i,Gt as a,H as o,I as s,L as c,M as l,N as u,Q as d,T as ee,a as f,at as p,d as m,gt as te,ht as h,j as g,k as _,ot as ne,pn as v,rt as y,tt as b,yt as x}from"./router-BbFt1lFf.js";import{t as S}from"./useManagerApi-B65ileRW.js";import{t as C}from"./providers-DBzc7lYt.js";import{n as w,t as T}from"./useCopyToast-SPDhUFJc.js";import{t as E}from"./latestRequest-Dk21IcrH.js";import{a as re,d as D,o as O,r as ie,t as ae}from"./format-OBe8peUU.js";x();function oe(e){let{loadError:t,run:r}=w(e),i=n(null);function a(e){let t=async()=>{let n=await r(e);return n?i.value===t&&(i.value=null):i.value=t,n};return t}function o(e){r(i.value??e)}return{loadError:t,track:a,retry:o}}o(),x(),a();var se={class:`flex-col-gap`},ce=[`onClick`],le={key:1},ue={class:`flex-between`},de={class:`flex-gap-8`},fe={class:`usage-grid`},pe={class:`usage-cell`},k={class:`usage-num orange`},A={class:`usage-cell`},j={class:`usage-num blue`},M={class:`usage-cell`},N={class:`usage-num green`},P={class:`usage-cell`},F={class:`usage-num purple`},I={class:`usage-cell`},L={class:`usage-num purple`},R={key:0,class:`usage-cell`},z={class:`usage-num gray`},B={key:0},me=[`onClick`],V=m(c({__name:`DashboardView`,setup(a){let o=C(),{copyWithToast:c}=T(),{loadError:m,track:x,retry:w}=oe(),V=n(null),H=n(!0),U=n(``),W=n(`today`),he=D(`today`),G=n([...he]),ge=e=>{let t=D(e);t&&(W.value=e,G.value=t,K())},_e=e=>{e?ge(e):(W.value=``,G.value=null,K())},ve=()=>{W.value=``,K()},ye=_(()=>{let e=new Set;for(let t of o.suppliers)for(let n of t.models)e.add(n.modelName);if(V.value?.models)for(let t of V.value.models)e.add(t.model);return[...e].sort()}),be=E(),K=x(async()=>{let e=be();H.value=!0;try{let t=new URLSearchParams;U.value&&t.set(`model`,U.value),G.value?.length===2&&(t.set(`startDate`,G.value[0]),t.set(`endDate`,G.value[1]));let n=t.toString(),r=await f.get(`/api/usage${n?`?${n}`:``}`);if(!e())return;V.value=r}catch(t){if(!e())return;throw t}finally{e()&&(H.value=!1)}}),xe=E(),q=x(async()=>{let e=xe();try{await Promise.all([K(),o.loadProviders()])}catch(t){if(!e())return;throw t}finally{e()&&(H.value=!1)}}),Se=()=>w(q),Ce=window.location.hostname,we=Number(window.location.port)||null,Te=_(()=>ae({protocol:window.location.protocol,hostname:Ce,port:we})),J=n(null),Y=n(null),X=n(!1),Z=n(Date.now()),Q=async()=>{let[e,t]=await Promise.allSettled([S.processes(),f.get(`/api/status`)]);if(e.status===`fulfilled`&&(J.value=e.value[0]??null),t.status===`rejected`){Y.value=null,X.value=!0;return}let n=t.value?.masterKeySet;Y.value=typeof n==`boolean`?n:null,X.value=typeof n!=`boolean`,Z.value=Date.now()},Ee=e=>{if(!e.startedAt)return`-`;let t=Math.max(0,Math.floor((Z.value-e.startedAt)/1e3)),n=Math.floor(t/86400);t-=n*86400;let r=Math.floor(t/3600);t-=r*3600;let i=Math.floor(t/60);return t-=i*60,[n?`${n}天`:``,r?`${r}时`:``,i?`${i}分`:``,`${t}秒`].filter(Boolean).join(` `)},De=_(()=>X.value?`获取失败,请刷新重试`:Y.value===null?`-`:Y.value?`已设置(/v1 需 Bearer 认证)`:`未设置(/v1 免鉴权)`),Oe=_(()=>{let e=[{key:`API 地址`,value:Te.value},{key:`API 密钥`,value:De.value},{key:`供应商数量`,value:String(o.providerCount)},{key:`模型总数`,value:String(o.totalModelCount)}],t=J.value;return t&&e.push({key:`运行状态`,value:`运行中`},{key:`PID`,value:String(t.pid??`-`)},{key:`运行时长`,value:Ee(t)},{key:`内存占用`,value:re(t.memory)}),e}),$;return d(()=>{q(),Q(),$=window.setInterval(Q,1e4)}),e(()=>window.clearInterval($)),(e,n)=>{let a=p(`el-button`),o=p(`el-alert`),d=p(`el-table-column`),f=p(`el-table`),_=p(`el-card`),x=p(`el-option`),S=p(`el-select`),C=p(`el-date-picker`),w=p(`el-tag`),T=p(`el-empty`),E=ne(`loading`);return b(),u(`div`,se,[i(m)?(b(),g(o,{key:0,title:i(m),type:`error`,"show-icon":``,closable:!1},{default:h(()=>[s(a,{size:`small`,onClick:Se},{default:h(()=>[...n[3]||=[r(`重试`,-1)]]),_:1})]),_:1},8,[`title`])):l(``,!0),s(_,{shadow:`never`},{header:h(()=>[...n[4]||=[t(`span`,{class:`fw-600`},`服务信息`,-1)]]),default:h(()=>[te((b(),g(f,{border:``,data:Oe.value,"show-header":!1,class:`full-width`},{default:h(()=>[s(d,{prop:`key`,width:`160`}),s(d,null,{default:h(({row:e})=>[e.key===`API 地址`?(b(),u(`span`,{key:0,class:`copy-text`,onClick:t=>i(c)(e.value)},v(e.value),9,ce)):(b(),u(`span`,le,v(e.value),1))]),_:1})]),_:1},8,[`data`])),[[E,H.value]])]),_:1}),V.value?(b(),g(_,{key:1,shadow:`never`},{header:h(()=>[t(`div`,ue,[n[6]||=t(`span`,{class:`fw-600`},`用量统计`,-1),t(`div`,de,[s(S,{modelValue:U.value,"onUpdate:modelValue":n[0]||=e=>U.value=e,placeholder:`全部模型`,clearable:``,filterable:``,style:{width:`220px`},onChange:i(K)},{default:h(()=>[(b(!0),u(ee,null,y(ye.value,e=>(b(),g(x,{key:e,label:e,value:e},null,8,[`label`,`value`]))),128))]),_:1},8,[`modelValue`,`onChange`]),s(S,{modelValue:W.value,"onUpdate:modelValue":n[1]||=e=>W.value=e,placeholder:`时间范围`,clearable:``,style:{width:`130px`},onChange:_e},{default:h(()=>[s(x,{label:`今天`,value:`today`}),s(x,{label:`昨天`,value:`yesterday`}),s(x,{label:`前天`,value:`beforeYesterday`}),s(x,{label:`近三天`,value:`last3days`}),s(x,{label:`本周`,value:`week`}),s(x,{label:`本月`,value:`month`})]),_:1},8,[`modelValue`]),s(C,{modelValue:G.value,"onUpdate:modelValue":n[2]||=e=>G.value=e,type:`daterange`,"range-separator":`至`,"start-placeholder":`开始日期`,"end-placeholder":`结束日期`,"value-format":`YYYY-MM-DD`,style:{width:`260px`},onChange:ve},null,8,[`modelValue`]),s(a,{loading:H.value,onClick:i(K)},{default:h(()=>[...n[5]||=[r(`刷新`,-1)]]),_:1},8,[`loading`,`onClick`])]),s(w,{type:`info`},{default:h(()=>[r(v(V.value.total.calls)+` 次调用`,1)]),_:1})])]),default:h(()=>[t(`div`,fe,[t(`div`,pe,[t(`div`,k,v(i(O)(V.value.total.total_tokens)),1),n[7]||=t(`div`,{class:`usage-label`},`总 Token`,-1)]),t(`div`,A,[t(`div`,j,v(i(O)(V.value.total.input_tokens)),1),n[8]||=t(`div`,{class:`usage-label`},`输入 Token`,-1)]),t(`div`,M,[t(`div`,N,v(i(O)(V.value.total.output_tokens)),1),n[9]||=t(`div`,{class:`usage-label`},`输出 Token`,-1)]),t(`div`,P,[t(`div`,F,v(i(O)(V.value.total.cache_read_tokens)),1),n[10]||=t(`div`,{class:`usage-label`},`缓存读取`,-1)]),t(`div`,I,[t(`div`,L,v(V.value.total.cache_rate==null?`-`:V.value.total.cache_rate+`%`),1),n[11]||=t(`div`,{class:`usage-label`},`缓存命中率`,-1)]),V.value.logStorageMB==null?l(``,!0):(b(),u(`div`,R,[t(`div`,z,v(V.value.logStorageMB),1),n[12]||=t(`div`,{class:`usage-label`},`数据目录占用 (MB)`,-1)]))]),V.value.models?.length?(b(),u(`div`,B,[n[13]||=t(`div`,{class:`section-title`},`模型用量`,-1),s(f,{data:V.value.models,border:``,stripe:``,class:`full-width`},{default:h(()=>[s(d,{prop:`model`,label:`模型`,"min-width":`200`},{default:h(({row:e})=>[t(`span`,{class:`copy-text`,onClick:t=>i(c)(e.model)},v(e.model),9,me)]),_:1}),s(d,{label:`调用次数`,width:`90`,align:`center`},{default:h(({row:e})=>[r(v(e.calls),1)]),_:1}),s(d,{label:`平均耗时`,width:`110`,align:`center`},{default:h(({row:e})=>[r(v(i(ie)(e.avg_duration)),1)]),_:1}),s(d,{label:`总 Token`,width:`140`,align:`center`},{default:h(({row:e})=>[r(v(i(O)(e.total_tokens)),1)]),_:1}),s(d,{label:`输入 Token`,width:`140`,align:`center`},{default:h(({row:e})=>[r(v(i(O)(e.input_tokens)),1)]),_:1}),s(d,{label:`输出 Token`,width:`140`,align:`center`},{default:h(({row:e})=>[r(v(i(O)(e.output_tokens)),1)]),_:1}),s(d,{label:`缓存读取`,width:`130`,align:`center`},{default:h(({row:e})=>[r(v(i(O)(e.cache_read_tokens)),1)]),_:1}),s(d,{label:`缓存命中率`,width:`110`,align:`center`},{default:h(({row:e})=>[r(v(e.cache_rate==null?`-`:e.cache_rate+`%`),1)]),_:1})]),_:1},8,[`data`])])):(b(),g(T,{key:1,description:`暂无调用记录`,"image-size":60}))]),_:1})):l(``,!0)])}}}),[[`__scopeId`,`data-v-71c466d8`]]);export{V as default};
@@ -0,0 +1 @@
1
+ .copy-text[data-v-71c466d8]{color:#409eff;cursor:pointer}.copy-text[data-v-71c466d8]:hover{text-decoration:underline}
@@ -1 +1 @@
1
- import{A as e,Et as t,F as n,Ft as r,Gt as i,H as a,I as o,L as s,M as c,N as l,Q as u,T as d,at as f,d as p,gt as m,ht as h,i as g,j as _,k as v,l as y,n as b,o as x,ot as S,pn as C,tt as w,u as T,ut as E,yt as D}from"./router-DjNMXIJT.js";import{m as O}from"./message-D5w4qfHY.js";import{t as k}from"./index-DRdS47Vm.js";import{n as A,t as j}from"./useManagerApi-COiXXii9.js";import{t as M}from"./useToast-DKg4wBxA.js";import{n as N,t as P}from"./version-quqH5oMc.js";a(),i(),D();var F={class:`check-body`},I={class:`version-row`},L={class:`version-row`},R={class:`muted-sm`},z=p(s({__name:`UpdateCheckDialog`,props:{modelValue:{type:Boolean,default:!1},modelModifiers:{}},emits:[`update:modelValue`],setup(i){let{toast:a}=M(),s=E(i,`modelValue`),u=t(!1),p=t(!1),g=t(null),v=t(null),y=t(null),b=t(null),x=null,T=()=>x?.abort(),D=e=>new Date(e).toLocaleString(),O=e=>`https://www.npmjs.com/package/ezllm/v/${e}`,z=async()=>{v.value=null,y.value=null,b.value=null,u.value=!0;try{let e=await j.update();if(g.value=e,!e.supported)return;v.value=await j.checkUpdate()}catch(e){y.value=e.message||`检查更新失败`}finally{u.value=!1}},B=async()=>{if(!v.value)return;let e=v.value.latestVersion;try{await k.confirm(`确定更新到 ${e}?服务会自动重启并在启动时安装新版本:重启期间管理台短暂不可访问(预计 1~3 分钟),更新完成后自动恢复。`,`更新确认`,{confirmButtonText:`立即更新`,cancelButtonText:`取消`,type:`warning`})}catch{return}p.value=!0;let t=new AbortController;x=t;try{let e=await j.update(),n=await j.applyUpdate();if(!n.updated){b.value=`已是最新版本 ${n.toVersion}`,v.value={hasUpdate:!1,currentVersion:n.toVersion,latestVersion:n.toVersion,checkedAt:Date.now()};return}b.value=`正在重启服务并安装 ${n.toVersion}(预计 1~3 分钟):重启期间管理台短暂不可访问,完成后这里会自动显示结果。`;let r=await A({targetVersion:n.toVersion,sinceAppliedAt:e.lastApply?.appliedAt??null,signal:t.signal});if(t.signal.aborted)return;r&&!r.error?(a(`已更新到 ${r.toVersion}`,`success`),b.value=`已更新 ${r.fromVersion} → ${r.toVersion},服务已在新版本上运行。`,v.value={hasUpdate:!1,currentVersion:r.toVersion,latestVersion:r.toVersion,checkedAt:Date.now()}):r?.error?(a(`更新失败:${r.error}`,`error`),b.value=N(r.error)):b.value=`更新仍在进行(安装耗时较长):稍后刷新页面查看最新状态。`}catch(e){a(e.message||`更新失败`,`error`)}finally{p.value=!1,x===t&&(x=null)}};return(t,i)=>{let a=f(`el-alert`),x=f(`el-tag`),E=f(`el-link`),k=f(`el-button`),A=f(`el-dialog`),j=S(`loading`);return w(),_(A,{modelValue:s.value,"onUpdate:modelValue":i[2]||=e=>s.value=e,title:`检查更新`,width:`420px`,"close-on-click-modal":!p.value,onOpen:z,onClose:T},{footer:h(()=>[v.value?.hasUpdate?(w(),_(k,{key:0,type:`primary`,loading:p.value,onClick:B},{default:h(()=>[n(C(p.value?`更新中...`:`立即更新`),1)]),_:1},8,[`loading`])):c(``,!0),o(k,{onClick:i[1]||=e=>s.value=!1},{default:h(()=>[...i[11]||=[n(`关闭`,-1)]]),_:1})]),default:h(()=>[m((w(),l(`div`,F,[g.value&&!g.value.supported?(w(),_(a,{key:0,title:`当前为源码运行模式,更新功能不可用`,type:`info`,"show-icon":``,closable:!1},{default:h(()=>[...i[3]||=[e(`div`,{class:`alert-hint`},[n(` 仅 npm 全局安装的包支持自动更新。本地开发用仓库里的 `),e(`code`,null,`pnpm upgrade:local`),n(` 更新运行中的服务。 `)],-1)]]),_:1})):v.value?(w(),l(d,{key:1},[e(`div`,I,[i[5]||=e(`span`,{class:`version-label`},`当前版本`,-1),o(x,{size:`small`},{default:h(()=>[n(C(v.value.currentVersion),1)]),_:1}),r(P)(v.value.currentVersion)?(w(),_(x,{key:0,type:`warning`,size:`small`,"disable-transitions":``},{default:h(()=>[...i[4]||=[n(`本地构建`,-1)]]),_:1})):c(``,!0)]),e(`div`,L,[i[6]||=e(`span`,{class:`version-label`},`最新版本`,-1),o(x,{type:v.value.hasUpdate?`warning`:`success`,size:`small`,"disable-transitions":``},{default:h(()=>[n(C(v.value.latestVersion),1)]),_:1},8,[`type`])]),o(a,{title:v.value.hasUpdate?`发现新版本 ${v.value.latestVersion}(当前 ${v.value.currentVersion})`:`已是最新版本 ${v.value.currentVersion}`,type:v.value.hasUpdate?`warning`:`success`,"show-icon":``,closable:!1,class:`mt-12`},null,8,[`title`,`type`]),e(`div`,R,[n(` 检查时间:`+C(D(v.value.checkedAt))+` `,1),v.value.hasUpdate?(w(),l(d,{key:0},[i[8]||=n(` · `,-1),o(E,{type:`primary`,href:O(v.value.latestVersion),target:`_blank`},{default:h(()=>[...i[7]||=[n(`在 npm 查看该版本`,-1)]]),_:1},8,[`href`])],64)):c(``,!0)])],64)):!u.value&&y.value?(w(),_(a,{key:2,title:y.value,type:`error`,"show-icon":``,closable:!1},{default:h(()=>[i[10]||=e(`div`,{class:`alert-hint`},` 检查需访问 npm 源,可能是网络不可达或版本号解析失败;稍后重试即可。 `,-1),o(k,{size:`small`,class:`mt-8`,onClick:z},{default:h(()=>[...i[9]||=[n(`重试`,-1)]]),_:1})]),_:1},8,[`title`])):c(``,!0)])),[[j,u.value]]),b.value?(w(),_(a,{key:0,title:b.value,type:`info`,"show-icon":``,class:`mt-12`,onClose:i[0]||=e=>b.value=null},null,8,[`title`])):c(``,!0)]),_:1},8,[`modelValue`,`close-on-click-modal`])}}}),[[`__scopeId`,`data-v-ea1efda8`]]);a(),i(),D();var B={class:`layout`},V={class:`header-nav`},H={class:`header-actions`},U={class:`main`},W={class:`page`},G=s({__name:`LayoutView`,setup(i){let a=y(),s=T(),d=t(!1);u(()=>{b()});let p=v(()=>g.value?.authRequired!==!1),m=v(()=>{if(!p.value)return`免登录`;let e=localStorage.getItem(`webui_token`)||``;try{return atob(e).split(`:`)[0]||`用户`}catch{return`用户`}}),S=v(()=>{let e=a.path;return e===`/`?`/`:e.startsWith(`/providers`)?`/providers`:e.startsWith(`/service-status`)?`/service-status`:e.startsWith(`/settings`)?`/settings`:e.startsWith(`/logs`)?`/logs`:`/`}),E=()=>{x(),s.push(`/login`)},D=e=>{e===`logout`&&E(),e===`check-update`&&(d.value=!0)};return(t,i)=>{let a=f(`el-menu-item`),s=f(`el-menu`),u=f(`el-icon`),g=f(`el-button`),v=f(`el-dropdown-item`),y=f(`el-dropdown-menu`),b=f(`el-dropdown`),x=f(`el-header`),T=f(`router-view`);return w(),l(`div`,B,[o(x,{class:`header`},{default:h(()=>[i[8]||=e(`h1`,null,[e(`span`,null,`E`),n(`ZLLM`)],-1),e(`nav`,V,[o(s,{mode:`horizontal`,"default-active":S.value,ellipsis:!1,router:``,class:`header-menu`},{default:h(()=>[o(a,{index:`/`},{default:h(()=>[...i[1]||=[n(`首页`,-1)]]),_:1}),o(a,{index:`/providers`},{default:h(()=>[...i[2]||=[n(`供应商管理`,-1)]]),_:1}),o(a,{index:`/service-status`},{default:h(()=>[...i[3]||=[n(`服务状态`,-1)]]),_:1}),o(a,{index:`/logs`},{default:h(()=>[...i[4]||=[n(`日志`,-1)]]),_:1}),o(a,{index:`/settings`},{default:h(()=>[...i[5]||=[n(`设置`,-1)]]),_:1})]),_:1},8,[`default-active`])]),e(`div`,H,[o(b,{trigger:`click`,onCommand:D,class:`ml-12`},{dropdown:h(()=>[o(y,null,{default:h(()=>[o(v,{command:`check-update`},{default:h(()=>[...i[6]||=[n(`检查更新`,-1)]]),_:1}),p.value?(w(),_(v,{key:0,command:`logout`,divided:``},{default:h(()=>[...i[7]||=[n(`退出`,-1)]]),_:1})):c(``,!0)]),_:1})]),default:h(()=>[o(g,null,{default:h(()=>[n(C(m.value)+` `,1),o(u,{class:`el-icon--right`},{default:h(()=>[o(r(O))]),_:1})]),_:1})]),_:1})])]),_:1}),e(`main`,U,[e(`div`,W,[o(T)])]),o(z,{modelValue:d.value,"onUpdate:modelValue":i[0]||=e=>d.value=e},null,8,[`modelValue`])])}}});export{G as default};
1
+ import{A as e,Et as t,F as n,Ft as r,Gt as i,H as a,I as o,L as s,M as c,N as l,Q as u,T as d,at as f,d as p,gt as m,ht as h,i as g,j as _,k as v,l as y,n as b,o as x,ot as S,pn as C,tt as w,u as T,ut as E,yt as D}from"./router-BbFt1lFf.js";import{m as O}from"./message-BfNKbnQ4.js";import{t as k}from"./index-Bxg3HL8C.js";import{n as A,t as j}from"./useManagerApi-B65ileRW.js";import{t as M}from"./useToast-D0wtmNdH.js";import{n as N,t as P}from"./version-DegxwpkG.js";a(),i(),D();var F={class:`check-body`},I={class:`version-row`},L={class:`version-row`},R={class:`muted-sm`},z=p(s({__name:`UpdateCheckDialog`,props:{modelValue:{type:Boolean,default:!1},modelModifiers:{}},emits:[`update:modelValue`],setup(i){let{toast:a}=M(),s=E(i,`modelValue`),u=t(!1),p=t(!1),g=t(null),v=t(null),y=t(null),b=t(null),x=null,T=()=>x?.abort(),D=e=>new Date(e).toLocaleString(),O=e=>`https://www.npmjs.com/package/ezllm/v/${e}`,z=async()=>{v.value=null,y.value=null,b.value=null,u.value=!0;try{let e=await j.update();if(g.value=e,!e.supported)return;v.value=await j.checkUpdate()}catch(e){y.value=e.message||`检查更新失败`}finally{u.value=!1}},B=async()=>{if(!v.value)return;let e=v.value.latestVersion;try{await k.confirm(`确定更新到 ${e}?服务会自动重启并在启动时安装新版本:重启期间管理台短暂不可访问(预计 1~3 分钟),更新完成后自动恢复。`,`更新确认`,{confirmButtonText:`立即更新`,cancelButtonText:`取消`,type:`warning`})}catch{return}p.value=!0;let t=new AbortController;x=t;try{let e=await j.update(),n=await j.applyUpdate();if(!n.updated){b.value=`已是最新版本 ${n.toVersion}`,v.value={hasUpdate:!1,currentVersion:n.toVersion,latestVersion:n.toVersion,checkedAt:Date.now()};return}b.value=`正在重启服务并安装 ${n.toVersion}(预计 1~3 分钟):重启期间管理台短暂不可访问,完成后这里会自动显示结果。`;let r=await A({targetVersion:n.toVersion,sinceAppliedAt:e.lastApply?.appliedAt??null,signal:t.signal});if(t.signal.aborted)return;r&&!r.error?(a(`已更新到 ${r.toVersion}`,`success`),b.value=`已更新 ${r.fromVersion} → ${r.toVersion},服务已在新版本上运行。`,v.value={hasUpdate:!1,currentVersion:r.toVersion,latestVersion:r.toVersion,checkedAt:Date.now()}):r?.error?(a(`更新失败:${r.error}`,`error`),b.value=N(r.error)):b.value=`更新仍在进行(安装耗时较长):稍后刷新页面查看最新状态。`}catch(e){a(e.message||`更新失败`,`error`)}finally{p.value=!1,x===t&&(x=null)}};return(t,i)=>{let a=f(`el-alert`),x=f(`el-tag`),E=f(`el-link`),k=f(`el-button`),A=f(`el-dialog`),j=S(`loading`);return w(),_(A,{modelValue:s.value,"onUpdate:modelValue":i[2]||=e=>s.value=e,title:`检查更新`,width:`420px`,"close-on-click-modal":!p.value,onOpen:z,onClose:T},{footer:h(()=>[v.value?.hasUpdate?(w(),_(k,{key:0,type:`primary`,loading:p.value,onClick:B},{default:h(()=>[n(C(p.value?`更新中...`:`立即更新`),1)]),_:1},8,[`loading`])):c(``,!0),o(k,{onClick:i[1]||=e=>s.value=!1},{default:h(()=>[...i[11]||=[n(`关闭`,-1)]]),_:1})]),default:h(()=>[m((w(),l(`div`,F,[g.value&&!g.value.supported?(w(),_(a,{key:0,title:`当前为源码运行模式,更新功能不可用`,type:`info`,"show-icon":``,closable:!1},{default:h(()=>[...i[3]||=[e(`div`,{class:`alert-hint`},[n(` 仅 npm 全局安装的包支持自动更新。本地开发用仓库里的 `),e(`code`,null,`pnpm upgrade:local`),n(` 更新运行中的服务。 `)],-1)]]),_:1})):v.value?(w(),l(d,{key:1},[e(`div`,I,[i[5]||=e(`span`,{class:`version-label`},`当前版本`,-1),o(x,{size:`small`},{default:h(()=>[n(C(v.value.currentVersion),1)]),_:1}),r(P)(v.value.currentVersion)?(w(),_(x,{key:0,type:`warning`,size:`small`,"disable-transitions":``},{default:h(()=>[...i[4]||=[n(`本地构建`,-1)]]),_:1})):c(``,!0)]),e(`div`,L,[i[6]||=e(`span`,{class:`version-label`},`最新版本`,-1),o(x,{type:v.value.hasUpdate?`warning`:`success`,size:`small`,"disable-transitions":``},{default:h(()=>[n(C(v.value.latestVersion),1)]),_:1},8,[`type`])]),o(a,{title:v.value.hasUpdate?`发现新版本 ${v.value.latestVersion}(当前 ${v.value.currentVersion})`:`已是最新版本 ${v.value.currentVersion}`,type:v.value.hasUpdate?`warning`:`success`,"show-icon":``,closable:!1,class:`mt-12`},null,8,[`title`,`type`]),e(`div`,R,[n(` 检查时间:`+C(D(v.value.checkedAt))+` `,1),v.value.hasUpdate?(w(),l(d,{key:0},[i[8]||=n(` · `,-1),o(E,{type:`primary`,href:O(v.value.latestVersion),target:`_blank`},{default:h(()=>[...i[7]||=[n(`在 npm 查看该版本`,-1)]]),_:1},8,[`href`])],64)):c(``,!0)])],64)):!u.value&&y.value?(w(),_(a,{key:2,title:y.value,type:`error`,"show-icon":``,closable:!1},{default:h(()=>[i[10]||=e(`div`,{class:`alert-hint`},` 检查需访问 npm 源,可能是网络不可达或版本号解析失败;稍后重试即可。 `,-1),o(k,{size:`small`,class:`mt-8`,onClick:z},{default:h(()=>[...i[9]||=[n(`重试`,-1)]]),_:1})]),_:1},8,[`title`])):c(``,!0)])),[[j,u.value]]),b.value?(w(),_(a,{key:0,title:b.value,type:`info`,"show-icon":``,class:`mt-12`,onClose:i[0]||=e=>b.value=null},null,8,[`title`])):c(``,!0)]),_:1},8,[`modelValue`,`close-on-click-modal`])}}}),[[`__scopeId`,`data-v-ea1efda8`]]);a(),i(),D();var B={class:`layout`},V=[`title`],H={key:0,class:`header-version-tag`},U={class:`header-nav`},W={class:`header-actions`},G={class:`main`},K={class:`page`},q=s({__name:`LayoutView`,setup(i){let a=y(),s=T(),d=t(!1);u(()=>{b()});let p=v(()=>g.value?.version||``),m=v(()=>P(g.value?.version)),S=v(()=>m.value?`本地构建版本(upgrade:local 产物)`:`当前运行版本`),E=v(()=>g.value?.authRequired!==!1),D=v(()=>{if(!E.value)return`免登录`;let e=localStorage.getItem(`webui_token`)||``;try{return atob(e).split(`:`)[0]||`用户`}catch{return`用户`}}),k=v(()=>{let e=a.path;return e===`/`?`/`:e.startsWith(`/providers`)?`/providers`:e.startsWith(`/service-status`)?`/service-status`:e.startsWith(`/settings`)?`/settings`:e.startsWith(`/logs`)?`/logs`:`/`}),A=()=>{x(),s.push(`/login`)},j=e=>{e===`logout`&&A(),e===`check-update`&&(d.value=!0)};return(t,i)=>{let a=f(`el-menu-item`),s=f(`el-menu`),u=f(`el-icon`),g=f(`el-button`),v=f(`el-dropdown-item`),y=f(`el-dropdown-menu`),b=f(`el-dropdown`),x=f(`el-header`),T=f(`router-view`);return w(),l(`div`,B,[o(x,{class:`header`},{default:h(()=>[i[8]||=e(`h1`,null,[e(`span`,null,`E`),n(`ZLLM`)],-1),p.value?(w(),l(`span`,{key:0,class:`header-version`,title:S.value},[n(` v`+C(p.value),1),m.value?(w(),l(`span`,H,`本地构建`)):c(``,!0)],8,V)):c(``,!0),e(`nav`,U,[o(s,{mode:`horizontal`,"default-active":k.value,ellipsis:!1,router:``,class:`header-menu`},{default:h(()=>[o(a,{index:`/`},{default:h(()=>[...i[1]||=[n(`首页`,-1)]]),_:1}),o(a,{index:`/providers`},{default:h(()=>[...i[2]||=[n(`供应商管理`,-1)]]),_:1}),o(a,{index:`/service-status`},{default:h(()=>[...i[3]||=[n(`服务状态`,-1)]]),_:1}),o(a,{index:`/logs`},{default:h(()=>[...i[4]||=[n(`日志`,-1)]]),_:1}),o(a,{index:`/settings`},{default:h(()=>[...i[5]||=[n(`设置`,-1)]]),_:1})]),_:1},8,[`default-active`])]),e(`div`,W,[o(b,{trigger:`click`,onCommand:j,class:`ml-12`},{dropdown:h(()=>[o(y,null,{default:h(()=>[o(v,{command:`check-update`},{default:h(()=>[...i[6]||=[n(`检查更新`,-1)]]),_:1}),E.value?(w(),_(v,{key:0,command:`logout`,divided:``},{default:h(()=>[...i[7]||=[n(`退出`,-1)]]),_:1})):c(``,!0)]),_:1})]),default:h(()=>[o(g,null,{default:h(()=>[n(C(D.value)+` `,1),o(u,{class:`el-icon--right`},{default:h(()=>[o(r(O))]),_:1})]),_:1})]),_:1})])]),_:1}),e(`main`,G,[e(`div`,K,[o(T)])]),o(z,{modelValue:d.value,"onUpdate:modelValue":i[0]||=e=>d.value=e},null,8,[`modelValue`])])}}});export{q as default};
@@ -1 +1 @@
1
- import{A as e,C as t,Et as n,F as r,H as i,I as a,L as o,M as s,N as c,S as l,a as u,at as d,c as f,d as p,h as m,ht as h,j as g,tt as _,u as v,yt as y}from"./router-DjNMXIJT.js";i(),m(),y();var b={class:`login-wrap`},x=p(o({__name:`LoginView`,setup(i){let o=v(),p=n(``),m=n(``),y=n(``),x=n(!1);async function S(){if(!p.value){y.value=`请输入用户名`;return}if(!m.value){y.value=`请输入密码`;return}x.value=!0,y.value=``;try{let e=await u.post(`/api/auth/login`,{username:p.value.trim(),password:m.value});f(e.token),o.push(`/`)}catch(e){y.value=e.message||`登录失败`}finally{x.value=!1}}return(n,i)=>{let o=d(`el-input`),u=d(`el-form-item`),f=d(`el-alert`),v=d(`el-button`),C=d(`el-form`),w=d(`el-card`);return _(),c(`div`,b,[a(w,{shadow:`hover`,class:`login-card`},{default:h(()=>[i[3]||=e(`h2`,{class:`login-title`},`EZLLM`,-1),a(C,{"label-position":`top`,onSubmit:t(S,[`prevent`])},{default:h(()=>[a(u,{label:`用户名`},{default:h(()=>[a(o,{modelValue:p.value,"onUpdate:modelValue":i[0]||=e=>p.value=e,placeholder:`请输入用户名`,onKeyup:l(S,[`enter`])},null,8,[`modelValue`])]),_:1}),a(u,{label:`密码`},{default:h(()=>[a(o,{modelValue:m.value,"onUpdate:modelValue":i[1]||=e=>m.value=e,type:`password`,placeholder:`请输入密码`,"show-password":``,onKeyup:l(S,[`enter`])},null,8,[`modelValue`])]),_:1}),y.value?(_(),g(f,{key:0,title:y.value,type:`error`,"show-icon":``,class:`mb-16`},null,8,[`title`])):s(``,!0),a(v,{type:`primary`,class:`full-width`,loading:x.value,onClick:S},{default:h(()=>[...i[2]||=[r(` 登录 `,-1)]]),_:1},8,[`loading`])]),_:1})]),_:1})])}}}),[[`__scopeId`,`data-v-c8c59fbd`]]);export{x as default};
1
+ import{A as e,C as t,Et as n,F as r,H as i,I as a,L as o,M as s,N as c,S as l,a as u,at as d,c as f,d as p,h as m,ht as h,j as g,tt as _,u as v,yt as y}from"./router-BbFt1lFf.js";i(),m(),y();var b={class:`login-wrap`},x=p(o({__name:`LoginView`,setup(i){let o=v(),p=n(``),m=n(``),y=n(``),x=n(!1);async function S(){if(!p.value){y.value=`请输入用户名`;return}if(!m.value){y.value=`请输入密码`;return}x.value=!0,y.value=``;try{let e=await u.post(`/api/auth/login`,{username:p.value.trim(),password:m.value});f(e.token),o.push(`/`)}catch(e){y.value=e.message||`登录失败`}finally{x.value=!1}}return(n,i)=>{let o=d(`el-input`),u=d(`el-form-item`),f=d(`el-alert`),v=d(`el-button`),C=d(`el-form`),w=d(`el-card`);return _(),c(`div`,b,[a(w,{shadow:`hover`,class:`login-card`},{default:h(()=>[i[3]||=e(`h2`,{class:`login-title`},`EZLLM`,-1),a(C,{"label-position":`top`,onSubmit:t(S,[`prevent`])},{default:h(()=>[a(u,{label:`用户名`},{default:h(()=>[a(o,{modelValue:p.value,"onUpdate:modelValue":i[0]||=e=>p.value=e,placeholder:`请输入用户名`,onKeyup:l(S,[`enter`])},null,8,[`modelValue`])]),_:1}),a(u,{label:`密码`},{default:h(()=>[a(o,{modelValue:m.value,"onUpdate:modelValue":i[1]||=e=>m.value=e,type:`password`,placeholder:`请输入密码`,"show-password":``,onKeyup:l(S,[`enter`])},null,8,[`modelValue`])]),_:1}),y.value?(_(),g(f,{key:0,title:y.value,type:`error`,"show-icon":``,class:`mb-16`},null,8,[`title`])):s(``,!0),a(v,{type:`primary`,class:`full-width`,loading:x.value,onClick:S},{default:h(()=>[...i[2]||=[r(` 登录 `,-1)]]),_:1},8,[`loading`])]),_:1})]),_:1})])}}}),[[`__scopeId`,`data-v-c8c59fbd`]]);export{x as default};
@@ -0,0 +1 @@
1
+ .ip-link[data-v-2e1cbd0d]{color:#409eff;cursor:pointer}.ip-link[data-v-2e1cbd0d]:hover{text-decoration:underline}.ip-pop[data-v-2e1cbd0d]{min-height:20px;font-size:13px}.ip-pop-row[data-v-2e1cbd0d]{gap:8px;margin-bottom:4px;display:flex}.ip-pop-label[data-v-2e1cbd0d]{color:#909399;flex-shrink:0}.ip-pop-error[data-v-2e1cbd0d]{color:#f56c6c}.ip-pop-link[data-v-2e1cbd0d]{color:#409eff;font-size:13px}.error-text[data-v-2e1cbd0d]{color:#f56c6c}.pane-bar[data-v-2e1cbd0d]{flex-shrink:0;justify-content:flex-end;margin-bottom:8px;display:flex}.json-block[data-v-2e1cbd0d]{white-space:pre-wrap;word-break:break-all;background:#f5f7fa;border:1px solid #e4e7ed;border-radius:4px;flex:1;min-height:0;padding:12px;font-size:13px;line-height:1.5;overflow:auto}.detail-content[data-v-2e1cbd0d]{flex-direction:column;height:78vh;display:flex;overflow:hidden}.detail-content[data-v-2e1cbd0d] .el-tabs{flex-direction:column;flex:1;min-height:0;display:flex}.detail-content[data-v-2e1cbd0d] .el-tabs__content{flex:1;min-height:0}.detail-content[data-v-2e1cbd0d] .el-tab-pane{flex-direction:column;height:100%;display:flex;overflow:auto}.copy-btn[data-v-2e1cbd0d]{margin-left:8px;padding:2px 10px;font-size:12px}.logs-page[data-v-30d3051a]{flex-direction:column;height:calc(100vh - 124px);display:flex;overflow:hidden}.request-id[data-v-30d3051a]{cursor:pointer;white-space:nowrap;text-overflow:ellipsis;font-family:ui-monospace,Consolas,monospace;display:block;overflow:hidden}.request-id[data-v-30d3051a]:hover{color:var(--el-color-primary)}.filter-bar[data-v-30d3051a]{flex-wrap:nowrap;align-items:center;gap:12px;display:flex}.filter-bar .keyword-input[data-v-30d3051a]{flex:1;min-width:160px}.filter-bar .push-right[data-v-30d3051a]{margin-left:auto}.filter-bar[data-v-30d3051a] .el-button+.el-button{margin-left:0}.mt-16[data-v-30d3051a]{margin-top:16px}.table-card[data-v-30d3051a]{flex:1;min-height:0;overflow:hidden}.table-card[data-v-30d3051a] .el-card__body{flex-direction:column;height:100%;display:flex}.table-wrap[data-v-30d3051a]{flex:1;min-height:0}.pagination-bar[data-v-30d3051a]{flex-shrink:0;justify-content:space-between;align-items:center;margin-top:16px;display:flex}
@@ -0,0 +1,9 @@
1
+ import{A as e,Et as t,F as n,Ft as r,Gt as i,H as a,I as o,L as s,M as c,N as l,Q as u,S as d,T as f,a as p,at as m,d as h,gt as ee,h as g,ht as _,j as v,k as y,ot as te,pn as b,rt as x,tt as S,yt as C}from"./router-BbFt1lFf.js";import{t as w}from"./index-Bxg3HL8C.js";import{t as T}from"./providers-DBzc7lYt.js";import{n as E,t as D}from"./useCopyToast-SPDhUFJc.js";import{t as O}from"./useToast-D0wtmNdH.js";import{t as k}from"./latestRequest-Dk21IcrH.js";import{c as A,d as j,l as M,n as ne,o as N,r as re,s as P}from"./format-OBe8peUU.js";a(),C(),i();var F={key:1,class:`detail-content`},I={key:0,class:`ip-link`},L={class:`ip-pop`},R={class:`ip-pop-row`},z={key:0,class:`ip-pop-row`},B={key:1,class:`ip-pop-error`},V={key:2},H=[`href`],U={key:1},W={key:1},G={class:`error-text`},K={class:`pane-bar`},q={class:`json-block`},J={class:`pane-bar`},Y={class:`json-block`},ie={class:`pane-bar`},X={class:`json-block`},ae={class:`pane-bar`},Z={class:`json-block`},Q={class:`pane-bar`},oe={class:`json-block`},$={class:`pane-bar`},se={class:`json-block`},ce={class:`pane-bar`},le={class:`json-block`},ue={class:`pane-bar`},de={class:`json-block`},fe=h(s({__name:`LogDetailDialog`,setup(i,{expose:a}){let{copyWithToast:s}=D(),{loadError:u,run:d}=E(),h=t(!1),g=t(null),y=t(`info`),x=t(!1),C=t(null),w=k(),T=e=>{C.value=e,h.value=!0,g.value=null,y.value=`info`;let t=w();d(async()=>{try{let n=`?id=${String(e.id)}`,r=await p.get(`/api/logs/detail`+n);if(!t())return;r&&r.detail?g.value={...r.detail,...r.meta}:g.value=r}catch(e){if(!t())return;throw e}})},O=()=>{C.value&&T(C.value)};a({open:T});let j=async e=>{if(!(e._ipInfo||e._ipInfoError)){x.value=!0;try{let t=await p.get(`/api/logs/ipinfo?ip=${encodeURIComponent(e.ip)}`);t.error?e._ipInfoError=t.error:e._ipInfo=t}catch{e._ipInfoError=`查询失败`}finally{x.value=!1}}},fe=e=>[e.country,e.region,e.city].filter(Boolean).join(` · `)||`-`;return(t,i)=>{let a=m(`el-button`),d=m(`el-alert`),p=m(`el-descriptions-item`),C=m(`el-tag`),w=m(`el-divider`),T=m(`el-popover`),E=m(`el-descriptions`),D=m(`el-tab-pane`),k=m(`el-tabs`),pe=m(`el-dialog`),me=te(`loading`);return S(),v(pe,{modelValue:h.value,"onUpdate:modelValue":i[11]||=e=>h.value=e,title:`日志详情`,width:`80%`,top:`5vh`,"destroy-on-close":``},{default:_(()=>[r(u)?(S(),v(d,{key:0,title:r(u),type:`error`,"show-icon":``,closable:!1,class:`mb-16`},{default:_(()=>[o(a,{size:`small`,onClick:O},{default:_(()=>[...i[12]||=[n(`重试`,-1)]]),_:1})]),_:1},8,[`title`])):c(``,!0),g.value?(S(),l(`div`,F,[o(k,{modelValue:y.value,"onUpdate:modelValue":i[10]||=e=>y.value=e},{default:_(()=>[o(D,{label:`基本信息`,name:`info`},{default:_(()=>[o(E,{column:2,border:``},{default:_(()=>[o(p,{label:`时间`},{default:_(()=>[n(b(r(A)(g.value.time)),1)]),_:1}),o(p,{label:`模型`},{default:_(()=>[n(b(g.value.model),1)]),_:1}),o(p,{label:`方法`},{default:_(()=>[n(b(g.value.method),1)]),_:1}),o(p,{label:`路径`},{default:_(()=>[n(b(g.value.path),1)]),_:1}),o(p,{label:`状态码`},{default:_(()=>[n(b(g.value.status),1)]),_:1}),o(p,{label:`流式`},{default:_(()=>[o(C,{type:g.value.stream?`warning`:`info`},{default:_(()=>[n(b(g.value.stream?`是`:`否`),1)]),_:1},8,[`type`])]),_:1}),o(p,{label:`IP`},{default:_(()=>[g.value.ip?(S(),l(`span`,I,[o(T,{placement:`right`,width:`240`,trigger:`click`,onShow:i[0]||=e=>j(g.value)},{reference:_(()=>[e(`span`,null,b(g.value.ip),1)]),default:_(()=>[ee((S(),l(`div`,L,[g.value._ipInfo?(S(),l(f,{key:0},[e(`div`,R,[i[13]||=e(`span`,{class:`ip-pop-label`},`归属地`,-1),e(`span`,null,b(fe(g.value._ipInfo)),1)]),g.value._ipInfo.isp?(S(),l(`div`,z,[i[14]||=e(`span`,{class:`ip-pop-label`},`运营商`,-1),e(`span`,null,b(g.value._ipInfo.isp),1)])):c(``,!0)],64)):g.value._ipInfoError?(S(),l(`div`,B,b(g.value._ipInfoError),1)):(S(),l(`span`,V,`查询中...`)),o(w,{style:{margin:`8px 0`}}),e(`a`,{class:`ip-pop-link`,href:`https://m.ip138.com/iplookup.php?ip=${encodeURIComponent(g.value.ip)}`,target:`_blank`},`在 ip138 查看`,8,H)])),[[me,x.value]])]),_:1})])):(S(),l(`span`,U,`-`))]),_:1}),o(p,{label:`Session`},{default:_(()=>[n(b(g.value.session_id||`-`),1)]),_:1}),o(p,{label:`ID`},{default:_(()=>[g.value._id==null?(S(),l(`span`,W,`-`)):(S(),l(`span`,{key:0,class:`request-id`,title:`点击复制(日志 id 同时是响应头 x-request-id)`,onClick:i[1]||=e=>r(s)(String(g.value._id))},b(g.value._id),1))]),_:1}),o(p,{label:`输入 Token(含缓存读取)`},{default:_(()=>[n(b(r(N)(g.value.input_tokens??0)),1)]),_:1}),o(p,{label:`输出 Token`},{default:_(()=>[n(b(r(N)(g.value.output_tokens??0)),1)]),_:1}),o(p,{label:`缓存读取`},{default:_(()=>[n(b(r(N)(g.value.cache_read_tokens??0)),1)]),_:1}),o(p,{label:`请求耗时`},{default:_(()=>[n(b(r(re)(g.value.duration)),1)]),_:1}),o(p,{label:`缓存命中率`},{default:_(()=>[n(b(r(ne)(g.value)),1)]),_:1}),g.value.error?(S(),v(p,{key:0,label:`错误`,span:2},{default:_(()=>[e(`span`,G,b(g.value.error),1)]),_:1})):c(``,!0)]),_:1}),g.value.response?.truncated?(S(),v(d,{key:0,class:`mt-16`,type:`warning`,closable:!1,title:`内容超过单请求捕获上限,已截断`})):c(``,!0)]),_:1}),g.value.response?._capture?(S(),l(f,{key:0},[o(D,{label:`客户端请求`,name:`inbound`},{default:_(()=>[e(`div`,K,[o(a,{size:`small`,class:`copy-btn`,onClick:i[2]||=e=>r(s)(r(P)(g.value.response.inbound_request))},{default:_(()=>[...i[15]||=[n(` 复制 `,-1)]]),_:1})]),e(`pre`,q,b(r(P)(g.value.response.inbound_request)),1)]),_:1}),o(D,{label:`转发上游的请求`,name:`upstreamReq`},{default:_(()=>[e(`div`,J,[o(a,{size:`small`,class:`copy-btn`,onClick:i[3]||=e=>r(s)(r(P)(g.value.response.upstream_request))},{default:_(()=>[...i[16]||=[n(` 复制 `,-1)]]),_:1})]),e(`pre`,Y,b(r(P)(g.value.response.upstream_request)),1)]),_:1}),g.value.response.upstream_response_text?(S(),v(D,{key:0,label:`上游响应(原始)`,name:`upstreamRes`},{default:_(()=>[e(`div`,ie,[o(a,{size:`small`,class:`copy-btn`,onClick:i[4]||=e=>r(s)(g.value.response.upstream_response_text)},{default:_(()=>[...i[17]||=[n(` 复制 `,-1)]]),_:1})]),e(`pre`,X,b(r(M)(g.value.response.upstream_response_text)),1)]),_:1})):g.value.response.upstream_stream_frames?.length?(S(),v(D,{key:1,label:`上游响应(原始流,${g.value.response.upstream_stream_frames.length} 帧)`,name:`upstreamStream`},{default:_(()=>[e(`div`,ae,[o(a,{size:`small`,class:`copy-btn`,onClick:i[5]||=e=>r(s)(g.value.response.upstream_stream_frames.join(`
2
+
3
+ `))},{default:_(()=>[...i[18]||=[n(` 复制 `,-1)]]),_:1})]),e(`pre`,Z,b(g.value.response.upstream_stream_frames.join(`
4
+
5
+ `)),1)]),_:1},8,[`label`])):c(``,!0),g.value.response.client_response!==void 0&&g.value.response.client_response!==null?(S(),v(D,{key:2,label:`回写客户端(转换后)`,name:`clientRes`},{default:_(()=>[e(`div`,Q,[o(a,{size:`small`,class:`copy-btn`,onClick:i[6]||=e=>r(s)(r(P)(g.value.response.client_response))},{default:_(()=>[...i[19]||=[n(` 复制 `,-1)]]),_:1})]),e(`pre`,oe,b(r(P)(g.value.response.client_response)),1)]),_:1})):g.value.response.client_stream_frames?.length?(S(),v(D,{key:3,label:`回写客户端(${g.value.response.client_stream_frames.length} 帧)`,name:`clientStream`},{default:_(()=>[e(`div`,$,[o(a,{size:`small`,class:`copy-btn`,onClick:i[7]||=e=>r(s)(g.value.response.client_stream_frames.join(`
6
+
7
+ `))},{default:_(()=>[...i[20]||=[n(` 复制 `,-1)]]),_:1})]),e(`pre`,se,b(g.value.response.client_stream_frames.join(`
8
+
9
+ `)),1)]),_:1},8,[`label`])):c(``,!0)],64)):(S(),l(f,{key:1},[o(D,{label:`请求体`,name:`request`},{default:_(()=>[e(`div`,ce,[o(a,{size:`small`,class:`copy-btn`,onClick:i[8]||=e=>r(s)(r(P)(g.value.request))},{default:_(()=>[...i[21]||=[n(` 复制 `,-1)]]),_:1})]),e(`pre`,le,b(r(P)(g.value.request)),1)]),_:1}),o(D,{label:`响应体`,name:`response`},{default:_(()=>[e(`div`,ue,[o(a,{size:`small`,class:`copy-btn`,onClick:i[9]||=e=>r(s)(r(P)(g.value.response))},{default:_(()=>[...i[22]||=[n(` 复制 `,-1)]]),_:1})]),e(`pre`,de,b(r(P)(g.value.response)),1)]),_:1})],64))]),_:1},8,[`modelValue`])])):c(``,!0)]),_:1},8,[`modelValue`])}}}),[[`__scopeId`,`data-v-2e1cbd0d`]]);function pe(e){if(e.truncated!==!0)return null;let t=e.scanLimit;return`结果可能不完整:${typeof t==`number`&&Number.isFinite(t)&&t>0?`仅扫描了最近 ${t} 条有详情的记录`:`仅扫描了最近的部分记录`},建议缩小时间范围或更换关键词`}function me(e,t){let n=e.trim();return t>0||n.length!==1?null:`未找到匹配记录:单字符关键字仅按摘要列(模型、路径等)匹配,不会回退搜索请求/响应内容,建议补充至 2 个字符以上`}a(),C(),g(),i();var he={class:`logs-page`},ge={class:`filter-bar`},_e={class:`table-wrap`},ve=[`onClick`],ye={key:1},be={class:`pagination-bar`},xe=h(s({__name:`LogsView`,setup(i){let a=T(),{toast:s}=O(),{copyWithToast:h}=D(),{loadError:g,run:C}=E(),M=t(``),P=t(null),F=t(`2xx`),I=t(`model`),L=t(``),R=t(``),z=t([]),B=t(0),V=t(1),H=t(50),U=t(!1),W=t(null),G=t(null),K=e=>{G.value?.open(e)},q=y(()=>{let e=new Set;for(let t of a.suppliers)for(let n of t.models)e.add(n.modelName);for(let t of z.value)t.model&&e.add(t.model);return[...e].sort()}),J=e=>{let t=j(e);t&&(M.value=e,P.value=t,$())},Y=e=>{e?J(e):(M.value=``,P.value=null,$())},ie=e=>{M.value=``,P.value=e||null,$()},X=()=>{let e={};return P.value&&P.value.length===2&&(e.startDate=P.value[0],e.endDate=P.value[1]),F.value&&(e.status=F.value),I.value&&(e.pathGroup=I.value),L.value&&(e.model=L.value),R.value&&(e.keyword=R.value),e},ae=k(),Z=()=>C(async()=>{let e=ae();U.value=!0;try{let t={...X(),page:String(V.value),pageSize:String(H.value),sort:`time`,order:`desc`},n=new URLSearchParams(t).toString(),r=await p.get(`/api/logs?`+n);if(!e())return;z.value=r.items||[],B.value=r.total||0,W.value=pe(r)??me(R.value,r.total||0)}catch(t){if(!e())return;throw t}finally{e()&&(U.value=!1)}}),Q=t(!1),oe=async()=>{if(!(Q.value||B.value===0)){try{await w.confirm(`将永久清理当前筛选条件下的 ${B.value} 条日志,清理后不可恢复,请谨慎操作!`,`清理确认`,{confirmButtonText:`确认清理`,cancelButtonText:`取消`,type:`warning`})}catch{return}Q.value=!0;try{let e=new URLSearchParams(X()).toString(),t=await p.post(`/api/logs/clear?`+e,{});s(`已清理 ${t.deleted??0} 条日志`,`success`),V.value>1&&(V.value=1),Z()}catch(e){s(e.message||`清理失败`,`error`)}finally{Q.value=!1}}},$=()=>{V.value=1,Z()},se=()=>{M.value=`today`,P.value=null,F.value=`2xx`,I.value=`model`,L.value=``,R.value=``,V.value=1,H.value=50,J(`today`)};return u(()=>{a.loadProviders().catch(()=>{}),J(`today`)}),(t,i)=>{let a=m(`el-button`),s=m(`el-alert`),u=m(`el-option`),p=m(`el-select`),y=m(`el-date-picker`),C=m(`el-input`),w=m(`el-card`),T=m(`el-table-column`),E=m(`el-tag`),D=m(`el-table`),O=m(`el-pagination`),k=te(`loading`);return S(),l(`div`,he,[r(g)?(S(),v(s,{key:0,title:r(g),type:`error`,"show-icon":``,closable:!1,class:`mb-16`},{default:_(()=>[o(a,{size:`small`,onClick:Z},{default:_(()=>[...i[9]||=[n(`重试`,-1)]]),_:1})]),_:1},8,[`title`])):c(``,!0),W.value?(S(),v(s,{key:1,title:W.value,type:`warning`,"show-icon":``,closable:``,class:`mb-16`,onClose:i[0]||=e=>W.value=null},null,8,[`title`])):c(``,!0),o(w,{shadow:`never`},{default:_(()=>[e(`div`,ge,[o(p,{modelValue:M.value,"onUpdate:modelValue":i[1]||=e=>M.value=e,placeholder:`时间范围`,clearable:``,style:{width:`130px`},onChange:Y},{default:_(()=>[o(u,{label:`今天`,value:`today`}),o(u,{label:`昨天`,value:`yesterday`}),o(u,{label:`前天`,value:`beforeYesterday`}),o(u,{label:`近三天`,value:`last3days`}),o(u,{label:`本周`,value:`week`}),o(u,{label:`本月`,value:`month`})]),_:1},8,[`modelValue`]),o(y,{modelValue:P.value,"onUpdate:modelValue":i[2]||=e=>P.value=e,type:`daterange`,"value-format":`YYYY-MM-DD`,"range-separator":`至`,"start-placeholder":`开始日期`,"end-placeholder":`结束日期`,style:{width:`260px`},onChange:ie},null,8,[`modelValue`]),o(p,{modelValue:F.value,"onUpdate:modelValue":i[3]||=e=>F.value=e,placeholder:`全部状态`,clearable:``,style:{width:`120px`},onChange:$},{default:_(()=>[o(u,{label:`成功 (2xx)`,value:`2xx`}),o(u,{label:`3xx`,value:`3xx`}),o(u,{label:`4xx`,value:`4xx`}),o(u,{label:`5xx`,value:`5xx`}),o(u,{label:`错误 (4xx+)`,value:`err`})]),_:1},8,[`modelValue`]),o(p,{modelValue:I.value,"onUpdate:modelValue":i[4]||=e=>I.value=e,placeholder:`全部路径`,clearable:``,style:{width:`130px`},onChange:$},{default:_(()=>[o(u,{label:`模型接口`,value:`model`}),o(u,{label:`其他`,value:`other`})]),_:1},8,[`modelValue`]),o(p,{modelValue:L.value,"onUpdate:modelValue":i[5]||=e=>L.value=e,placeholder:`全部模型`,clearable:``,filterable:``,style:{width:`220px`},onChange:$},{default:_(()=>[(S(!0),l(f,null,x(q.value,e=>(S(),v(u,{key:e,label:e,value:e},null,8,[`label`,`value`]))),128))]),_:1},8,[`modelValue`]),o(C,{modelValue:R.value,"onUpdate:modelValue":i[6]||=e=>R.value=e,class:`keyword-input`,placeholder:`关键字搜索(请求/响应内容)`,clearable:``,onKeyup:d($,[`enter`])},null,8,[`modelValue`]),o(a,{type:`primary`,class:`push-right`,onClick:$},{default:_(()=>[...i[10]||=[n(`搜索`,-1)]]),_:1}),o(a,{onClick:se},{default:_(()=>[...i[11]||=[n(`重置`,-1)]]),_:1})])]),_:1}),o(w,{shadow:`never`,class:`mt-16 table-card`},{default:_(()=>[e(`div`,_e,[ee((S(),v(D,{data:z.value,border:``,stripe:``,height:`100%`},{default:_(()=>[o(T,{prop:`id`,label:`ID`,"min-width":`110`},{default:_(({row:e})=>[e.id==null?(S(),l(`span`,ye,`-`)):(S(),l(`span`,{key:0,class:`request-id`,title:`点击复制(日志 id 同时是响应头 x-request-id,可用于关联控制台日志与检索)`,onClick:t=>r(h)(String(e.id))},b(e.id),9,ve))]),_:1}),o(T,{prop:`time`,label:`时间`,width:`180`},{default:_(({row:e})=>[n(b(r(A)(e.time)),1)]),_:1}),o(T,{prop:`model`,label:`模型`,"min-width":`180`}),o(T,{prop:`status`,label:`状态`,width:`70`,align:`center`},{default:_(({row:e})=>[o(E,{type:e.status>=400?`danger`:e.status>=300?`warning`:`success`},{default:_(()=>[n(b(e.status),1)]),_:2},1032,[`type`])]),_:1}),o(T,{label:`输入`,width:`110`,align:`center`},{default:_(({row:e})=>[n(b(r(N)(e.input_tokens??0)),1)]),_:1}),o(T,{label:`输出`,width:`110`,align:`center`},{default:_(({row:e})=>[n(b(r(N)(e.output_tokens??0)),1)]),_:1}),o(T,{label:`缓存读取`,width:`90`,align:`center`},{default:_(({row:e})=>[n(b(r(N)(e.cache_read_tokens??0)),1)]),_:1}),o(T,{label:`缓存命中率`,width:`105`,align:`center`},{default:_(({row:t})=>[e(`span`,null,b(r(ne)(t)),1)]),_:1}),o(T,{label:`请求耗时`,width:`90`,align:`center`},{default:_(({row:e})=>[n(b(r(re)(e.duration)),1)]),_:1}),o(T,{label:`详情`,width:`70`,align:`center`},{default:_(({row:e})=>[o(a,{link:``,type:`primary`,onClick:t=>K(e)},{default:_(()=>[...i[12]||=[n(`查看`,-1)]]),_:1},8,[`onClick`])]),_:1})]),_:1},8,[`data`])),[[k,U.value]])]),e(`div`,be,[o(a,{type:`danger`,plain:``,size:`small`,loading:Q.value,disabled:B.value===0,onClick:oe},{default:_(()=>[...i[13]||=[n(`清理已筛选日志`,-1)]]),_:1},8,[`loading`,`disabled`]),o(O,{class:`push-right`,"current-page":V.value,"onUpdate:currentPage":i[7]||=e=>V.value=e,"page-size":H.value,"onUpdate:pageSize":i[8]||=e=>H.value=e,total:B.value,"page-sizes":[20,50,100,200],layout:`total, sizes, prev, pager, next`,onCurrentChange:Z,onSizeChange:Z},null,8,[`current-page`,`page-size`,`total`])])]),_:1}),o(fe,{ref_key:`logDetailRef`,ref:G},null,512)])}}}),[[`__scopeId`,`data-v-30d3051a`]]);export{xe as default};
@@ -0,0 +1 @@
1
+ .quota-block[data-v-31dc0c20]{background:#fafbfc;border:1px solid #f0f2f5;border-radius:6px;margin-bottom:4px;padding:8px 10px}.quota-header[data-v-31dc0c20]{justify-content:space-between;align-items:center;gap:8px;margin-bottom:6px;display:flex}.quota-block-title[data-v-31dc0c20]{color:#303133;flex-shrink:0;font-size:13px;font-weight:600}.quota-header-stats[data-v-31dc0c20]{flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:4px;min-width:0;display:flex}.quota-hint[data-v-31dc0c20]{color:#c0c4cc;font-size:12px}.quota-error[data-v-31dc0c20]{color:#f56c6c;word-break:break-all;margin-bottom:4px;font-size:12px}.quota-row[data-v-31dc0c20]{align-items:flex-start;gap:8px;padding:3px 0;display:flex}.quota-label[data-v-31dc0c20]{color:#909399;flex-shrink:0;width:72px;font-size:12px;line-height:18px}.quota-main[data-v-31dc0c20]{flex:1;min-width:0}.quota-main[data-v-31dc0c20] .el-progress{width:100%}.quota-main[data-v-31dc0c20] .el-progress-bar__outer{background-color:#f2f2f2}.quota-main[data-v-31dc0c20] .el-progress-bar__inner{align-items:center;display:flex}.quota-main[data-v-31dc0c20] .el-progress-bar__innerText{color:#303133;white-space:nowrap;margin:0 5px;font-size:12px;line-height:1}.quota-cap-null[data-v-31dc0c20]{color:#606266;font-size:12px}.provider-card[data-v-00272874]{cursor:grab;min-width:0;transition:box-shadow .2s,border-color .2s}.provider-card[data-v-00272874] .el-card__header{padding-bottom:10px}.provider-card[data-v-00272874] .el-card__body{padding-top:12px}.provider-card[data-v-00272874]:hover{border-color:#b1d2ff;box-shadow:0 4px 16px #3b82f61a}.provider-card.dragging[data-v-00272874]{cursor:grabbing}.provider-card.is-disabled[data-v-00272874]{background:#fafafa}.provider-card.is-disabled .provider-name[data-v-00272874]{color:#a8abb2}.provider-card.is-disabled[data-v-00272874] .el-card__header{background:#fafafa}.card-header-right[data-v-00272874]{flex-shrink:0;align-items:center;gap:10px;display:flex}.header-switch[data-v-00272874]{cursor:auto;align-items:center;display:flex}.header-switch[data-v-00272874] .el-switch{cursor:pointer}.card-header[data-v-00272874]{justify-content:space-between;align-items:center;gap:12px;display:flex}.card-title[data-v-00272874]{align-items:center;gap:8px;min-width:0;display:flex}.provider-name[data-v-00272874]{color:#303133;text-overflow:ellipsis;white-space:nowrap;font-size:15px;font-weight:600;overflow:hidden}.card-actions[data-v-00272874]{cursor:auto;flex-shrink:0;gap:0;display:flex}.model-tags[data-v-00272874]{flex-wrap:wrap;gap:6px;margin-top:10px;display:flex}.model-tag[data-v-00272874]{color:#606266;cursor:pointer;text-overflow:ellipsis;white-space:nowrap;background:#f5f7fa;border:1px solid #0000;border-radius:999px;align-items:center;max-width:100%;padding:3px 10px;font-size:12px;line-height:18px;transition:color .15s,background .15s;display:inline-flex;overflow:hidden}.model-tag[data-v-00272874]:hover{color:#3b82f6;background:#eff6ff}.model-tag.ok[data-v-00272874]{color:#16a34a;background:#f0fdf4}.model-tag.error[data-v-00272874]{color:#dc2626;background:#fef2f2}.badge-mark[data-v-00272874]{margin-left:3px}.card-footer[data-v-00272874]{border-top:1px solid #f0f2f5;justify-content:space-between;align-items:center;margin-top:14px;padding-top:12px;display:flex}.model-count[data-v-00272874]{color:#16a34a;cursor:pointer;border-radius:4px;font-size:13px;font-weight:500}.model-count[data-v-00272874]:hover{text-decoration:underline}.drag-hint-icon[data-v-00272874]{color:#c0c4cc;cursor:grab;flex-shrink:0}.drag-hint-icon[data-v-00272874]:hover{color:#909399}.detail-body[data-v-08dfa117]{max-height:70vh;overflow-y:auto}.detail-head[data-v-08dfa117]{align-items:center;gap:12px;margin-bottom:12px;display:flex}.detail-name[data-v-08dfa117]{font-size:14px;font-weight:600}.detail-pre[data-v-08dfa117]{color:#303133;white-space:pre-wrap;word-break:break-all;background:#f5f7fa;border:1px solid #e4e7ed;border-radius:6px;max-height:300px;margin:0;padding:12px;font-size:12px;line-height:1.6;overflow:auto}.protocol-help .help-intro[data-v-2248b61a]{color:#606266;margin-bottom:14px;font-size:13px}.protocol-help .help-item[data-v-2248b61a]{margin-bottom:12px}.protocol-help .help-title[data-v-2248b61a]{color:#303133;align-items:center;gap:8px;margin-bottom:4px;font-size:14px;font-weight:600;display:flex}.protocol-help .help-item p[data-v-2248b61a]{color:#606266;margin:0;font-size:13px;line-height:1.7}.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-5ab9aafa]{flex-shrink:0;width:400px}.test-result-link[data-v-5ab9aafa]{font-size:12px}.col-help[data-v-5ab9aafa]{margin-right:4px}.dialog-body[data-v-1d6d4d0a]{max-height:80vh;padding-bottom:100px;overflow:hidden auto}.test-ok-text[data-v-1d6d4d0a]{color:#16a34a;text-overflow:ellipsis;white-space:nowrap;vertical-align:middle;max-width:140px;font-size:12px;display:inline-block;overflow:hidden}.test-fail-text[data-v-1d6d4d0a]{color:#dc2626;text-overflow:ellipsis;white-space:nowrap;vertical-align:middle;max-width:140px;font-size:12px;display:inline-block;overflow:hidden}.protocol-hint[data-v-1d6d4d0a]{line-height:1.8}.label-inline-toggle[data-v-1d6d4d0a]{align-items:center;gap:6px;margin-left:8px;display:inline-flex}.models-dialog-body[data-v-7b39dd25]{flex-direction:column;height:calc(94vh - 100px);display:flex}.models-dialog-body .models-table[data-v-7b39dd25]{flex:1;min-height:0}.error-tag[data-v-7b39dd25]{text-overflow:ellipsis;white-space:nowrap;max-width:100px;overflow:hidden}.quota-help[data-v-775466be]{background:#fff;height:70vh;overflow:hidden auto}.quota-help .help-intro[data-v-775466be]{color:#606266;margin-bottom:14px;font-size:13px;line-height:1.7}.quota-help .help-item[data-v-775466be]{margin-bottom:12px}.quota-help .help-title[data-v-775466be]{color:#303133;margin-bottom:4px;font-size:14px;font-weight:600}.quota-help .help-item p[data-v-775466be]{color:#606266;margin:0;font-size:13px;line-height:1.7}.quota-help .help-tip[data-v-775466be]{color:#909399!important;margin-top:4px!important}.quota-help .help-shot[data-v-775466be]{border:1px solid #dcdfe6;border-radius:6px;width:100%;margin:8px 0;display:block}.quota-help code[data-v-775466be]{background:#f5f7fa;border-radius:3px;padding:1px 4px;font-size:12px}.quota-help .help-table[data-v-775466be]{width:100%}.footer[data-v-775466be]{place-content:center;display:flex}.dialog-body[data-v-90cee0de]{min-height:70vh;overflow-y:auto}.muted-sm[data-v-90cee0de]{color:#909399;font-size:12px;line-height:1.6}.status-row[data-v-90cee0de]{margin-top:4px}.test-ok-text-full[data-v-90cee0de]{color:#16a34a;font-size:12px}.test-fail-text-full[data-v-90cee0de]{color:#dc2626;word-break:break-all;font-size:12px}.col-help-icon[data-v-90cee0de]{margin-left:4px}.ghost[data-v-ede77d7c]{opacity:.4}.provider-grid[data-v-ede77d7c]{grid-template-columns:repeat(2,1fr);gap:16px;display:grid}@media (width<=992px){.provider-grid[data-v-ede77d7c]{grid-template-columns:1fr}}