diff --git a/examples/server/public/index.html.gz b/examples/server/public/index.html.gz
index d358fdf78..207988144 100644
Binary files a/examples/server/public/index.html.gz and b/examples/server/public/index.html.gz differ
diff --git a/examples/server/server.cpp b/examples/server/server.cpp
index 42f0b17bd..448244af3 100644
--- a/examples/server/server.cpp
+++ b/examples/server/server.cpp
@@ -2889,7 +2889,11 @@ static std::vector format_partial_response_oaicompat(server_task_result ta
};
streaming_chunks.push_back(finish_chunk);
}
-
+ if (server_task_result_dict.count(task_result.id) > 0)
+ {
+ for (auto& chunk : streaming_chunks)
+ chunk.push_back({ "timings", server_task_result_dict[task_result.id].timings.to_json() });
+ }
// Return streaming chunks (could be just final chunk if no diffs)
if (!streaming_chunks.empty()) {
return streaming_chunks;
diff --git a/examples/server/webui/dist/index.html b/examples/server/webui/dist/index.html
index 0b2aaa30a..6767625b7 100644
--- a/examples/server/webui/dist/index.html
+++ b/examples/server/webui/dist/index.html
@@ -609,8 +609,8 @@
interruptBuffer[0] = 0;
};
`;let C0;const ou=Uv?new Uint8Array(new SharedArrayBuffer(1)):null,jv=()=>{C0||(C0=new Worker(URL.createObjectURL(new Blob([cC],{type:"text/javascript"}))))};Zt.getConfig().pyIntepreterEnabled&&jv();const fC=(e,t)=>{jv();const n=Math.random()*1e8,r={};return ou&&(ou[0]=0),{donePromise:new Promise(s=>{C0.onmessage=c=>{const{error:f,stdOutAndErr:d,running:m}=c.data;if(n===c.data.id)if(m){t();return}else s(f?f.toString():d.join(`
-`))},C0.postMessage({id:n,python:e,context:r,interruptBuffer:ou})}),interrupt:()=>{console.log("Interrupting..."),console.trace(),ou&&(ou[0]=2)}}};function dC(){const{canvasData:e,setCanvasData:t}=_a(),[n,r]=Y.useState((e==null?void 0:e.content)??""),[a,l]=Y.useState(!1),[s,c]=Y.useState(""),[f,d]=Y.useState(),[m,g]=Y.useState(!1),w=async b=>{f==null||f(),l(!0),c("Loading Pyodide...");const{donePromise:_,interrupt:T}=fC(b,()=>{c("Running..."),g(Uv)});d(()=>T);const D=await _;c(D),l(!1),g(!1)};return Y.useEffect(()=>{r((e==null?void 0:e.content)??""),w((e==null?void 0:e.content)??"")},[e==null?void 0:e.content]),(e==null?void 0:e.type)!==G0.PY_INTERPRETER?null:j.jsx("div",{className:"card bg-base-200 w-full h-full shadow-xl",children:j.jsxs("div",{className:"card-body",children:[j.jsxs("div",{className:"flex justify-between items-center mb-4",children:[j.jsx("span",{className:"text-lg font-bold",children:"Python Interpreter"}),j.jsx(d7,{className:"bg-base-100",onClick:()=>t(null)})]}),j.jsxs("div",{className:"grid grid-rows-3 gap-4 h-full",children:[j.jsx("textarea",{className:"textarea textarea-bordered w-full h-full font-mono",value:n,onChange:b=>r(b.target.value)}),j.jsxs("div",{className:"font-mono flex flex-col row-span-2",children:[j.jsxs("div",{className:"flex items-center mb-2",children:[j.jsxs("button",{className:"btn btn-sm bg-base-100",onClick:()=>w(n),disabled:a,children:[j.jsx(r7,{className:"h-6 w-6"})," Run"]}),m&&j.jsxs("button",{className:"btn btn-sm bg-base-100 ml-2",onClick:()=>f==null?void 0:f(),children:[j.jsx(l7,{className:"h-6 w-6"})," Stop"]}),j.jsx("span",{className:"grow text-right text-xs",children:j.jsx(d0,{href:"https://github.com/ggerganov/llama.cpp/issues/11762",children:"Report a bug"})})]}),j.jsx("textarea",{className:"textarea textarea-bordered h-full dark-color",value:s,readOnly:!0})]})]})]})})}const hC=e=>{const[t,n]=Y.useState(null);return Y.useEffect(()=>{const r=a=>{var l;if(((l=a.data)==null?void 0:l.command)==="setText"){const s=a.data;e.setValue(s==null?void 0:s.text),s!=null&&s.context&&s.context.length>0&&n({type:"context",content:s.context}),e.focus()}};return window.addEventListener("message",r),()=>window.removeEventListener("message",r)},[e]),Y.useEffect(()=>{const r=a=>{a.key==="Escape"&&window.parent.postMessage({command:"escapePressed"},"*")};return window.addEventListener("keydown",r),()=>window.removeEventListener("keydown",r)},[]),{extraContext:t,clearExtraContext:()=>n(null)}},pC="(min-width: 1024px)",o0=e=>{if(!e)return;if(!window.matchMedia(pC).matches){e.style.height="",e.style.maxHeight="";return}const n=window.getComputedStyle(e).maxHeight;e.style.maxHeight="none",e.style.height="auto",e.style.height=`${e.scrollHeight}px`,e.style.maxHeight=n};function mC(e){const[t,n]=Y.useState(e),r=Y.useRef(null);Y.useEffect(()=>{const l=r.current;l&&(typeof t=="string"&&t.length>0?(l.value=t,setTimeout(()=>o0(l),0),n("")):setTimeout(()=>o0(l),0))},[r,t]);const a=Y.useCallback(l=>{o0(l.currentTarget)},[]);return{value:()=>{var l;return((l=r.current)==null?void 0:l.value)??""},setValue:l=>{const s=r.current;s&&(s.value=l,setTimeout(()=>o0(s),0))},focus:()=>{r.current&&r.current.focus()},ref:r,onInput:a}}const lh={content(){const e=new URL(window.location.href);return e.searchParams.get("m")??e.searchParams.get("q")??""},shouldSend(){return new URL(window.location.href).searchParams.has("q")},clear(){A3(["m","q"])}};function gC(e,t){const n=Zt.filterByLeafNodeId(e,t,!0),r=[],a=new Map;for(const s of e)a.set(s.id,s);const l=s=>{let c=a.get(s);for(;c&&c.children.length!==0;)c=a.get(c.children.at(-1)??-1);return(c==null?void 0:c.id)??-1};for(const s of n){const c=a.get(s.parent??-1);if(!c)continue;const f=c.children;s.type!=="root"&&r.push({msg:s,siblingLeafNodeIds:f.map(l),siblingCurrIdx:f.indexOf(s.id)})}return r}const Xa=C3((e,t=80)=>{const n=document.getElementById("main-scroll");if(!n)return;const r=n.scrollHeight-n.scrollTop-n.clientHeight;(!e||r<50)&&setTimeout(()=>n.scrollTo({top:n.scrollHeight}),t)},80);function Oy(){const{viewingChat:e,sendMessage:t,isGenerating:n,stopGenerating:r,pendingMessages:a,canvasData:l,replaceMessageAndGenerate:s,continueMessageAndGenerate:c}=_a(),f=mC(lh.content()),{extraContext:d,clearExtraContext:m}=hC(f),g=d?[d]:void 0,[w,b]=Y.useState(-1),_=Y.useMemo(()=>e?gC(e.messages,w):[],[w,e]),T=(e==null?void 0:e.conv.id)??null,D=a[T??""];Y.useEffect(()=>{b(-1),Xa(!1,1)},[T]);const R=Oe=>{Oe&&b(Oe),Xa(!0)},U=async()=>{var C;const Oe=f.value();if(Oe.trim().length===0||n(T??""))return;f.setValue(""),Xa(!1),b(-1);const V=((C=_.at(-1))==null?void 0:C.msg.id)??null;await t(T,V,Oe,g,R)||f.setValue(Oe),m()},F=async(Oe,V)=>{e&&(b(Oe.id),Xa(!1),await s(e.conv.id,Oe.parent,V,Oe.extra,R),b(-1),Xa(!1))},oe=async Oe=>{e&&(b(Oe.parent),Xa(!1),await s(e.conv.id,Oe.parent,null,Oe.extra,R),b(-1),Xa(!1))},ie=async(Oe,V)=>{!e||!c||(b(Oe.id),Xa(!1),await c(e.conv.id,Oe.id,V,R),b(-1),Xa(!1))},K=!!l;Y.useEffect(()=>{lh.shouldSend()?U():f.focus(),lh.clear()},[f.ref]);const we=D&&!_.some(Oe=>Oe.msg.id===D.id)?[{msg:D,siblingLeafNodeIds:[],siblingCurrIdx:0,isPending:!0}]:[];return j.jsxs("div",{className:Yr({"grid lg:gap-8 grow transition-[300ms]":!0,"grid-cols-[1fr_0fr] lg:grid-cols-[1fr_1fr]":K,"grid-cols-[1fr_0fr]":!K}),children:[j.jsxs("div",{className:Yr({"flex flex-col w-full max-w-[900px] mx-auto":!0,"hidden lg:flex":K,flex:!K}),children:[j.jsxs("div",{id:"messages-list",className:"grow",children:[j.jsx("div",{className:"mt-auto flex justify-center",children:e?"":"Send a message to start"}),[..._,...we].map(Oe=>{const V=Oe.msg,C=(D==null?void 0:D.id)===V.id;return j.jsx(uC,{msg:C?D:V,siblingLeafNodeIds:Oe.siblingLeafNodeIds,siblingCurrIdx:Oe.siblingCurrIdx,onRegenerateMessage:oe,onEditMessage:F,onChangeSibling:b,isPending:C||Oe.isPending,onContinueMessage:ie},V.id)})]}),j.jsxs("div",{className:"flex flex-row items-end pt-8 pb-6 sticky bottom-0 bg-base-100",children:[j.jsx("textarea",{className:"textarea textarea-bordered w-full resize-vertical lg:resize-none lg:max-h-48 lg:overflow-y-auto",placeholder:"Type a message (Shift+Enter to add a new line)",ref:f.ref,onInput:f.onInput,onKeyDown:Oe=>{Oe.nativeEvent.isComposing||Oe.keyCode===229||Oe.key==="Enter"&&!Oe.shiftKey&&(Oe.preventDefault(),U())},id:"msg-input",dir:"auto",rows:2}),n(T??"")?j.jsx("button",{className:"btn btn-neutral ml-2",onClick:()=>r(T??""),children:"Stop"}):j.jsx("button",{className:"btn btn-primary ml-2",onClick:U,children:"Send"})]})]}),j.jsx("div",{className:"w-full sticky top-[7em] h-[calc(100vh-9em)]",children:(l==null?void 0:l.type)===G0.PY_INTERPRETER&&j.jsx(dC,{})})]})}const yC=["temperature","top_k","top_p","min_p","max_tokens"],vC=["dynatemp_range","dynatemp_exponent","typical_p","xtc_probability","xtc_threshold","top_n_sigma"],bC=["repeat_last_n","repeat_penalty","presence_penalty","frequency_penalty","dry_multiplier","dry_base","dry_allowed_length","dry_penalty_last_n"],Sl="w-4 h-4 mr-1 inline";function wC({currentConfig:e,onLoadPreset:t}){const[n,r]=Y.useState(()=>Zt.getPresets()),[a,l]=Y.useState(""),[s,c]=Y.useState(null),{showConfirm:f,showAlert:d}=Xh(),m=async()=>{if(!a.trim()){await d("Please enter a preset name");return}const b=n.find(_=>_.name===a.trim());if(b)await f(`Preset "${a}" already exists. Do you want to overwrite it?`)&&(Zt.updatePreset(b.id,e),r(Zt.getPresets()),l(""),await d("Preset updated successfully"));else{const _=Zt.savePreset(a.trim(),e);r([...n,_]),l(""),await d("Preset saved successfully")}},g=async b=>{await f(`Load preset "${b.name}"? Current settings will be replaced.`)&&(t(b.config),c(b.id))},w=async b=>{await f(`Delete preset "${b.name}"?`)&&(Zt.deletePreset(b.id),r(n.filter(_=>_.id!==b.id)),s===b.id&&c(null))};return j.jsxs("div",{className:"space-y-4",children:[j.jsxs("div",{className:"form-control",children:[j.jsx("label",{className:"label",children:j.jsx("span",{className:"label-text",children:"Save current settings as preset"})}),j.jsxs("div",{className:"join",children:[j.jsx("input",{type:"text",placeholder:"Enter preset name",className:"input input-bordered join-item flex-1",value:a,onChange:b=>l(b.target.value),onKeyPress:b=>{b.key==="Enter"&&m()}}),j.jsx("button",{className:"btn btn-primary join-item",onClick:m,children:"Save Preset"})]})]}),j.jsxs("div",{className:"form-control",children:[j.jsx("label",{className:"label",children:j.jsx("span",{className:"label-text",children:"Saved presets"})}),n.length===0?j.jsx("div",{className:"alert",children:j.jsx("span",{children:"No presets saved yet"})}):j.jsx("div",{className:"space-y-2 max-h-64 overflow-y-auto",children:n.map(b=>j.jsx("div",{className:Yr({"card bg-base-200 p-3":!0,"ring-2 ring-primary":s===b.id}),children:j.jsxs("div",{className:"flex items-center justify-between",children:[j.jsxs("div",{children:[j.jsx("h4",{className:"font-semibold",children:b.name}),j.jsxs("p",{className:"text-sm opacity-70",children:["Created: ",new Date(b.createdAt).toLocaleString()]})]}),j.jsxs("div",{className:"flex gap-2",children:[j.jsx("button",{className:"btn btn-sm btn-primary",onClick:()=>g(b),children:"Load"}),j.jsx("button",{className:"btn btn-sm btn-error",onClick:()=>w(b),children:j.jsx(Zh,{className:"w-4 h-4"})})]})]})},b.id))})]})]})}const xC=(e,t)=>[{title:j.jsxs(j.Fragment,{children:[j.jsx(V6,{className:Sl}),"General"]}),fields:[{type:0,label:"API Key",key:"apiKey"},{type:1,label:"System Message (will be disabled if left empty)",key:"systemMessage"},...yC.map(n=>({type:0,label:n,key:n}))]},{title:j.jsxs(j.Fragment,{children:[j.jsx(X6,{className:Sl}),"Samplers"]}),fields:[{type:0,label:"Samplers queue",key:"samplers"},...vC.map(n=>({type:0,label:n,key:n}))]},{title:j.jsxs(j.Fragment,{children:[j.jsx(Q6,{className:Sl}),"Penalties"]}),fields:bC.map(n=>({type:0,label:n,key:n}))},{title:j.jsxs(j.Fragment,{children:[j.jsx(U6,{className:Sl}),"Reasoning"]}),fields:[{type:2,label:"Expand thought process by default when generating messages",key:"showThoughtInProgress"},{type:2,label:"Exclude thought process when sending requests to API (Recommended for DeepSeek-R1)",key:"excludeThoughtOnReq"}]},{title:j.jsxs(j.Fragment,{children:[j.jsx(a7,{className:Sl}),"Advanced"]}),fields:[{type:3,key:"custom",component:()=>{const n=async()=>{const a=await(await fetch("/demo-conversation.json")).json();Zt.remove(a.id);for(const l of a.messages)Zt.appendMsg(a.id,l)};return j.jsx("button",{className:"btn",onClick:n,children:"(debug) Import demo conversation"})}},{type:3,key:"custom",component:()=>{const n=async()=>{const r=await Zt.exportDB(),a=document.createElement("a");document.body.appendChild(a),a.href=URL.createObjectURL(r),document.body.appendChild(a),a.download="llamawebui_dump.json",a.click(),document.body.removeChild(a)};return j.jsx("button",{className:"btn",onClick:n,children:"Export conversation database"})}},{type:3,key:"custom",component:()=>{const n=async r=>{if(console.log(r),!r.target.files)throw oa.error("Target.files cant be null"),new Error("e.target.files cant be null");if(r.target.files.length!=1)throw oa.error("Number of selected files for DB import must be 1 but was "+r.target.files.length+"."),new Error("Number of selected files for DB import must be 1 but was "+r.target.files.length+".");const a=r.target.files[0];try{if(!a)throw new Error("No DB found to import.");console.log("Importing DB "+a.name),await Zt.importDB(a),oa.success("Import complete"),window.location.reload()}catch(l){console.error(""+l),oa.error(""+l)}};return j.jsxs("div",{children:[j.jsxs("label",{htmlFor:"db-import",className:"btn",role:"button",tabIndex:0,children:[" ","Reset and import conversation database"," "]}),j.jsx("input",{id:"db-import",type:"file",accept:".json",className:"file-upload",onInput:n,hidden:!0})]})}},{type:2,label:"Show tokens per second",key:"showTokensPerSecond"},{type:1,label:j.jsxs(j.Fragment,{children:["Custom JSON config (For more info, refer to"," ",j.jsx(d0,{href:"https://github.com/ikawrakow/ik_llama.cpp/tree/main/examples/server/README.md",children:"server documentation"}),")"]}),key:"custom"}]},{title:j.jsxs(j.Fragment,{children:[j.jsx(L6,{className:Sl}),"Experimental"]}),fields:[{type:3,key:"custom",component:()=>j.jsx(j.Fragment,{children:j.jsxs("p",{className:"mb-8",children:["Experimental features are not guaranteed to work correctly.",j.jsx("br",{}),j.jsx("br",{}),"If you encounter any problems, create a"," ",j.jsx(d0,{href:"https://github.com/ikawrakow/ik_llama.cpp/issues/new?template=019-bug-misc.yml",children:"Bug (misc.)"})," ","report on Github. Please also specify ",j.jsx("b",{children:"webui/experimental"})," on the report title and include screenshots.",j.jsx("br",{}),j.jsx("br",{}),"Some features may require packages downloaded from CDN, so they need internet connection."]})})},{type:2,label:j.jsxs(j.Fragment,{children:[j.jsx("b",{children:"Enable Python interpreter"}),j.jsx("br",{}),j.jsxs("small",{className:"text-xs",children:["This feature uses"," ",j.jsx(d0,{href:"https://pyodide.org",children:"pyodide"}),', downloaded from CDN. To use this feature, ask the LLM to generate Python code inside a Markdown code block. You will see a "Run" button on the code block, near the "Copy" button.']})]}),key:"pyIntepreterEnabled"}]},{title:j.jsxs(j.Fragment,{children:[j.jsx(B6,{className:Sl}),"Presets"]}),fields:[{type:3,key:"custom",component:()=>j.jsx(wC,{currentConfig:e,onLoadPreset:t})}]}];function kC({show:e,onClose:t}){const{config:n,saveConfig:r}=_a(),[a,l]=Y.useState(0),[s,c]=Y.useState(JSON.parse(JSON.stringify(n))),f=xC(s,c),d=()=>{window.confirm("Are you sure you want to reset all settings?")&&c(Do)},m=()=>{const w=JSON.parse(JSON.stringify(s));for(const b in w){const _=w[b],T=dh(Do[b]),D=fh(Do[b]),R=hh(Do[b]);if(D){if(!fh(_)){alert(`Value for ${b} must be string`);return}}else if(R){const U=_.toString().trim(),F=Number(U);if(isNaN(F)||!hh(F)||U.length===0){alert(`Value for ${b} must be numeric`);return}w[b]=F}else if(T){if(!dh(_)){alert(`Value for ${b} must be boolean`);return}}else console.error(`Unknown default type for key ${b}`)}r(w),t()},g=w=>b=>{c({...s,[w]:b})};return j.jsx("dialog",{className:Yr({modal:!0,"modal-open":e}),children:j.jsxs("div",{className:"modal-box w-11/12 max-w-3xl",children:[j.jsx("h3",{className:"text-lg font-bold mb-6",children:"Settings"}),j.jsxs("div",{className:"flex flex-col md:flex-row h-[calc(90vh-12rem)]",children:[j.jsx("div",{className:"hidden md:flex flex-col items-stretch pr-4 mr-4 border-r-2 border-base-200",children:f.map((w,b)=>j.jsx("div",{className:Yr({"btn btn-ghost justify-start font-normal w-44 mb-1":!0,"btn-active":a===b}),onClick:()=>l(b),dir:"auto",children:w.title},b))}),j.jsx("div",{className:"md:hidden flex flex-row gap-2 mb-4",children:j.jsxs("details",{className:"dropdown",children:[j.jsx("summary",{className:"btn bt-sm w-full m-1",children:f[a].title}),j.jsx("ul",{className:"menu dropdown-content bg-base-100 rounded-box z-[1] w-52 p-2 shadow",children:f.map((w,b)=>j.jsx("div",{className:Yr({"btn btn-ghost justify-start font-normal":!0,"btn-active":a===b}),onClick:()=>l(b),dir:"auto",children:w.title},b))})]})}),j.jsxs("div",{className:"grow overflow-y-auto px-4",children:[f[a].fields.map((w,b)=>{const _=`${a}-${b}`;if(w.type===0)return j.jsx(SC,{configKey:w.key,value:s[w.key],onChange:g(w.key),label:w.label},_);if(w.type===1)return j.jsx(EC,{configKey:w.key,value:s[w.key].toString(),onChange:g(w.key),label:w.label},_);if(w.type===2)return j.jsx(_C,{configKey:w.key,value:!!s[w.key],onChange:g(w.key),label:w.label},_);if(w.type===3)return j.jsx("div",{className:"mb-2",children:typeof w.component=="string"?w.component:w.component({value:s[w.key],onChange:g(w.key)})},_)}),j.jsx("p",{className:"opacity-40 mb-6 text-sm mt-8",children:"Settings are saved in browser's localStorage"})]})]}),j.jsxs("div",{className:"modal-action",children:[j.jsx("button",{className:"btn",onClick:d,children:"Reset to default"}),j.jsx("button",{className:"btn",onClick:t,children:"Close"}),j.jsx("button",{className:"btn btn-primary",onClick:m,children:"Save"})]})]})})}function EC({configKey:e,value:t,onChange:n,label:r}){return j.jsxs("label",{className:"form-control mb-2",children:[j.jsx("div",{className:"label inline",children:r||e}),j.jsx("textarea",{className:"textarea textarea-bordered h-24",placeholder:`Default: ${Do[e]||"none"}`,value:t,onChange:a=>n(a.target.value)})]})}function SC({configKey:e,value:t,onChange:n,label:r}){const a=R3[e];return j.jsxs(j.Fragment,{children:[a&&j.jsxs("div",{className:"block md:hidden mb-1",children:[j.jsx("b",{children:r||e}),j.jsx("br",{}),j.jsx("p",{className:"text-xs whitespace-normal",children:a})]}),j.jsxs("label",{className:"input input-bordered join-item grow flex items-center gap-2 mb-2",children:[j.jsxs("div",{className:"dropdown dropdown-hover",children:[j.jsx("div",{tabIndex:0,role:"button",className:"font-bold hidden md:block",children:r||e}),a&&j.jsx("div",{className:"dropdown-content menu bg-base-100 rounded-box z-10 w-64 p-2 shadow mt-4 whitespace-normal break-words",children:a})]}),j.jsx("input",{type:"text",className:"grow",placeholder:`Default: ${Do[e]||"none"}`,value:t,onChange:l=>n(l.target.value)})]})]})}function _C({configKey:e,value:t,onChange:n,label:r}){return j.jsxs("div",{className:"flex flex-row items-center mb-2",children:[j.jsx("input",{type:"checkbox",className:"toggle",checked:t,onChange:a=>n(a.target.checked)}),j.jsx("span",{className:"ml-4",children:r||e})]})}function NC(){return j.jsx(R6,{children:j.jsx(u3,{children:j.jsx("div",{className:"flex flex-row drawer lg:drawer-open",children:j.jsx(M6,{children:j.jsx(Fb,{children:j.jsxs(l0,{element:j.jsx(TC,{}),children:[j.jsx(l0,{path:"/chat/:convId",element:j.jsx(Oy,{})}),j.jsx(l0,{path:"*",element:j.jsx(Oy,{})})]})})})})})})}function TC(){const{showSettings:e,setShowSettings:t}=_a();return j.jsxs(j.Fragment,{children:[j.jsx(m7,{}),j.jsxs("div",{className:"drawer-content grow flex flex-col h-screen mx-auto px-4 overflow-auto bg-base-100",id:"main-scroll",children:[j.jsx(f7,{}),j.jsx(zb,{})]}),j.jsx(kC,{show:e,onClose:()=>t(!1)})]})}K5.createRoot(document.getElementById("root")).render(j.jsx(Y.StrictMode,{children:j.jsx(NC,{})}));
-
+`))},C0.postMessage({id:n,python:e,context:r,interruptBuffer:ou})}),interrupt:()=>{console.log("Interrupting..."),console.trace(),ou&&(ou[0]=2)}}};function dC(){const{canvasData:e,setCanvasData:t}=_a(),[n,r]=Y.useState((e==null?void 0:e.content)??""),[a,l]=Y.useState(!1),[s,c]=Y.useState(""),[f,d]=Y.useState(),[m,g]=Y.useState(!1),w=async b=>{f==null||f(),l(!0),c("Loading Pyodide...");const{donePromise:_,interrupt:T}=fC(b,()=>{c("Running..."),g(Uv)});d(()=>T);const D=await _;c(D),l(!1),g(!1)};return Y.useEffect(()=>{r((e==null?void 0:e.content)??""),w((e==null?void 0:e.content)??"")},[e==null?void 0:e.content]),(e==null?void 0:e.type)!==G0.PY_INTERPRETER?null:j.jsx("div",{className:"card bg-base-200 w-full h-full shadow-xl",children:j.jsxs("div",{className:"card-body",children:[j.jsxs("div",{className:"flex justify-between items-center mb-4",children:[j.jsx("span",{className:"text-lg font-bold",children:"Python Interpreter"}),j.jsx(d7,{className:"bg-base-100",onClick:()=>t(null)})]}),j.jsxs("div",{className:"grid grid-rows-3 gap-4 h-full",children:[j.jsx("textarea",{className:"textarea textarea-bordered w-full h-full font-mono",value:n,onChange:b=>r(b.target.value)}),j.jsxs("div",{className:"font-mono flex flex-col row-span-2",children:[j.jsxs("div",{className:"flex items-center mb-2",children:[j.jsxs("button",{className:"btn btn-sm bg-base-100",onClick:()=>w(n),disabled:a,children:[j.jsx(r7,{className:"h-6 w-6"})," Run"]}),m&&j.jsxs("button",{className:"btn btn-sm bg-base-100 ml-2",onClick:()=>f==null?void 0:f(),children:[j.jsx(l7,{className:"h-6 w-6"})," Stop"]}),j.jsx("span",{className:"grow text-right text-xs",children:j.jsx(d0,{href:"https://github.com/ggerganov/llama.cpp/issues/11762",children:"Report a bug"})})]}),j.jsx("textarea",{className:"textarea textarea-bordered h-full dark-color",value:s,readOnly:!0})]})]})]})})}const hC=e=>{const[t,n]=Y.useState(null);return Y.useEffect(()=>{const r=a=>{var l;if(((l=a.data)==null?void 0:l.command)==="setText"){const s=a.data;e.setValue(s==null?void 0:s.text),s!=null&&s.context&&s.context.length>0&&n({type:"context",content:s.context}),e.focus()}};return window.addEventListener("message",r),()=>window.removeEventListener("message",r)},[e]),Y.useEffect(()=>{const r=a=>{a.key==="Escape"&&window.parent.postMessage({command:"escapePressed"},"*")};return window.addEventListener("keydown",r),()=>window.removeEventListener("keydown",r)},[]),{extraContext:t,clearExtraContext:()=>n(null)}},pC="(min-width: 1024px)",o0=e=>{if(!e)return;if(!window.matchMedia(pC).matches){e.style.height="",e.style.maxHeight="";return}const n=window.getComputedStyle(e).maxHeight;e.style.maxHeight="none",e.style.height="auto",e.style.height=`${e.scrollHeight}px`,e.style.maxHeight=n};function mC(e){const[t,n]=Y.useState(e),r=Y.useRef(null);Y.useEffect(()=>{const l=r.current;l&&(typeof t=="string"&&t.length>0?(l.value=t,setTimeout(()=>o0(l),0),n("")):setTimeout(()=>o0(l),0))},[r,t]);const a=Y.useCallback(l=>{o0(l.currentTarget)},[]);return{value:()=>{var l;return((l=r.current)==null?void 0:l.value)??""},setValue:l=>{const s=r.current;s&&(s.value=l,setTimeout(()=>o0(s),0))},focus:()=>{r.current&&r.current.focus()},ref:r,onInput:a}}const lh={content(){const e=new URL(window.location.href);return e.searchParams.get("m")??e.searchParams.get("q")??""},shouldSend(){return new URL(window.location.href).searchParams.has("q")},clear(){A3(["m","q"])}};function gC(e,t){const n=Zt.filterByLeafNodeId(e,t,!0),r=[],a=new Map;for(const s of e)a.set(s.id,s);const l=s=>{let c=a.get(s);for(;c&&c.children.length!==0;)c=a.get(c.children.at(-1)??-1);return(c==null?void 0:c.id)??-1};for(const s of n){const c=a.get(s.parent??-1);if(!c)continue;const f=c.children;s.type!=="root"&&r.push({msg:s,siblingLeafNodeIds:f.map(l),siblingCurrIdx:f.indexOf(s.id)})}return r}const Xa=C3((e,t=80)=>{const n=document.getElementById("main-scroll");if(!n)return;const r=n.scrollHeight-n.scrollTop-n.clientHeight;(!e||r<50)&&setTimeout(()=>n.scrollTo({top:n.scrollHeight}),t)},80);function Oy(){const{viewingChat:e,sendMessage:t,isGenerating:n,stopGenerating:r,pendingMessages:a,canvasData:l,replaceMessageAndGenerate:s,continueMessageAndGenerate:c}=_a(),f=mC(lh.content()),{extraContext:d,clearExtraContext:m}=hC(f),g=d?[d]:void 0,[w,b]=Y.useState(-1),_=Y.useMemo(()=>e?gC(e.messages,w):[],[w,e]),T=(e==null?void 0:e.conv.id)??null,D=a[T??""];Y.useEffect(()=>{b(-1),Xa(!1,1)},[T]);const R=Oe=>{Oe&&b(Oe),Xa(!0)},U=async()=>{var C;const Oe=f.value();if(Oe.trim().length===0||n(T??""))return;f.setValue(""),Xa(!1),b(-1);const V=((C=_.at(-1))==null?void 0:C.msg.id)??null;await t(T,V,Oe,g,R)||f.setValue(Oe),m()},F=async(Oe,V)=>{e&&(b(Oe.id),Xa(!1),await s(e.conv.id,Oe.parent,V,Oe.extra,R),b(-1),Xa(!1))},oe=async Oe=>{e&&(b(Oe.parent),Xa(!1),await s(e.conv.id,Oe.parent,null,Oe.extra,R),b(-1),Xa(!1))},ie=async(Oe,V)=>{!e||!c||(b(Oe.id),Xa(!1),await c(e.conv.id,Oe.id,V,R),b(-1),Xa(!1))},K=!!l;Y.useEffect(()=>{lh.shouldSend()?U():f.focus(),lh.clear()},[f.ref]);const we=D&&!_.some(Oe=>Oe.msg.id===D.id)?[{msg:D,siblingLeafNodeIds:[],siblingCurrIdx:0,isPending:!0}]:[];return j.jsxs("div",{className:Yr({"grid lg:gap-8 grow transition-[300ms]":!0,"grid-cols-[1fr_0fr] lg:grid-cols-[1fr_1fr]":K,"grid-cols-[1fr_0fr]":!K}),children:[j.jsxs("div",{className:Yr({"flex flex-col w-full max-w-[900px] mx-auto":!0,"hidden lg:flex":K,flex:!K}),children:[j.jsxs("div",{id:"messages-list",className:"grow",children:[j.jsx("div",{className:"mt-auto flex justify-center",children:e?"":"Send a message to start"}),[..._,...we].map(Oe=>{const V=Oe.msg,C=(D==null?void 0:D.id)===V.id;return j.jsx(uC,{msg:C?D:V,siblingLeafNodeIds:Oe.siblingLeafNodeIds,siblingCurrIdx:Oe.siblingCurrIdx,onRegenerateMessage:oe,onEditMessage:F,onChangeSibling:b,isPending:C||Oe.isPending,onContinueMessage:ie},V.id)})]}),j.jsxs("div",{className:"flex flex-row items-end pt-8 pb-6 sticky bottom-0 bg-base-100",children:[j.jsx("textarea",{className:"textarea textarea-bordered w-full resize-vertical lg:resize-none lg:max-h-48 lg:overflow-y-auto",placeholder:"Type a message (Shift+Enter to add a new line)",ref:f.ref,onInput:f.onInput,onKeyDown:Oe=>{Oe.nativeEvent.isComposing||Oe.keyCode===229||Oe.key==="Enter"&&!Oe.shiftKey&&(Oe.preventDefault(),U())},id:"msg-input",dir:"auto",rows:2}),n(T??"")?j.jsx("button",{className:"btn btn-neutral ml-2",onClick:()=>r(T??""),children:"Stop"}):j.jsx("button",{className:"btn btn-primary ml-2",onClick:U,children:"Send"})]})]}),j.jsx("div",{className:"w-full sticky top-[7em] h-[calc(100vh-9em)]",children:(l==null?void 0:l.type)===G0.PY_INTERPRETER&&j.jsx(dC,{})})]})}const yC=["temperature","top_k","top_p","min_p","max_tokens"],vC=["dynatemp_range","dynatemp_exponent","typical_p","xtc_probability","xtc_threshold","top_n_sigma"],bC=["repeat_last_n","repeat_penalty","presence_penalty","frequency_penalty","dry_multiplier","dry_base","dry_allowed_length","dry_penalty_last_n"],Sl="w-4 h-4 mr-1 inline";function wC({currentConfig:e,onLoadPreset:t}){const[n,r]=Y.useState(()=>Zt.getPresets()),[a,l]=Y.useState(""),[s,c]=Y.useState(null),{showConfirm:f,showAlert:d}=Xh(),m=async()=>{if(!a.trim()){await d("Please enter a preset name");return}const b=n.find(_=>_.name===a.trim());if(b)await f(`Preset "${a}" already exists. Do you want to overwrite it?`)&&(Zt.updatePreset(b.id,e),r(Zt.getPresets()),l(""),await d("Preset updated successfully"));else{const _=Zt.savePreset(a.trim(),e);r([...n,_]),l(""),await d("Preset saved successfully")}},g=async b=>{await f(`Load preset "${b.name}"? Current settings will be replaced.`)&&(t(b.config),c(b.id))},w=async b=>{await f(`Delete preset "${b.name}"?`)&&(Zt.deletePreset(b.id),r(n.filter(_=>_.id!==b.id)),s===b.id&&c(null))};return j.jsxs("div",{className:"space-y-4",children:[j.jsxs("div",{className:"form-control",children:[j.jsx("label",{className:"label",children:j.jsx("span",{className:"label-text",children:"Save current settings as preset"})}),j.jsxs("div",{className:"join",children:[j.jsx("input",{type:"text",placeholder:"Enter preset name",className:"input input-bordered join-item flex-1",value:a,onChange:b=>l(b.target.value),onKeyPress:b=>{b.key==="Enter"&&m()}}),j.jsx("button",{className:"btn btn-primary join-item",onClick:m,children:"Save Preset"})]})]}),j.jsxs("div",{className:"form-control",children:[j.jsx("label",{className:"label",children:j.jsx("span",{className:"label-text",children:"Saved presets"})}),n.length===0?j.jsx("div",{className:"alert",children:j.jsx("span",{children:"No presets saved yet"})}):j.jsx("div",{className:"space-y-2 max-h-64 overflow-y-auto",children:n.map(b=>j.jsx("div",{className:Yr({"card bg-base-200 p-3":!0,"ring-2 ring-primary":s===b.id}),children:j.jsxs("div",{className:"flex items-center justify-between",children:[j.jsxs("div",{children:[j.jsx("h4",{className:"font-semibold",children:b.name}),j.jsxs("p",{className:"text-sm opacity-70",children:["Created: ",new Date(b.createdAt).toLocaleString()]})]}),j.jsxs("div",{className:"flex gap-2",children:[j.jsx("button",{className:"btn btn-sm btn-primary",onClick:()=>g(b),children:"Load"}),j.jsx("button",{className:"btn btn-sm btn-error",onClick:()=>w(b),children:j.jsx(Zh,{className:"w-4 h-4"})})]})]})},b.id))})]})]})}const xC=(e,t)=>[{title:j.jsxs(j.Fragment,{children:[j.jsx(B6,{className:Sl}),"Presets"]}),fields:[{type:3,key:"custom",component:()=>j.jsx(wC,{currentConfig:e,onLoadPreset:t})}]},{title:j.jsxs(j.Fragment,{children:[j.jsx(V6,{className:Sl}),"General"]}),fields:[{type:0,label:"API Key",key:"apiKey"},{type:1,label:"System Message (will be disabled if left empty)",key:"systemMessage"},...yC.map(n=>({type:0,label:n,key:n}))]},{title:j.jsxs(j.Fragment,{children:[j.jsx(X6,{className:Sl}),"Samplers"]}),fields:[{type:0,label:"Samplers queue",key:"samplers"},...vC.map(n=>({type:0,label:n,key:n}))]},{title:j.jsxs(j.Fragment,{children:[j.jsx(Q6,{className:Sl}),"Penalties"]}),fields:bC.map(n=>({type:0,label:n,key:n}))},{title:j.jsxs(j.Fragment,{children:[j.jsx(U6,{className:Sl}),"Reasoning"]}),fields:[{type:2,label:"Expand thought process by default when generating messages",key:"showThoughtInProgress"},{type:2,label:"Exclude thought process when sending requests to API (Recommended for DeepSeek-R1)",key:"excludeThoughtOnReq"}]},{title:j.jsxs(j.Fragment,{children:[j.jsx(a7,{className:Sl}),"Advanced"]}),fields:[{type:3,key:"custom",component:()=>{const n=async()=>{const a=await(await fetch("/demo-conversation.json")).json();Zt.remove(a.id);for(const l of a.messages)Zt.appendMsg(a.id,l)};return j.jsx("button",{className:"btn",onClick:n,children:"(debug) Import demo conversation"})}},{type:3,key:"custom",component:()=>{const n=async()=>{const r=await Zt.exportDB(),a=document.createElement("a");document.body.appendChild(a),a.href=URL.createObjectURL(r),document.body.appendChild(a),a.download="llamawebui_dump.json",a.click(),document.body.removeChild(a)};return j.jsx("button",{className:"btn",onClick:n,children:"Export conversation database"})}},{type:3,key:"custom",component:()=>{const n=async r=>{if(console.log(r),!r.target.files)throw oa.error("Target.files cant be null"),new Error("e.target.files cant be null");if(r.target.files.length!=1)throw oa.error("Number of selected files for DB import must be 1 but was "+r.target.files.length+"."),new Error("Number of selected files for DB import must be 1 but was "+r.target.files.length+".");const a=r.target.files[0];try{if(!a)throw new Error("No DB found to import.");console.log("Importing DB "+a.name),await Zt.importDB(a),oa.success("Import complete"),window.location.reload()}catch(l){console.error(""+l),oa.error(""+l)}};return j.jsxs("div",{children:[j.jsxs("label",{htmlFor:"db-import",className:"btn",role:"button",tabIndex:0,children:[" ","Reset and import conversation database"," "]}),j.jsx("input",{id:"db-import",type:"file",accept:".json",className:"file-upload",onInput:n,hidden:!0})]})}},{type:2,label:"Show tokens per second",key:"showTokensPerSecond"},{type:1,label:j.jsxs(j.Fragment,{children:["Custom JSON config (For more info, refer to"," ",j.jsx(d0,{href:"https://github.com/ikawrakow/ik_llama.cpp/tree/main/examples/server/README.md",children:"server documentation"}),")"]}),key:"custom"}]},{title:j.jsxs(j.Fragment,{children:[j.jsx(L6,{className:Sl}),"Experimental"]}),fields:[{type:3,key:"custom",component:()=>j.jsx(j.Fragment,{children:j.jsxs("p",{className:"mb-8",children:["Experimental features are not guaranteed to work correctly.",j.jsx("br",{}),j.jsx("br",{}),"If you encounter any problems, create a"," ",j.jsx(d0,{href:"https://github.com/ikawrakow/ik_llama.cpp/issues/new?template=019-bug-misc.yml",children:"Bug (misc.)"})," ","report on Github. Please also specify ",j.jsx("b",{children:"webui/experimental"})," on the report title and include screenshots.",j.jsx("br",{}),j.jsx("br",{}),"Some features may require packages downloaded from CDN, so they need internet connection."]})})},{type:2,label:j.jsxs(j.Fragment,{children:[j.jsx("b",{children:"Enable Python interpreter"}),j.jsx("br",{}),j.jsxs("small",{className:"text-xs",children:["This feature uses"," ",j.jsx(d0,{href:"https://pyodide.org",children:"pyodide"}),', downloaded from CDN. To use this feature, ask the LLM to generate Python code inside a Markdown code block. You will see a "Run" button on the code block, near the "Copy" button.']})]}),key:"pyIntepreterEnabled"}]}];function kC({show:e,onClose:t}){const{config:n,saveConfig:r}=_a(),[a,l]=Y.useState(0),[s,c]=Y.useState(JSON.parse(JSON.stringify(n))),f=xC(s,c),d=()=>{window.confirm("Are you sure you want to reset all settings?")&&c(Do)},m=()=>{const w=JSON.parse(JSON.stringify(s));for(const b in w){const _=w[b],T=dh(Do[b]),D=fh(Do[b]),R=hh(Do[b]);if(D){if(!fh(_)){alert(`Value for ${b} must be string`);return}}else if(R){const U=_.toString().trim(),F=Number(U);if(isNaN(F)||!hh(F)||U.length===0){alert(`Value for ${b} must be numeric`);return}w[b]=F}else if(T){if(!dh(_)){alert(`Value for ${b} must be boolean`);return}}else console.error(`Unknown default type for key ${b}`)}r(w),t()},g=w=>b=>{c({...s,[w]:b})};return j.jsx("dialog",{className:Yr({modal:!0,"modal-open":e}),children:j.jsxs("div",{className:"modal-box w-11/12 max-w-3xl",children:[j.jsx("h3",{className:"text-lg font-bold mb-6",children:"Settings"}),j.jsxs("div",{className:"flex flex-col md:flex-row h-[calc(90vh-12rem)]",children:[j.jsx("div",{className:"hidden md:flex flex-col items-stretch pr-4 mr-4 border-r-2 border-base-200",children:f.map((w,b)=>j.jsx("div",{className:Yr({"btn btn-ghost justify-start font-normal w-44 mb-1":!0,"btn-active":a===b}),onClick:()=>l(b),dir:"auto",children:w.title},b))}),j.jsx("div",{className:"md:hidden flex flex-row gap-2 mb-4",children:j.jsxs("details",{className:"dropdown",children:[j.jsx("summary",{className:"btn bt-sm w-full m-1",children:f[a].title}),j.jsx("ul",{className:"menu dropdown-content bg-base-100 rounded-box z-[1] w-52 p-2 shadow",children:f.map((w,b)=>j.jsx("div",{className:Yr({"btn btn-ghost justify-start font-normal":!0,"btn-active":a===b}),onClick:()=>l(b),dir:"auto",children:w.title},b))})]})}),j.jsxs("div",{className:"grow overflow-y-auto px-4",children:[f[a].fields.map((w,b)=>{const _=`${a}-${b}`;if(w.type===0)return j.jsx(SC,{configKey:w.key,value:s[w.key],onChange:g(w.key),label:w.label},_);if(w.type===1)return j.jsx(EC,{configKey:w.key,value:s[w.key].toString(),onChange:g(w.key),label:w.label},_);if(w.type===2)return j.jsx(_C,{configKey:w.key,value:!!s[w.key],onChange:g(w.key),label:w.label},_);if(w.type===3)return j.jsx("div",{className:"mb-2",children:typeof w.component=="string"?w.component:w.component({value:s[w.key],onChange:g(w.key)})},_)}),j.jsx("p",{className:"opacity-40 mb-6 text-sm mt-8",children:"Settings are saved in browser's localStorage"})]})]}),j.jsxs("div",{className:"modal-action",children:[j.jsx("button",{className:"btn",onClick:d,children:"Reset to default"}),j.jsx("button",{className:"btn",onClick:t,children:"Close"}),j.jsx("button",{className:"btn btn-primary",onClick:m,children:"Save"})]})]})})}function EC({configKey:e,value:t,onChange:n,label:r}){return j.jsxs("label",{className:"form-control mb-2",children:[j.jsx("div",{className:"label inline",children:r||e}),j.jsx("textarea",{className:"textarea textarea-bordered h-24",placeholder:`Default: ${Do[e]||"none"}`,value:t,onChange:a=>n(a.target.value)})]})}function SC({configKey:e,value:t,onChange:n,label:r}){const a=R3[e];return j.jsxs(j.Fragment,{children:[a&&j.jsxs("div",{className:"block md:hidden mb-1",children:[j.jsx("b",{children:r||e}),j.jsx("br",{}),j.jsx("p",{className:"text-xs whitespace-normal",children:a})]}),j.jsxs("label",{className:"input input-bordered join-item grow flex items-center gap-2 mb-2",children:[j.jsxs("div",{className:"dropdown dropdown-hover",children:[j.jsx("div",{tabIndex:0,role:"button",className:"font-bold hidden md:block",children:r||e}),a&&j.jsx("div",{className:"dropdown-content menu bg-base-100 rounded-box z-10 w-64 p-2 shadow mt-4 whitespace-normal break-words",children:a})]}),j.jsx("input",{type:"text",className:"grow",placeholder:`Default: ${Do[e]||"none"}`,value:t,onChange:l=>n(l.target.value)})]})]})}function _C({configKey:e,value:t,onChange:n,label:r}){return j.jsxs("div",{className:"flex flex-row items-center mb-2",children:[j.jsx("input",{type:"checkbox",className:"toggle",checked:t,onChange:a=>n(a.target.checked)}),j.jsx("span",{className:"ml-4",children:r||e})]})}function NC(){return j.jsx(R6,{children:j.jsx(u3,{children:j.jsx("div",{className:"flex flex-row drawer lg:drawer-open",children:j.jsx(M6,{children:j.jsx(Fb,{children:j.jsxs(l0,{element:j.jsx(TC,{}),children:[j.jsx(l0,{path:"/chat/:convId",element:j.jsx(Oy,{})}),j.jsx(l0,{path:"*",element:j.jsx(Oy,{})})]})})})})})})}function TC(){const{showSettings:e,setShowSettings:t}=_a();return j.jsxs(j.Fragment,{children:[j.jsx(m7,{}),j.jsxs("div",{className:"drawer-content grow flex flex-col h-screen mx-auto px-4 overflow-auto bg-base-100",id:"main-scroll",children:[j.jsx(f7,{}),j.jsx(zb,{})]}),j.jsx(kC,{show:e,onClose:()=>t(!1)})]})}K5.createRoot(document.getElementById("root")).render(j.jsx(Y.StrictMode,{children:j.jsx(NC,{})}));
+
diff --git a/examples/server/webui/src/components/SettingDialog.tsx b/examples/server/webui/src/components/SettingDialog.tsx
index 004b51ab0..1d86c2a48 100644
--- a/examples/server/webui/src/components/SettingDialog.tsx
+++ b/examples/server/webui/src/components/SettingDialog.tsx
@@ -228,6 +228,26 @@ const SETTING_SECTIONS = (
localConfig: typeof CONFIG_DEFAULT,
setLocalConfig: (config: typeof CONFIG_DEFAULT) => void
): SettingSection[] => [
+ {
+ title: (
+ <>
+
+ Presets
+ >
+ ),
+ fields: [
+ {
+ type: SettingInputType.CUSTOM,
+ key: 'custom', // dummy key for presets
+ component: () => (
+
+ ),
+ },
+ ],
+ },
{
title: (
<>
@@ -489,26 +509,7 @@ const SETTING_SECTIONS = (
},
],
},
- {
- title: (
- <>
-
- Presets
- >
- ),
- fields: [
- {
- type: SettingInputType.CUSTOM,
- key: 'custom', // dummy key for presets
- component: () => (
-
- ),
- },
- ],
- },
+
];
export default function SettingDialog({
diff --git a/examples/server/webui/src/index.scss b/examples/server/webui/src/index.scss
index d4ae4a415..5560ff076 100644
--- a/examples/server/webui/src/index.scss
+++ b/examples/server/webui/src/index.scss
@@ -48,7 +48,7 @@ html {
.chat-bubble-base-300 {
--tw-bg-opacity: 1;
--tw-text-opacity: 1;
- @apply bg-base-300 text-base-content;
+ @apply break-words bg-base-300 text-base-content;
}
/* Highlight.js */