llmpt 1.3.12 → 1.3.13

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/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## v1.3.13 (2026-06-17)
4
+
5
+ ### 修复
6
+ - 设置面板 GitHub 链接去掉尾部箭头符号
7
+
8
+ ---
9
+
3
10
  ## v1.3.12 (2026-06-17)
4
11
 
5
12
  ### 新增
@@ -18,4 +18,4 @@ import{r as e}from"./rolldown-runtime-QTnfLwEv.js";import{C as t,D as n,E as r,O
18
18
 
19
19
  `):``;t.trim()&&n.push(t)}return n.length?n.join(`
20
20
 
21
- `):null}function Ne(e){let t=e.response.body;if(!t)return null;if(t.choices&&Array.isArray(t.choices)&&t.choices.length>0){let e=t.choices[0].message;if(e){let t=e.content,n=e.reasoning_content,r=[];if(n&&r.push({type:`thinking`,thinking:n}),typeof t==`string`&&t.trim()?r.push({type:`text`,text:t}):Array.isArray(t)?r.push(...t):t!=null&&r.push({type:`text`,text:JSON.stringify(t)}),r.length===1&&r[0].type===`text`)return r[0].text||null;if(r.length>0)return r}return null}if(t.content&&Array.isArray(t.content)){let e=t.content;return e.length===1&&e[0].type===`text`?e[0].text||null:e}return typeof t.text==`string`?t.text:null}function Pe({onClose:e}){let[t,n]=(0,V.useState)([]),[r,i]=(0,V.useState)(!0),[a,o]=(0,V.useState)(null);(0,V.useEffect)(()=>{Promise.all([G.getProviders().then(n).catch(()=>{}),G.getConfig().then(e=>o(e.proxy?.port??null)).catch(()=>{})]).finally(()=>i(!1))},[]);let s=a??(window.location.port?Number(window.location.port):void 0);return(0,K.jsxs)(`div`,{className:`p-5 space-y-5`,children:[(0,K.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,K.jsx)(`h2`,{className:`text-base font-semibold`,children:`设置`}),(0,K.jsx)(`button`,{onClick:e,style:{color:`var(--text-tertiary)`},children:(0,K.jsx)(v,{size:16})})]}),(0,K.jsxs)(`div`,{className:`rounded-lg p-3`,style:{background:`var(--bg)`,border:`1px solid var(--border)`},children:[(0,K.jsx)(`h3`,{className:`text-sm font-medium mb-2`,children:`代理服务器`}),(0,K.jsxs)(`div`,{className:`grid grid-cols-2 gap-x-4 gap-y-1.5 text-xs`,children:[(0,K.jsx)(`span`,{style:{color:`var(--text-tertiary)`},children:`监听端口`}),(0,K.jsx)(`span`,{className:`font-mono`,style:{color:`var(--blue)`},children:s}),(0,K.jsx)(`span`,{style:{color:`var(--text-tertiary)`},children:`OpenAI Base URL`}),(0,K.jsxs)(`span`,{className:`font-mono`,style:{color:`var(--blue)`},children:[`http://localhost:`,s,`/v1`]}),(0,K.jsx)(`span`,{style:{color:`var(--text-tertiary)`},children:`Anthropic Base URL`}),(0,K.jsxs)(`span`,{className:`font-mono`,style:{color:`var(--blue)`},children:[`http://localhost:`,s]})]})]}),(0,K.jsxs)(`div`,{className:`rounded-lg p-3`,style:{background:`var(--bg)`,border:`1px solid var(--border)`},children:[(0,K.jsx)(`h3`,{className:`text-sm font-medium mb-1`,children:`上游服务商`}),(0,K.jsxs)(`p`,{className:`text-[11px] mb-3`,style:{color:`var(--text-tertiary)`},children:[`修改请编辑项目根目录的 `,(0,K.jsx)(`code`,{className:`font-mono px-1 py-0.5 rounded`,style:{background:`var(--bg-code)`},children:`.env`}),` 文件后重启服务`]}),r?(0,K.jsx)(`div`,{className:`text-sm`,style:{color:`var(--text-tertiary)`},children:`加载中…`}):t.length===0?(0,K.jsxs)(`div`,{className:`text-sm italic`,style:{color:`var(--text-tertiary)`},children:[`未检测到已配置的 Provider。请在 `,(0,K.jsx)(`code`,{className:`font-mono`,children:`.env`}),` 中设置 `,(0,K.jsx)(`code`,{className:`font-mono`,children:`OPENAI_API_KEY`}),` 等变量。`]}):(0,K.jsx)(`div`,{className:`space-y-1.5`,children:t.map(e=>(0,K.jsxs)(`div`,{className:`flex items-center gap-3 px-3 py-2 rounded`,style:{border:`1px solid var(--border)`},children:[(0,K.jsx)(`span`,{className:`w-1.5 h-1.5 rounded-full flex-shrink-0`,style:{background:`var(--green)`}}),(0,K.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,K.jsx)(`div`,{className:`text-sm font-medium capitalize`,children:e.type}),(0,K.jsx)(`div`,{className:`text-[11px] font-mono truncate`,style:{color:`var(--text-tertiary)`},children:e.baseUrl})]}),e.apiKey&&(0,K.jsx)(`span`,{className:`text-[11px] font-mono flex-shrink-0`,style:{color:`var(--text-tertiary)`},children:e.apiKey})]},e.type))})]}),(0,K.jsxs)(`div`,{className:`flex items-center justify-end gap-3 pt-1`,style:{color:`var(--text-tertiary)`,fontSize:`11px`},children:[(0,K.jsxs)(`span`,{children:[`LPT v`,`1.3.12`]}),(0,K.jsx)(`span`,{style:{opacity:.35},children:`·`}),(0,K.jsx)(`a`,{href:`https://github.com/zhxsking/llm_proxy_trace`,target:`_blank`,rel:`noopener noreferrer`,style:{color:`var(--text-tertiary)`,textDecoration:`none`},onMouseEnter:e=>e.currentTarget.style.color=`var(--text-secondary)`,onMouseLeave:e=>e.currentTarget.style.color=`var(--text-tertiary)`,children:`GitHub ↗`})]})]})}function Fe({open:e,title:t,message:n,confirmText:r=`确认`,cancelText:i=`取消`,danger:a=!1,onConfirm:o,onCancel:s}){let c=(0,V.useRef)(null);return(0,V.useEffect)(()=>{e&&c.current?.focus()},[e]),(0,V.useEffect)(()=>{if(!e)return;let t=e=>{e.key===`Escape`&&s(),e.key===`Enter`&&o()};return window.addEventListener(`keydown`,t),()=>window.removeEventListener(`keydown`,t)},[e,o,s]),e?(0,K.jsx)(`div`,{className:`confirm-overlay`,onClick:s,children:(0,K.jsxs)(`div`,{className:`confirm-dialog`,onClick:e=>e.stopPropagation(),children:[(0,K.jsx)(`div`,{className:`confirm-icon-row`,children:(0,K.jsx)(`span`,{className:`confirm-icon${a?` danger`:``}`,children:(0,K.jsx)(m,{size:20})})}),(0,K.jsx)(`div`,{className:`confirm-title`,children:t}),(0,K.jsx)(`div`,{className:`confirm-message`,children:n}),(0,K.jsxs)(`div`,{className:`confirm-actions`,children:[(0,K.jsx)(`button`,{className:`confirm-btn cancel`,onClick:s,children:i}),(0,K.jsx)(`button`,{ref:c,className:`confirm-btn ok${a?` danger`:``}`,onClick:o,children:r})]})]})}):null}function Ie(){try{let e=localStorage.getItem(`lpt-theme`);if(e===`dark`||e===`light`)return e}catch{}return window.matchMedia(`(prefers-color-scheme: dark)`).matches?`dark`:`light`}function Le(e){document.documentElement.setAttribute(`data-theme`,e);try{localStorage.setItem(`lpt-theme`,e)}catch{}}function Re(){let[e,t]=(0,V.useState)([]),[r,i]=(0,V.useState)(null),[a,c]=(0,V.useState)(null),[u,d]=(0,V.useState)(``),[f,m]=(0,V.useState)(`all`),[h,g]=(0,V.useState)(!1),[_,y]=(0,V.useState)({open:!1,title:``,message:``,onConfirm:()=>{}}),b=(e,t,n)=>{y({open:!0,title:e,message:t,onConfirm:n})},x=()=>y(e=>({...e,open:!1})),[ee,te]=(0,V.useState)(!0),[S,ne]=(0,V.useState)(Ie),[C,w]=(0,V.useState)(320),T=(0,V.useRef)(!1),E=(0,V.useRef)(0),D=(0,V.useRef)(0),[O,k]=(0,V.useState)(!1),A=(0,V.useRef)(null),[j,M]=(0,V.useState)(!1);(0,V.useEffect)(()=>{Le(S)},[S]),(0,V.useEffect)(()=>{if(!O)return;let e=e=>{A.current&&!A.current.contains(e.target)&&k(!1)};return document.addEventListener(`mousedown`,e),()=>document.removeEventListener(`mousedown`,e)},[O]);let oe=()=>ne(e=>e===`light`?`dark`:`light`),N=e.find(e=>e.id===a)||null,P=(0,V.useCallback)(async()=>{try{let[e,n]=await Promise.all([G.getTraces({keyword:u||void 0,limit:200}),G.getStats()]);t(e.traces),i(n)}catch{}finally{te(!1)}},[u]),F=(0,V.useCallback)(e=>{if(e.type===`trace:new`){let n=e.data;u||t(e=>[n,...e]),i(e=>e&&{...e,totalCalls:e.totalCalls+1})}else if(e.type===`trace:update`||e.type===`trace:complete`){let n=e.data;t(e=>e.map(e=>e.id===n.id?{...e,...n}:e)),e.type===`trace:complete`&&G.getStats().then(i).catch(()=>{})}},[u]),{connected:I}=le({url:`ws://${window.location.host}/ws`,onEvent:F});(0,V.useEffect)(()=>{P()},[P]);let L=e=>{T.current=!0,E.current=e.clientX,D.current=C,document.body.style.userSelect=`none`,document.body.style.cursor=`col-resize`;let t=e=>{if(!T.current)return;let t=e.clientX-E.current;w(Math.max(240,Math.min(600,D.current+t)))},n=()=>{T.current=!1,document.body.style.userSelect=``,document.body.style.cursor=``,document.removeEventListener(`mousemove`,t),document.removeEventListener(`mouseup`,n)};document.addEventListener(`mousemove`,t),document.addEventListener(`mouseup`,n)},se=r?.totalTokens??0,ce=r?.totalCalls??0,R=r?.avgDuration??0,z=e.reduce((e,t)=>e+(t.usage?.promptTokens||0),0),B=e.reduce((e,t)=>e+(t.usage?.completionTokens||0),0),H=f===`all`?e:e.filter(e=>e.status===f),U={};for(let t of e)t.model&&(U[t.model]=(U[t.model]||0)+1);let W=Object.entries(U).sort((e,t)=>t[1]-e[1]).slice(0,4),[J,de]=(0,V.useState)(null),fe=J?H.filter(e=>e.model===J):H,pe=e.filter(e=>e.status===`error`).length;return(0,K.jsxs)(`div`,{className:`flex flex-col h-screen overflow-hidden`,style:{background:`var(--bg)`,color:`var(--text)`},children:[(0,K.jsxs)(`header`,{className:`flex items-center gap-3 px-4 flex-shrink-0`,style:{background:`var(--bg-card)`,borderBottom:`1px solid var(--border)`,height:`52px`,zIndex:10},children:[(0,K.jsxs)(`div`,{className:`flex items-center gap-2 flex-shrink-0`,children:[(0,K.jsx)(n,{size:18,style:{color:`var(--blue)`}}),(0,K.jsx)(`span`,{className:`text-sm font-semibold tracking-tight`,children:`LPT`})]}),(0,K.jsx)(`div`,{className:`w-px h-5 flex-shrink-0`,style:{background:`var(--border)`}}),(0,K.jsxs)(`div`,{className:`flex items-center gap-3 text-[11px] flex-shrink-0`,style:{color:`var(--text-tertiary)`},children:[(0,K.jsxs)(`span`,{children:[`请求 `,(0,K.jsx)(`b`,{className:`font-mono`,style:{color:`var(--text-secondary)`},children:ce})]}),(0,K.jsxs)(`span`,{children:[`Tokens `,(0,K.jsx)(`b`,{className:`font-mono`,style:{color:`var(--text-secondary)`},children:se.toLocaleString()})]}),z>0&&(0,K.jsxs)(`span`,{className:`flex items-center gap-0.5 font-mono`,style:{color:`var(--blue)`},children:[`↑`,(0,K.jsx)(`b`,{children:z.toLocaleString()})]}),B>0&&(0,K.jsxs)(`span`,{className:`flex items-center gap-0.5 font-mono`,style:{color:`var(--green)`},children:[`↓`,(0,K.jsx)(`b`,{children:B.toLocaleString()})]}),R>0&&(0,K.jsxs)(`span`,{children:[`均 `,R,`ms`]})]}),W.length>0&&(0,K.jsxs)(K.Fragment,{children:[(0,K.jsx)(`div`,{className:`w-px h-5 flex-shrink-0`,style:{background:`var(--border)`}}),(0,K.jsx)(`div`,{className:`flex items-center gap-1.5 overflow-hidden flex-1`,children:W.map(([e,t])=>(0,K.jsxs)(`button`,{onClick:()=>de(t=>t===e?null:e),className:`filter-chip${J===e?` active`:``}`,children:[e.split(`/`).pop()||e,(0,K.jsx)(`span`,{className:`chip-count`,children:t})]},e))})]}),(0,K.jsxs)(`div`,{className:`ml-auto flex items-center gap-2 flex-shrink-0`,children:[(0,K.jsxs)(`span`,{className:`text-[11px] flex items-center gap-1`,style:{color:I?`var(--green)`:`var(--red)`},children:[(0,K.jsx)(`span`,{className:`w-1.5 h-1.5 rounded-full`,style:{background:I?`var(--green)`:`var(--red)`}}),I?`实时`:`离线`]}),(0,K.jsx)(q,{content:`从磁盘加载历史记录`,children:(0,K.jsxs)(`button`,{onClick:async()=>{M(!0),await G.reloadTraces().catch(()=>{}),await P(),M(!1)},className:`flex items-center gap-1 text-[11px] px-2 py-1 rounded border transition-colors`,style:{borderColor:`var(--border)`,color:j?`var(--blue)`:`var(--text-tertiary)`},disabled:j,children:[(0,K.jsx)(l,{size:12,style:{animation:j?`spin 1s linear infinite`:`none`}}),`加载记录`]})}),(0,K.jsxs)(`div`,{ref:A,className:`clear-split-btn`,style:{position:`relative`},children:[(0,K.jsx)(q,{content:`清空当前会话`,children:(0,K.jsxs)(`button`,{onClick:()=>{G.deleteTraces(!1).then(()=>{t([]),P()}),k(!1)},className:`clear-split-main`,children:[(0,K.jsx)(p,{size:12}),` 清空`]})}),(0,K.jsx)(`button`,{onClick:()=>k(e=>!e),className:`clear-split-arrow`,"aria-label":`更多清空选项`,children:(0,K.jsx)(o,{size:11})}),O&&(0,K.jsxs)(`div`,{className:`clear-dropdown`,children:[(0,K.jsx)(`div`,{className:`clear-dropdown-label`,children:`选择清空方式`}),(0,K.jsxs)(`button`,{className:`clear-dropdown-item`,onClick:()=>{G.deleteTraces(!1).then(()=>{t([]),P()}),k(!1)},children:[(0,K.jsx)(p,{size:12}),(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`div`,{className:`clear-dropdown-item-title`,children:`清空当前会话`}),(0,K.jsx)(`div`,{className:`clear-dropdown-item-desc`,children:`仅清内存,文件保留,重启可恢复`})]})]}),(0,K.jsxs)(`button`,{className:`clear-dropdown-item danger`,onClick:()=>{k(!1),b(`永久删除所有文件`,`将永久删除所有 Trace 文件,无法恢复,确认吗?`,()=>{G.deleteTraces(!0).then(()=>{t([]),P()})})},children:[(0,K.jsx)(p,{size:12}),(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`div`,{className:`clear-dropdown-item-title`,children:`永久删除所有文件`}),(0,K.jsx)(`div`,{className:`clear-dropdown-item-desc`,children:`删除磁盘 JSONL,重启不恢复`})]})]})]})]}),(0,K.jsx)(q,{content:S===`dark`?`浅色模式`:`深色模式`,children:(0,K.jsx)(`button`,{onClick:oe,className:`flex items-center justify-center w-7 h-7 rounded border transition-colors`,style:{borderColor:`var(--border)`,color:`var(--text-tertiary)`},children:S===`dark`?(0,K.jsx)(s,{size:13}):(0,K.jsx)(ie,{size:13})})}),(0,K.jsx)(q,{content:`Provider 与代理设置`,children:(0,K.jsxs)(`button`,{onClick:()=>g(!h),className:`flex items-center gap-1 text-[11px] px-2 py-1 rounded border transition-colors`,style:{borderColor:h?`var(--blue)`:`var(--border)`,color:h?`var(--blue)`:`var(--text-tertiary)`,background:h?`var(--blue-bg)`:`transparent`},children:[(0,K.jsx)(re,{size:12}),` 设置`]})})]})]}),(0,K.jsxs)(`div`,{className:`flex flex-1 overflow-hidden relative`,children:[h&&(0,K.jsx)(`div`,{className:`absolute inset-0 z-20 flex`,style:{background:`rgba(0,0,0,0.15)`},onClick:()=>g(!1),children:(0,K.jsx)(`div`,{className:`ml-auto h-full overflow-auto`,style:{width:`420px`,background:`var(--bg-card)`,borderLeft:`1px solid var(--border)`},onClick:e=>e.stopPropagation(),children:(0,K.jsx)(Pe,{onClose:()=>g(!1)})})}),(0,K.jsxs)(`div`,{className:`flex flex-col flex-shrink-0`,style:{width:`${C}px`,borderRight:`1px solid var(--border)`,background:`var(--bg-card)`},children:[(0,K.jsxs)(`div`,{className:`flex items-center gap-2 px-3 py-2 border-b flex-shrink-0`,style:{borderColor:`var(--border)`},children:[(0,K.jsx)(ae,{size:13,style:{color:`var(--text-tertiary)`}}),(0,K.jsx)(`input`,{type:`text`,placeholder:`搜索消息、模型…`,value:u,onChange:e=>d(e.target.value),className:`flex-1 text-xs bg-transparent outline-none`,style:{color:`var(--text)`}}),u&&(0,K.jsx)(`button`,{onClick:()=>d(``),style:{color:`var(--text-tertiary)`},children:(0,K.jsx)(v,{size:11})}),(0,K.jsx)(`div`,{className:`flex items-center gap-1 flex-shrink-0`,children:[`all`,`error`].map(t=>(0,K.jsx)(q,{content:t===`all`?`显示全部请求`:`只看报错的请求`,placement:`bottom`,delay:400,children:(0,K.jsx)(`button`,{onClick:()=>m(t),className:`filter-chip${f===t?` active`:``}`,style:{fontSize:`10px`,padding:`2px 7px`},children:t===`all`?`全部 ${e.length}`:`✕ ${pe}`})},t))})]}),(0,K.jsx)(ue,{traces:fe,activeId:a,onSelect:e=>{c(e)},loading:ee,onDeleteFile:n=>{b(`删除记录文件`,`删除 ${n}?此操作不可恢复。`,async()=>{await G.deleteTraceFile(n).catch(()=>{}),t(e=>e.filter(e=>e.sourceFile!==n)),a&&e.find(e=>e.id===a)?.sourceFile===n&&c(null)})}})]}),(0,K.jsx)(`div`,{className:`resize-handle`,onMouseDown:L,style:{background:`var(--border-light)`}}),(0,K.jsx)(`div`,{className:`flex-1 overflow-auto detail-scroll`,style:{background:`var(--bg)`},children:N?(0,K.jsx)(ge,{trace:N},N.id):(0,K.jsx)(ze,{hasTraces:e.length>0})})]}),(0,K.jsx)(Fe,{open:_.open,title:_.title,message:_.message,confirmText:`确认删除`,danger:!0,onConfirm:()=>{x(),_.onConfirm()},onCancel:x})]})}function ze({hasTraces:e}){let t=window.location.port,r=t?`http://localhost:${t}`:window.location.origin;return e?(0,K.jsx)(`div`,{className:`flex items-center justify-center h-full text-sm`,style:{color:`var(--text-tertiary)`},children:`选择左侧一条请求查看详情`}):(0,K.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-full gap-5`,style:{padding:`40px`},children:[(0,K.jsx)(n,{size:28,style:{color:`var(--border)`}}),(0,K.jsxs)(`div`,{style:{textAlign:`center`},children:[(0,K.jsx)(`div`,{style:{fontSize:`15px`,fontWeight:600,color:`var(--text)`,marginBottom:`6px`},children:`等待第一条请求…`}),(0,K.jsx)(`div`,{style:{fontSize:`12px`,color:`var(--text-tertiary)`,lineHeight:1.7},children:`设置以下环境变量,所有兼容 OpenAI 接口的工具都会被自动拦截`})]}),(0,K.jsxs)(`div`,{style:{background:`var(--bg-code)`,border:`1px solid var(--border)`,borderRadius:`8px`,padding:`14px 18px`,fontFamily:`var(--mono)`,fontSize:`13px`,lineHeight:2,minWidth:`340px`},children:[(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`span`,{style:{color:`var(--amber)`},children:`export `}),(0,K.jsx)(`span`,{style:{color:`var(--blue)`},children:`OPENAI_BASE_URL`}),(0,K.jsx)(`span`,{style:{color:`var(--text-tertiary)`},children:`=`}),(0,K.jsxs)(`span`,{style:{color:`var(--green)`},children:[`"`,r,`/v1"`]})]}),(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`span`,{style:{color:`var(--amber)`},children:`export `}),(0,K.jsx)(`span`,{style:{color:`var(--blue)`},children:`OPENAI_API_KEY`}),(0,K.jsx)(`span`,{style:{color:`var(--text-tertiary)`},children:`=`}),(0,K.jsx)(`span`,{style:{color:`var(--green)`},children:`"any-key"`}),(0,K.jsx)(`span`,{style:{color:`var(--text-tertiary)`,fontStyle:`italic`,fontSize:`11px`},children:` # 代理不验证 key`})]})]}),(0,K.jsxs)(`div`,{style:{fontSize:`11px`,color:`var(--text-tertiary)`,lineHeight:1.7,textAlign:`center`,maxWidth:`320px`},children:[`上游 API 地址在 `,(0,K.jsx)(`span`,{style:{color:`var(--blue)`},children:`设置`}),` 中配置。 记录内容包括流式输出、工具调用与思考过程。`]})]})}H.createRoot(document.getElementById(`root`)).render((0,K.jsx)(V.StrictMode,{children:(0,K.jsx)(Re,{})}));
21
+ `):null}function Ne(e){let t=e.response.body;if(!t)return null;if(t.choices&&Array.isArray(t.choices)&&t.choices.length>0){let e=t.choices[0].message;if(e){let t=e.content,n=e.reasoning_content,r=[];if(n&&r.push({type:`thinking`,thinking:n}),typeof t==`string`&&t.trim()?r.push({type:`text`,text:t}):Array.isArray(t)?r.push(...t):t!=null&&r.push({type:`text`,text:JSON.stringify(t)}),r.length===1&&r[0].type===`text`)return r[0].text||null;if(r.length>0)return r}return null}if(t.content&&Array.isArray(t.content)){let e=t.content;return e.length===1&&e[0].type===`text`?e[0].text||null:e}return typeof t.text==`string`?t.text:null}function Pe({onClose:e}){let[t,n]=(0,V.useState)([]),[r,i]=(0,V.useState)(!0),[a,o]=(0,V.useState)(null);(0,V.useEffect)(()=>{Promise.all([G.getProviders().then(n).catch(()=>{}),G.getConfig().then(e=>o(e.proxy?.port??null)).catch(()=>{})]).finally(()=>i(!1))},[]);let s=a??(window.location.port?Number(window.location.port):void 0);return(0,K.jsxs)(`div`,{className:`p-5 space-y-5`,children:[(0,K.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,K.jsx)(`h2`,{className:`text-base font-semibold`,children:`设置`}),(0,K.jsx)(`button`,{onClick:e,style:{color:`var(--text-tertiary)`},children:(0,K.jsx)(v,{size:16})})]}),(0,K.jsxs)(`div`,{className:`rounded-lg p-3`,style:{background:`var(--bg)`,border:`1px solid var(--border)`},children:[(0,K.jsx)(`h3`,{className:`text-sm font-medium mb-2`,children:`代理服务器`}),(0,K.jsxs)(`div`,{className:`grid grid-cols-2 gap-x-4 gap-y-1.5 text-xs`,children:[(0,K.jsx)(`span`,{style:{color:`var(--text-tertiary)`},children:`监听端口`}),(0,K.jsx)(`span`,{className:`font-mono`,style:{color:`var(--blue)`},children:s}),(0,K.jsx)(`span`,{style:{color:`var(--text-tertiary)`},children:`OpenAI Base URL`}),(0,K.jsxs)(`span`,{className:`font-mono`,style:{color:`var(--blue)`},children:[`http://localhost:`,s,`/v1`]}),(0,K.jsx)(`span`,{style:{color:`var(--text-tertiary)`},children:`Anthropic Base URL`}),(0,K.jsxs)(`span`,{className:`font-mono`,style:{color:`var(--blue)`},children:[`http://localhost:`,s]})]})]}),(0,K.jsxs)(`div`,{className:`rounded-lg p-3`,style:{background:`var(--bg)`,border:`1px solid var(--border)`},children:[(0,K.jsx)(`h3`,{className:`text-sm font-medium mb-1`,children:`上游服务商`}),(0,K.jsxs)(`p`,{className:`text-[11px] mb-3`,style:{color:`var(--text-tertiary)`},children:[`修改请编辑项目根目录的 `,(0,K.jsx)(`code`,{className:`font-mono px-1 py-0.5 rounded`,style:{background:`var(--bg-code)`},children:`.env`}),` 文件后重启服务`]}),r?(0,K.jsx)(`div`,{className:`text-sm`,style:{color:`var(--text-tertiary)`},children:`加载中…`}):t.length===0?(0,K.jsxs)(`div`,{className:`text-sm italic`,style:{color:`var(--text-tertiary)`},children:[`未检测到已配置的 Provider。请在 `,(0,K.jsx)(`code`,{className:`font-mono`,children:`.env`}),` 中设置 `,(0,K.jsx)(`code`,{className:`font-mono`,children:`OPENAI_API_KEY`}),` 等变量。`]}):(0,K.jsx)(`div`,{className:`space-y-1.5`,children:t.map(e=>(0,K.jsxs)(`div`,{className:`flex items-center gap-3 px-3 py-2 rounded`,style:{border:`1px solid var(--border)`},children:[(0,K.jsx)(`span`,{className:`w-1.5 h-1.5 rounded-full flex-shrink-0`,style:{background:`var(--green)`}}),(0,K.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,K.jsx)(`div`,{className:`text-sm font-medium capitalize`,children:e.type}),(0,K.jsx)(`div`,{className:`text-[11px] font-mono truncate`,style:{color:`var(--text-tertiary)`},children:e.baseUrl})]}),e.apiKey&&(0,K.jsx)(`span`,{className:`text-[11px] font-mono flex-shrink-0`,style:{color:`var(--text-tertiary)`},children:e.apiKey})]},e.type))})]}),(0,K.jsxs)(`div`,{className:`flex items-center justify-end gap-3 pt-1`,style:{color:`var(--text-tertiary)`,fontSize:`11px`},children:[(0,K.jsxs)(`span`,{children:[`LPT v`,`1.3.13`]}),(0,K.jsx)(`span`,{style:{opacity:.35},children:`·`}),(0,K.jsx)(`a`,{href:`https://github.com/zhxsking/llm_proxy_trace`,target:`_blank`,rel:`noopener noreferrer`,style:{color:`var(--text-tertiary)`,textDecoration:`none`},onMouseEnter:e=>e.currentTarget.style.color=`var(--text-secondary)`,onMouseLeave:e=>e.currentTarget.style.color=`var(--text-tertiary)`,children:`GitHub`})]})]})}function Fe({open:e,title:t,message:n,confirmText:r=`确认`,cancelText:i=`取消`,danger:a=!1,onConfirm:o,onCancel:s}){let c=(0,V.useRef)(null);return(0,V.useEffect)(()=>{e&&c.current?.focus()},[e]),(0,V.useEffect)(()=>{if(!e)return;let t=e=>{e.key===`Escape`&&s(),e.key===`Enter`&&o()};return window.addEventListener(`keydown`,t),()=>window.removeEventListener(`keydown`,t)},[e,o,s]),e?(0,K.jsx)(`div`,{className:`confirm-overlay`,onClick:s,children:(0,K.jsxs)(`div`,{className:`confirm-dialog`,onClick:e=>e.stopPropagation(),children:[(0,K.jsx)(`div`,{className:`confirm-icon-row`,children:(0,K.jsx)(`span`,{className:`confirm-icon${a?` danger`:``}`,children:(0,K.jsx)(m,{size:20})})}),(0,K.jsx)(`div`,{className:`confirm-title`,children:t}),(0,K.jsx)(`div`,{className:`confirm-message`,children:n}),(0,K.jsxs)(`div`,{className:`confirm-actions`,children:[(0,K.jsx)(`button`,{className:`confirm-btn cancel`,onClick:s,children:i}),(0,K.jsx)(`button`,{ref:c,className:`confirm-btn ok${a?` danger`:``}`,onClick:o,children:r})]})]})}):null}function Ie(){try{let e=localStorage.getItem(`lpt-theme`);if(e===`dark`||e===`light`)return e}catch{}return window.matchMedia(`(prefers-color-scheme: dark)`).matches?`dark`:`light`}function Le(e){document.documentElement.setAttribute(`data-theme`,e);try{localStorage.setItem(`lpt-theme`,e)}catch{}}function Re(){let[e,t]=(0,V.useState)([]),[r,i]=(0,V.useState)(null),[a,c]=(0,V.useState)(null),[u,d]=(0,V.useState)(``),[f,m]=(0,V.useState)(`all`),[h,g]=(0,V.useState)(!1),[_,y]=(0,V.useState)({open:!1,title:``,message:``,onConfirm:()=>{}}),b=(e,t,n)=>{y({open:!0,title:e,message:t,onConfirm:n})},x=()=>y(e=>({...e,open:!1})),[ee,te]=(0,V.useState)(!0),[S,ne]=(0,V.useState)(Ie),[C,w]=(0,V.useState)(320),T=(0,V.useRef)(!1),E=(0,V.useRef)(0),D=(0,V.useRef)(0),[O,k]=(0,V.useState)(!1),A=(0,V.useRef)(null),[j,M]=(0,V.useState)(!1);(0,V.useEffect)(()=>{Le(S)},[S]),(0,V.useEffect)(()=>{if(!O)return;let e=e=>{A.current&&!A.current.contains(e.target)&&k(!1)};return document.addEventListener(`mousedown`,e),()=>document.removeEventListener(`mousedown`,e)},[O]);let oe=()=>ne(e=>e===`light`?`dark`:`light`),N=e.find(e=>e.id===a)||null,P=(0,V.useCallback)(async()=>{try{let[e,n]=await Promise.all([G.getTraces({keyword:u||void 0,limit:200}),G.getStats()]);t(e.traces),i(n)}catch{}finally{te(!1)}},[u]),F=(0,V.useCallback)(e=>{if(e.type===`trace:new`){let n=e.data;u||t(e=>[n,...e]),i(e=>e&&{...e,totalCalls:e.totalCalls+1})}else if(e.type===`trace:update`||e.type===`trace:complete`){let n=e.data;t(e=>e.map(e=>e.id===n.id?{...e,...n}:e)),e.type===`trace:complete`&&G.getStats().then(i).catch(()=>{})}},[u]),{connected:I}=le({url:`ws://${window.location.host}/ws`,onEvent:F});(0,V.useEffect)(()=>{P()},[P]);let L=e=>{T.current=!0,E.current=e.clientX,D.current=C,document.body.style.userSelect=`none`,document.body.style.cursor=`col-resize`;let t=e=>{if(!T.current)return;let t=e.clientX-E.current;w(Math.max(240,Math.min(600,D.current+t)))},n=()=>{T.current=!1,document.body.style.userSelect=``,document.body.style.cursor=``,document.removeEventListener(`mousemove`,t),document.removeEventListener(`mouseup`,n)};document.addEventListener(`mousemove`,t),document.addEventListener(`mouseup`,n)},se=r?.totalTokens??0,ce=r?.totalCalls??0,R=r?.avgDuration??0,z=e.reduce((e,t)=>e+(t.usage?.promptTokens||0),0),B=e.reduce((e,t)=>e+(t.usage?.completionTokens||0),0),H=f===`all`?e:e.filter(e=>e.status===f),U={};for(let t of e)t.model&&(U[t.model]=(U[t.model]||0)+1);let W=Object.entries(U).sort((e,t)=>t[1]-e[1]).slice(0,4),[J,de]=(0,V.useState)(null),fe=J?H.filter(e=>e.model===J):H,pe=e.filter(e=>e.status===`error`).length;return(0,K.jsxs)(`div`,{className:`flex flex-col h-screen overflow-hidden`,style:{background:`var(--bg)`,color:`var(--text)`},children:[(0,K.jsxs)(`header`,{className:`flex items-center gap-3 px-4 flex-shrink-0`,style:{background:`var(--bg-card)`,borderBottom:`1px solid var(--border)`,height:`52px`,zIndex:10},children:[(0,K.jsxs)(`div`,{className:`flex items-center gap-2 flex-shrink-0`,children:[(0,K.jsx)(n,{size:18,style:{color:`var(--blue)`}}),(0,K.jsx)(`span`,{className:`text-sm font-semibold tracking-tight`,children:`LPT`})]}),(0,K.jsx)(`div`,{className:`w-px h-5 flex-shrink-0`,style:{background:`var(--border)`}}),(0,K.jsxs)(`div`,{className:`flex items-center gap-3 text-[11px] flex-shrink-0`,style:{color:`var(--text-tertiary)`},children:[(0,K.jsxs)(`span`,{children:[`请求 `,(0,K.jsx)(`b`,{className:`font-mono`,style:{color:`var(--text-secondary)`},children:ce})]}),(0,K.jsxs)(`span`,{children:[`Tokens `,(0,K.jsx)(`b`,{className:`font-mono`,style:{color:`var(--text-secondary)`},children:se.toLocaleString()})]}),z>0&&(0,K.jsxs)(`span`,{className:`flex items-center gap-0.5 font-mono`,style:{color:`var(--blue)`},children:[`↑`,(0,K.jsx)(`b`,{children:z.toLocaleString()})]}),B>0&&(0,K.jsxs)(`span`,{className:`flex items-center gap-0.5 font-mono`,style:{color:`var(--green)`},children:[`↓`,(0,K.jsx)(`b`,{children:B.toLocaleString()})]}),R>0&&(0,K.jsxs)(`span`,{children:[`均 `,R,`ms`]})]}),W.length>0&&(0,K.jsxs)(K.Fragment,{children:[(0,K.jsx)(`div`,{className:`w-px h-5 flex-shrink-0`,style:{background:`var(--border)`}}),(0,K.jsx)(`div`,{className:`flex items-center gap-1.5 overflow-hidden flex-1`,children:W.map(([e,t])=>(0,K.jsxs)(`button`,{onClick:()=>de(t=>t===e?null:e),className:`filter-chip${J===e?` active`:``}`,children:[e.split(`/`).pop()||e,(0,K.jsx)(`span`,{className:`chip-count`,children:t})]},e))})]}),(0,K.jsxs)(`div`,{className:`ml-auto flex items-center gap-2 flex-shrink-0`,children:[(0,K.jsxs)(`span`,{className:`text-[11px] flex items-center gap-1`,style:{color:I?`var(--green)`:`var(--red)`},children:[(0,K.jsx)(`span`,{className:`w-1.5 h-1.5 rounded-full`,style:{background:I?`var(--green)`:`var(--red)`}}),I?`实时`:`离线`]}),(0,K.jsx)(q,{content:`从磁盘加载历史记录`,children:(0,K.jsxs)(`button`,{onClick:async()=>{M(!0),await G.reloadTraces().catch(()=>{}),await P(),M(!1)},className:`flex items-center gap-1 text-[11px] px-2 py-1 rounded border transition-colors`,style:{borderColor:`var(--border)`,color:j?`var(--blue)`:`var(--text-tertiary)`},disabled:j,children:[(0,K.jsx)(l,{size:12,style:{animation:j?`spin 1s linear infinite`:`none`}}),`加载记录`]})}),(0,K.jsxs)(`div`,{ref:A,className:`clear-split-btn`,style:{position:`relative`},children:[(0,K.jsx)(q,{content:`清空当前会话`,children:(0,K.jsxs)(`button`,{onClick:()=>{G.deleteTraces(!1).then(()=>{t([]),P()}),k(!1)},className:`clear-split-main`,children:[(0,K.jsx)(p,{size:12}),` 清空`]})}),(0,K.jsx)(`button`,{onClick:()=>k(e=>!e),className:`clear-split-arrow`,"aria-label":`更多清空选项`,children:(0,K.jsx)(o,{size:11})}),O&&(0,K.jsxs)(`div`,{className:`clear-dropdown`,children:[(0,K.jsx)(`div`,{className:`clear-dropdown-label`,children:`选择清空方式`}),(0,K.jsxs)(`button`,{className:`clear-dropdown-item`,onClick:()=>{G.deleteTraces(!1).then(()=>{t([]),P()}),k(!1)},children:[(0,K.jsx)(p,{size:12}),(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`div`,{className:`clear-dropdown-item-title`,children:`清空当前会话`}),(0,K.jsx)(`div`,{className:`clear-dropdown-item-desc`,children:`仅清内存,文件保留,重启可恢复`})]})]}),(0,K.jsxs)(`button`,{className:`clear-dropdown-item danger`,onClick:()=>{k(!1),b(`永久删除所有文件`,`将永久删除所有 Trace 文件,无法恢复,确认吗?`,()=>{G.deleteTraces(!0).then(()=>{t([]),P()})})},children:[(0,K.jsx)(p,{size:12}),(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`div`,{className:`clear-dropdown-item-title`,children:`永久删除所有文件`}),(0,K.jsx)(`div`,{className:`clear-dropdown-item-desc`,children:`删除磁盘 JSONL,重启不恢复`})]})]})]})]}),(0,K.jsx)(q,{content:S===`dark`?`浅色模式`:`深色模式`,children:(0,K.jsx)(`button`,{onClick:oe,className:`flex items-center justify-center w-7 h-7 rounded border transition-colors`,style:{borderColor:`var(--border)`,color:`var(--text-tertiary)`},children:S===`dark`?(0,K.jsx)(s,{size:13}):(0,K.jsx)(ie,{size:13})})}),(0,K.jsx)(q,{content:`Provider 与代理设置`,children:(0,K.jsxs)(`button`,{onClick:()=>g(!h),className:`flex items-center gap-1 text-[11px] px-2 py-1 rounded border transition-colors`,style:{borderColor:h?`var(--blue)`:`var(--border)`,color:h?`var(--blue)`:`var(--text-tertiary)`,background:h?`var(--blue-bg)`:`transparent`},children:[(0,K.jsx)(re,{size:12}),` 设置`]})})]})]}),(0,K.jsxs)(`div`,{className:`flex flex-1 overflow-hidden relative`,children:[h&&(0,K.jsx)(`div`,{className:`absolute inset-0 z-20 flex`,style:{background:`rgba(0,0,0,0.15)`},onClick:()=>g(!1),children:(0,K.jsx)(`div`,{className:`ml-auto h-full overflow-auto`,style:{width:`420px`,background:`var(--bg-card)`,borderLeft:`1px solid var(--border)`},onClick:e=>e.stopPropagation(),children:(0,K.jsx)(Pe,{onClose:()=>g(!1)})})}),(0,K.jsxs)(`div`,{className:`flex flex-col flex-shrink-0`,style:{width:`${C}px`,borderRight:`1px solid var(--border)`,background:`var(--bg-card)`},children:[(0,K.jsxs)(`div`,{className:`flex items-center gap-2 px-3 py-2 border-b flex-shrink-0`,style:{borderColor:`var(--border)`},children:[(0,K.jsx)(ae,{size:13,style:{color:`var(--text-tertiary)`}}),(0,K.jsx)(`input`,{type:`text`,placeholder:`搜索消息、模型…`,value:u,onChange:e=>d(e.target.value),className:`flex-1 text-xs bg-transparent outline-none`,style:{color:`var(--text)`}}),u&&(0,K.jsx)(`button`,{onClick:()=>d(``),style:{color:`var(--text-tertiary)`},children:(0,K.jsx)(v,{size:11})}),(0,K.jsx)(`div`,{className:`flex items-center gap-1 flex-shrink-0`,children:[`all`,`error`].map(t=>(0,K.jsx)(q,{content:t===`all`?`显示全部请求`:`只看报错的请求`,placement:`bottom`,delay:400,children:(0,K.jsx)(`button`,{onClick:()=>m(t),className:`filter-chip${f===t?` active`:``}`,style:{fontSize:`10px`,padding:`2px 7px`},children:t===`all`?`全部 ${e.length}`:`✕ ${pe}`})},t))})]}),(0,K.jsx)(ue,{traces:fe,activeId:a,onSelect:e=>{c(e)},loading:ee,onDeleteFile:n=>{b(`删除记录文件`,`删除 ${n}?此操作不可恢复。`,async()=>{await G.deleteTraceFile(n).catch(()=>{}),t(e=>e.filter(e=>e.sourceFile!==n)),a&&e.find(e=>e.id===a)?.sourceFile===n&&c(null)})}})]}),(0,K.jsx)(`div`,{className:`resize-handle`,onMouseDown:L,style:{background:`var(--border-light)`}}),(0,K.jsx)(`div`,{className:`flex-1 overflow-auto detail-scroll`,style:{background:`var(--bg)`},children:N?(0,K.jsx)(ge,{trace:N},N.id):(0,K.jsx)(ze,{hasTraces:e.length>0})})]}),(0,K.jsx)(Fe,{open:_.open,title:_.title,message:_.message,confirmText:`确认删除`,danger:!0,onConfirm:()=>{x(),_.onConfirm()},onCancel:x})]})}function ze({hasTraces:e}){let t=window.location.port,r=t?`http://localhost:${t}`:window.location.origin;return e?(0,K.jsx)(`div`,{className:`flex items-center justify-center h-full text-sm`,style:{color:`var(--text-tertiary)`},children:`选择左侧一条请求查看详情`}):(0,K.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-full gap-5`,style:{padding:`40px`},children:[(0,K.jsx)(n,{size:28,style:{color:`var(--border)`}}),(0,K.jsxs)(`div`,{style:{textAlign:`center`},children:[(0,K.jsx)(`div`,{style:{fontSize:`15px`,fontWeight:600,color:`var(--text)`,marginBottom:`6px`},children:`等待第一条请求…`}),(0,K.jsx)(`div`,{style:{fontSize:`12px`,color:`var(--text-tertiary)`,lineHeight:1.7},children:`设置以下环境变量,所有兼容 OpenAI 接口的工具都会被自动拦截`})]}),(0,K.jsxs)(`div`,{style:{background:`var(--bg-code)`,border:`1px solid var(--border)`,borderRadius:`8px`,padding:`14px 18px`,fontFamily:`var(--mono)`,fontSize:`13px`,lineHeight:2,minWidth:`340px`},children:[(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`span`,{style:{color:`var(--amber)`},children:`export `}),(0,K.jsx)(`span`,{style:{color:`var(--blue)`},children:`OPENAI_BASE_URL`}),(0,K.jsx)(`span`,{style:{color:`var(--text-tertiary)`},children:`=`}),(0,K.jsxs)(`span`,{style:{color:`var(--green)`},children:[`"`,r,`/v1"`]})]}),(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`span`,{style:{color:`var(--amber)`},children:`export `}),(0,K.jsx)(`span`,{style:{color:`var(--blue)`},children:`OPENAI_API_KEY`}),(0,K.jsx)(`span`,{style:{color:`var(--text-tertiary)`},children:`=`}),(0,K.jsx)(`span`,{style:{color:`var(--green)`},children:`"any-key"`}),(0,K.jsx)(`span`,{style:{color:`var(--text-tertiary)`,fontStyle:`italic`,fontSize:`11px`},children:` # 代理不验证 key`})]})]}),(0,K.jsxs)(`div`,{style:{fontSize:`11px`,color:`var(--text-tertiary)`,lineHeight:1.7,textAlign:`center`,maxWidth:`320px`},children:[`上游 API 地址在 `,(0,K.jsx)(`span`,{style:{color:`var(--blue)`},children:`设置`}),` 中配置。 记录内容包括流式输出、工具调用与思考过程。`]})]})}H.createRoot(document.getElementById(`root`)).render((0,K.jsx)(V.StrictMode,{children:(0,K.jsx)(Re,{})}));
@@ -6,7 +6,7 @@
6
6
  <title>LLM Proxy Trace</title>
7
7
 
8
8
  <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%233b82f6' stroke-width='2'><circle cx='12' cy='12' r='10'/><path d='M12 6v6l4 2'/></svg>" />
9
- <script type="module" crossorigin src="/assets/index-DMLqX4dJ.js"></script>
9
+ <script type="module" crossorigin src="/assets/index-BhbzSfr_.js"></script>
10
10
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-QTnfLwEv.js">
11
11
  <link rel="modulepreload" crossorigin href="/assets/markdown-DsSlw-Z4.js">
12
12
  <link rel="modulepreload" crossorigin href="/assets/react-vendor-BnH7jZBV.js">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "llmpt",
3
- "version": "1.3.12",
3
+ "version": "1.3.13",
4
4
  "description": "LLM Proxy Trace - local LLM proxy that captures full traces with real-time Web dashboard",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",