@fugood/buttress-server 2.26.0-beta.21 → 2.26.0-beta.22
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.
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{a as e,i as t,n,o as r,r as i}from"./net-
|
|
2
|
+
import{a as e,i as t,n,o as r,r as i}from"./net-tWFM4EP8.mjs";import{a,c as o,n as s}from"./config-DiYweJ2m.mjs";import{n as c,r as l}from"./client-qqI-HCOj.mjs";import u from"node:fs/promises";import d from"node:path";import f from"node:fs";import{execFile as p,spawn as m}from"node:child_process";import h from"node-machine-id";import g from"@iarna/toml";import{promisify as _}from"node:util";const v=_(p),y=e=>e?.code===`ENOENT`,b=async e=>{try{return await u.stat(e)}catch(e){if(y(e))return null;throw e}},x=e=>{if(!e)return{};try{let{env:t}=g.parse(e);return t&&typeof t==`object`&&!Array.isArray(t)?t:{}}catch{return{}}},S=e=>(process.platform===`win32`?/^[\w@%+=:,./\\-]+$/:/^[\w@%+=:,./-]+$/).test(e)?e:process.platform===`win32`?`"${e}"`:`'${e.replaceAll(`'`,`'\\''`)}'`,C=e=>process.platform===`darwin`?[`open`,e]:process.platform===`win32`?[`rundll32`,`url.dll,FileProtocolHandler`,e]:[`xdg-open`,e],w=()=>{if(process.platform===`darwin`)return[{argv:[`pbcopy`]}];if(process.platform===`win32`)return[{argv:[`clip`],encode:e=>Buffer.from(`\ufeff${e}`,`utf16le`)}];let e={argv:[`wl-copy`]},t={argv:[`xclip`,`-selection`,`clipboard`]};return process.env.WAYLAND_DISPLAY?[e,t]:[t,e]},T=(e,t)=>new Promise((n,r)=>{let i=m(e,t,{detached:!0,stdio:`ignore`});i.once(`error`,r),i.once(`spawn`,()=>{i.unref(),n()})}),E=(e,t,n)=>new Promise((r,i)=>{let a=m(e,t,{stdio:[`pipe`,`ignore`,`ignore`]});a.once(`error`,i),a.stdin.once(`error`,i),a.once(`exit`,t=>{t===0?r():i(Error(`${e} exited with code ${t}`))}),a.stdin.end(n)}),D=n=>{let o=new Set,p=()=>{for(let e of[...o])e()};return{configPath:n,serverVersion:e().version,machineId:h.machineIdSync(),async readConfigText(){try{return await u.readFile(n,`utf8`)}catch(e){if(y(e))return null;throw e}},async writeConfigText(e,{backup:t}){let r=await u.realpath(n).catch(()=>n),i=await b(r);await u.mkdir(d.dirname(r),{recursive:!0}),p();let a=d.join(d.dirname(r),`.${d.basename(r)}.${process.pid}-${Date.now()}.tmp`);try{await u.writeFile(a,e,{encoding:`utf8`,flag:`wx`,...i?{mode:i.mode&511}:{}}),i&&await u.chmod(a,i.mode&4095);let o=null;return t&&i&&(o=`${n}.bak`,await u.copyFile(r,o)),await u.rename(a,r),{backupPath:o}}catch(e){throw await u.rm(a,{force:!0}),e}},async statConfig(){let e=await b(n);return e?{mtimeMs:e.mtimeMs}:null},watchConfig(e){let t=d.basename(n),r=null,i=null,a=!1,s=()=>{if(o.delete(s),!a)try{i=f.watch(d.dirname(n),(n,i)=>{i&&i!==t||(r&&clearTimeout(r),r=setTimeout(()=>{r=null,e()},100))}),i.on(`error`,e=>{console.warn(`[ui] Stopped watching ${n}: ${e.message}`),i?.close()})}catch(e){y(e)?o.add(s):console.warn(`[ui] Cannot watch ${n}: ${e.message}`)}};return s(),()=>{a=!0,o.delete(s),r&&clearTimeout(r),i?.close()}},async probeCaps(e){return(await t(e.map(e=>({type:e})))).map(({type:e,score:t,hasGpu:n,usableBytes:r})=>({type:e,score:t??0,hasGpu:n??!1,usableBytes:r??0}))},async getBackendDefaults(e){return r(e)},resolveHfToken(e){let t={...x(e.raw.env),...process.env};return[e.runtime.huggingface_token,t.HUGGINGFACE_TOKEN,t.HF_TOKEN].find(e=>typeof e==`string`&&e.trim()!==``)?.trim()??null},async hostInfo(){return i()},async preferredAppearance(){if(process.platform!==`darwin`)return`light`;try{let{stdout:e}=await v(`defaults`,[`read`,`-g`,`AppleInterfaceStyle`]);return e.trim()===`Dark`?`dark`:`light`}catch{return`light`}},async openExternal(e){let{protocol:t}=new URL(e);if(t!==`http:`&&t!==`https:`)throw Error(`Only http(s) links can be opened: ${e}`);let[n,...r]=C(e);await T(n,r)},async copyToClipboard(e){let t=null;for(let{argv:n,encode:r}of w()){let[i,...a]=n;try{await E(i,a,r?r(e):e);return}catch(e){t=e}}throw t},startCommand:()=>`bricks-buttress --config ${S(n)}`,agentConnection(e){if(!f.existsSync(n))return null;let t;try{t=c(n)}catch{return null}let{config:r,agentsConfig:i}=t;return i?{baseUrl:`http://127.0.0.1:${e?.port??(r.server.port||2080)}`,token:l(i.runtimeTokenFile)}:null},agentsConfigError(e){let t=e;if(t===void 0)try{t=f.readFileSync(n,`utf8`)}catch{return{agents:0,error:null}}let r;try{r=g.parse(t)}catch(e){return{agents:0,error:`invalid TOML: ${O(e)}`}}let i=Array.isArray(r.agents)?r.agents.length:0;if(i===0)return{agents:0,error:null};try{return s(a(r),{configDir:d.dirname(n)}),{agents:i,error:null}}catch(e){return{agents:i,error:O(e)}}}}},O=e=>e instanceof Error?e.message:String(e),k=()=>import(`@fugood/buttress-ui`),A=[`darwin-arm64`,`linux-x64`,`win32-x64`],j=[`chat`,`status`,`settings`],M=e=>j.includes(e),N=e=>{let t=[],n;for(let r=0;r<e.length;r+=1){let i=e[r];if(i===`-h`||i===`--help`)return{kind:`help`};if(i===`-c`||i===`--config`){let n=e[r+1];if(n===void 0)return{kind:`error`,message:`${i} needs a config file path`};t.push(n),r+=1}else if(i===`--tab`){let t=e[r+1];if(t===void 0||!M(t)){let e=t===void 0?``:` (got "${t}")`;return{kind:`error`,message:`--tab needs one of ${j.join(`, `)}${e}`}}n=t,r+=1}else if(i.startsWith(`-`))return{kind:`error`,message:`Unknown option: ${i}`};else t.push(i)}return t.length===0?{kind:`error`,message:`bricks-buttress ui needs a config file path`}:t.length>1?{kind:`error`,message:`Give one config file path (got ${t.join(`, `)})`}:t[0].includes(`
|
|
3
3
|
`)?{kind:`error`,message:`bricks-buttress ui edits a config file; inline TOML is not supported`}:{kind:`run`,configPath:d.resolve(t[0]),initialTab:n}},P=()=>({isBun:`Bun`in globalThis,platform:process.platform,arch:process.arch}),F=(e,t)=>e.isBun?A.includes(`${e.platform}-${e.arch}`)?null:`bricks-buttress ui runs on darwin-arm64, linux-x64 and win32-x64; on this machine open ${t()} in a browser instead`:`bricks-buttress ui needs Bun; run it with bun or the standalone bricks-buttress binary`,I=e=>{try{let t=g.parse(f.readFileSync(e,`utf8`)).server?.port;return Number.isInteger(t)&&t>0?t:o}catch{return o}},L=e=>`http://${n()??`localhost`}:${I(e)}/status`,R=e=>e instanceof Error?e.message:String(e),z=e=>{let{code:t,specifier:n}=e??{};return t===`ERR_MODULE_NOT_FOUND`&&n===`@fugood/buttress-ui`},B=e=>process.env.BUTTRESS_SERVER_DISTRIBUTION===`1`&&R(e).includes(`@gpuix/native-`),V=async e=>{try{let{launchButtressUi:e}=await k();return e}catch(t){z(t)?console.error(`This bricks-buttress install does not include the settings window (the npm package leaves it out); use the standalone bricks-buttress binary or a source checkout, or open ${L(e)} in a browser instead`):B(t)?console.error(`The window's native module (gpuix) is missing next to bricks-buttress; run \`bricks-buttress update\` to reinstall it, or open ${L(e)} in a browser instead (${R(t)})`):console.error(`bricks-buttress ui failed to load the window: ${R(t)}`),process.exit(1)}},H=async e=>{let t=N(e);t.kind===`help`&&(console.log(`
|
|
4
4
|
Usage:
|
|
5
5
|
bricks-buttress ui [-c|--config] <config.toml> [--tab <chat|status|settings>]
|
package/lib/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{a as e,c as t,d as n,i as r,l as i,n as a,o,r as s,s as c,t as l,u}from"./net-OvedjqrB.mjs";import{a as d,c as f,d as p,f as m,i as h,l as g,n as _,o as v,p as y,r as b,s as x,t as S,u as C}from"./config-DiYweJ2m.mjs";import{builtinModules as ee,createRequire as w}from"node:module";import{node as T}from"@elysiajs/node";import{Elysia as te,file as ne,sse as E,t as D}from"elysia";import{ReadableStream as re}from"node:stream/web";import O,{mkdir as ie,rm as k,unlink as A,writeFile as ae}from"node:fs/promises";import oe,{randomBytes as se,randomUUID as j,timingSafeEqual as ce}from"node:crypto";import M from"node:path";import N from"node:os";import le from"bytes";import P,{createWriteStream as ue}from"node:fs";import{fileURLToPath as de,pathToFileURL as fe}from"node:url";import{execSync as pe,spawn as me,spawnSync as he}from"node:child_process";import{ZodError as ge,z as F}from"zod";import{importSPKI as _e,jwtVerify as ve}from"jose";import ye from"ms";import be from"node:vm";import{moduleRunnerTransformSync as xe,transformSync as Se}from"oxc-transform";import Ce from"lodash";import we from"moment";import*as Te from"mathjs";import Ee from"voca";import De from"chroma-js";import Oe from"json5";import ke from"qs";import{nanoid as Ae}from"nanoid";import je from"md5";import{Server as Me}from"@modelcontextprotocol/sdk/server/index.js";import{CallToolRequestSchema as Ne,ListToolsRequestSchema as Pe}from"@modelcontextprotocol/sdk/types.js";import{Readable as Fe}from"node:stream";import{pipeline as Ie}from"node:stream/promises";import Le from"@iarna/toml";import{Buffer as Re}from"node:buffer";import ze from"node:dgram";import{Agent as Be}from"@earendil-works/pi-agent-core";import{createProvider as Ve,uuidv7 as He}from"@earendil-works/pi-ai";import{builtinModels as Ue}from"@earendil-works/pi-ai/providers/all";import{stream as We,streamSimple as Ge}from"@earendil-works/pi-ai/api/openai-completions";import Ke from"node:readline/promises";const qe=!(`Bun`in globalThis),I=e=>new te({adapter:qe?T():void 0,...e}),Je=(e,t,n)=>Buffer.from(JSON.stringify({t:e,d:t,ts:n}),`utf8`),Ye=(e,t,n)=>{if(!e)return null;let r=Math.floor(Date.now()/1e3),i=oe.sign(null,Je(t,n,r),e.privateKey).toString(`base64`);return{t,v:`2.0`,d:n,ts:r,kid:e.kid,sig:i}},Xe=e=>{let{workspace:t,serverKeyPair:n}=e;return!t||!n?null:{kid:n.kid,privateKey:oe.createPrivateKey({key:Buffer.from(n.privateKeyPkcs8,`base64`),format:`der`,type:`pkcs8`})}},Ze=D.Object({id:D.String(),name:D.String(),version:D.String(),address:D.String(),addresses:D.Array(D.String()),port:D.Number(),url:D.String(),generators:D.Array(D.Object({type:D.String(),score:D.Optional(D.Number()),hasGpu:D.Optional(D.Boolean()),usableBytes:D.Optional(D.Number())})),authentication:D.Object({required:D.Boolean(),type:D.String(),kid:D.Optional(D.String()),bound:D.Optional(D.Boolean())}),workspace:D.Optional(D.Object({id:D.String(),name:D.Optional(D.String())})),functions:D.Optional(D.Object({enabled:D.Boolean(),count:D.Number()})),envelope:D.Optional(D.Any())}),Qe=e=>[...new Set([e.address,...e.addresses||[]].filter(e=>typeof e==`string`&&e.length>0))],$e=e=>({id:e.id,name:e.name,version:e.version,address:e.address,addresses:Qe(e),port:e.port,url:e.url,generators:e.generators,authentication:e.authentication,workspace:e.workspace,functions:e.functions});var et=(e,t=null)=>{let n=I(),r=e.autodiscover?.http?.path??`/buttress/info`;return n.get(r,({store:{serverInfo:e}})=>{let n=$e(e),r=Ye(t,`ANNOUNCE`,{info:n});return r?{...n,envelope:r}:n},{response:Ze}),n};const tt=e=>`Bun`in globalThis?globalThis.Bun.file(e):ne(e);let nt=null;const rt=async e=>{if(nt&&nt.kid===e.kid)return nt.key;let t=await _e(e.issuerPublicKey,`EdDSA`);return nt={kid:e.kid,key:t},t},it=/^Bearer\s+(.+)$/i,at=(e,t)=>{if(e){let t=e.authorization||e.Authorization;if(t){let e=t.match(it);return e?e[1].trim():t.trim()}}if(t){let e=t.access_token??t.token;if(typeof e==`string`&&e)return e}return null},L=async(e,t)=>{if(!e||!t)return null;try{let{payload:n}=await ve(e,await rt(t),{algorithms:[`EdDSA`]}),r=n;return r.k!==`ba`||r.w_id!==t.id||r.st!==`ws`&&r.st!==`dev`||!r.sid||!r.exp?null:{workspaceId:r.w_id,subjectType:r.st,subjectId:r.sid,jti:r.jti,exp:r.exp}}catch{return null}},ot=(async({headers:e,query:t,set:n,store:r})=>{let i=r.workspaceState?.workspace;if(!i)return;let a=at(e,t);if(!r.agentsService?.verifyInternalToken?.(a)&&!await L(a,i))return n.status=401,n.headers&&(n.headers[`WWW-Authenticate`]=`Bearer`),{error:{code:`UNAUTHORIZED`,message:`Invalid or missing workspace access token`}}}),st=e=>{if(!e)return null;let t=e[`x-buttress-sess-id`]??e[`X-BUTTRESS-SESS-ID`];return t?t.trim():null},ct=(async({headers:e,query:t,set:n,store:r})=>{let i=r.sessions,a=st(e);if(!a||!i)return n.status=401,{error:{code:`SESSION_REQUIRED`,message:`Missing buttress session id`}};let o=i.get(a);if(!o)return n.status=401,{error:{code:`SESSION_INVALID`,message:`Unknown or expired buttress session`}};let s=r.workspaceState?.workspace;if(s&&o.identity){let r=await L(at(e,t),s);if(!r||r.subjectId!==o.identity.subjectId||r.subjectType!==o.identity.subjectType)return n.status=403,{error:{code:`SESSION_FORBIDDEN`,message:`Token identity mismatch for this session`}}}}),lt=(e,t)=>{let n=e.sessions,r=st(t);if(!r||!n)return null;let i=n.get(r);return i?{sessionId:r,session:i}:null},ut=typeof process<`u`&&process.versions!=null&&process.versions.node!=null;var dt=I().onBeforeHandle(ot).onBeforeHandle(ct).post(`/buttress/upload`,async({body:{file:e},headers:t,store:n})=>{let r=lt(n,t);if(!r)return{ok:!1,error:`Session guard mis-wired`};let{session:i}=r;try{let t=ut?e.stream():await e.arrayBuffer();return{ok:!0,filename:await i.fileManager.storeUpload(e.name,t)}}catch(e){return{ok:!1,error:String(e)}}},{body:D.Object({file:D.File()}),response:D.Object({ok:D.Boolean(),filename:D.Optional(D.String()),error:D.Optional(D.String())})}).get(`/buttress/download/:filename`,async({params:{filename:e},headers:t,store:n,set:r})=>{let i=lt(n,t);if(!i)return r.status=500,`Session guard mis-wired`;let{session:a}=i,o=a.fileManager.resolve(e);return o?tt(o):(r.status=404,`File not found`)},{params:D.Object({filename:D.String()})});const ft=(()=>{let e=new Set,t=[],n=(n,r)=>{if(!e.delete(n)||!r)return;let i=Date.now();t.unshift({type:n.type,repoId:n.repoId,filename:n.filename,totalBytes:null,durationMs:i-n.startedAt,finishedAt:new Date(i).toISOString(),...r}),t.length=Math.min(t.length,20)};return{begin(t){let r={type:t.type,repoId:t.repoId,filename:t.filename??null,startedAt:Date.now(),progress:null};return e.add(r),{progress(t){e.has(r)&&Number.isFinite(t)&&(r.progress=Math.round(Math.min(1,Math.max(0,t))*1e3)/1e3)},setRepoId(e){r.repoId=e},complete:()=>n(r,{success:!0}),fail:e=>n(r,{success:!1,error:e instanceof Error?e.message:String(e)}),discard:()=>n(r,null)}},snapshot(){return{active:[...e].map(e=>({type:e.type,repoId:e.repoId,filename:e.filename,receivedBytes:0,totalBytes:null,bytesPerSecond:null,startedAt:new Date(e.startedAt).toISOString(),progress:e.progress})),recent:[...t]}}}})(),pt=(e,t=200)=>e.length>t?`${e.slice(0,t)}…`:e,R=((e=50)=>{let t={calls:{total:0,failed:0},uploads:{total:0,failed:0,bytes:0},downloads:{total:0,missed:0,bytes:0},auth:{total:0,denied:0},daemons:{invocations:0,failed:0}},n={calls:[],uploads:[],downloads:[],auth:[],daemons:[]},r=null,i=(t,n)=>{t.unshift(n),t.length>e&&(t.length=e)};return{recordCall(e){t.calls.total+=1,e.success||(t.calls.failed+=1),i(n.calls,{...e,error:e.error&&pt(e.error),timestamp:new Date().toISOString()})},recordUpload(e){t.uploads.total+=1,e.success?t.uploads.bytes+=e.size:t.uploads.failed+=1,i(n.uploads,{...e,error:e.error&&pt(e.error),timestamp:new Date().toISOString()})},recordDownload(e){t.downloads.total+=1,e.success?t.downloads.bytes+=e.size??0:t.downloads.missed+=1,i(n.downloads,{...e,path:pt(e.path),timestamp:new Date().toISOString()})},recordDaemonActivity(e){t.daemons.invocations+=1,e.success||(t.daemons.failed+=1),i(n.daemons,{...e,error:e.error&&pt(e.error),timestamp:new Date().toISOString()})},setDaemonsProvider(e){r=e},recordAuth(e){t.auth.total+=1,e.outcome!==`allowed`&&(t.auth.denied+=1),i(n.auth,{...e,path:pt(e.path),timestamp:new Date().toISOString()})},snapshot(){let e=[];try{e=r?.()??[]}catch{}return{counters:{calls:{...t.calls},uploads:{...t.uploads},downloads:{...t.downloads},auth:{...t.auth},daemons:{...t.daemons}},daemons:e,history:{calls:[...n.calls],uploads:[...n.uploads],downloads:[...n.downloads],auth:[...n.auth],daemons:[...n.daemons]}}}}})(),mt=M.dirname(de(import.meta.url)),ht=()=>{try{let e=w(import.meta.url).resolve(`@fugood/buttress-ui/package.json`);return M.join(M.dirname(e),`lib`,`status.html`)}catch{return null}},gt=async()=>{let e=ht(),t=[...e?[e]:[],M.join(mt,`..`,`public`,`status.html`),M.join(mt,`..`,`..`,`public`,`status.html`),M.join(M.dirname(process.execPath),`public`,`status.html`)];return(await Promise.all(t.map(e=>O.access(e).then(()=>e,()=>null)))).find(e=>e!==null)??null},_t=e=>{let{status:t}=e;return t?.getFullStatus?t.getFullStatus():{timestamp:new Date().toISOString(),ggmlLlm:{generators:[],history:{}},ggmlStt:{generators:[],history:{}},onnxStt:{generators:[],history:{}},onnxTts:{generators:[],history:{}},ggmlTts:{generators:[],history:{}},ggmlDecision:{generators:[],history:{}}}},vt=async()=>{let e=await gt();if(!e)return console.error(`[Status] Failed to find status.html in candidate paths`),new Response(`Status page not found`,{status:404,headers:{"Content-Type":`text/plain`}});try{let t=await O.readFile(e,`utf-8`);return new Response(t,{headers:{"Content-Type":`text/html; charset=utf-8`}})}catch(e){return console.error(`[Status] Failed to serve status page:`,e),new Response(`Status page not found`,{status:404,headers:{"Content-Type":`text/plain`}})}},yt=e=>e?.functions?{enabled:!0,count:e.functions.count,daemonCount:e.functions.daemons??0,...R.snapshot()}:{enabled:!1},bt=(e,t)=>{if(!e)return null;let{udp:n,http:r}=e,i=!!(n?.announcements?.enabled||n?.requests?.enabled);return{udpPort:i?n.port??null:null,udpAnnouncing:i&&!!(t.workspace&&t.serverKeyPair),httpPath:r?.enabled?r.path??`/buttress/info`:null}},xt=({config:e,endpoints:t,functionsService:n,agentsService:r,workspaceState:i})=>{let a=r?.list()??[];return{openaiCompat:t.openaiCompat,anthropicMessages:t.anthropicMessages,typesafeCompat:t.typesafeCompat,autodiscover:bt(e.autodiscover,i),functions:{enabled:n!==null,count:n?.stats.count??0,daemons:n?.stats.daemons??0,hotReload:n?.config.hotReload??!1},agents:r?{enabled:!0,count:a.length,names:a}:null}},St=e=>typeof e==`string`&&e.trim()!==``?e:null,Ct=e=>e.map(({type:e,model:t})=>({type:e,repoId:St(t?.repo_id)??St(t?.repository),quantization:St(t?.quantization)})),wt=e=>{let{serverInfo:t,startedAt:n,sessions:r,config:i,host:a}=e;return{id:t.id,name:t.name,version:t.version,port:t.port,startedAt:new Date(n).toISOString(),uptimeMs:Date.now()-n,sessions:{open:r.size},generatorTypes:[...new Set(i.generators.map(e=>e.type))],generators:Ct(i.generators),features:xt(e),host:a,caps:t.generators.map(({type:e,score:t,hasGpu:n,usableBytes:r})=>({type:e,score:t??0,hasGpu:n??!1,usableBytes:r??0}))}};var Tt=I().get(`/status`,vt).get(`/status/`,vt).get(`/buttress/status`,({store:e})=>({..._t(e.backend),functions:yt(e.serverInfo),server:wt(e),downloads:ft.snapshot()}));const Et=[`repo_id`,`revision`,`filename`,`quantization`,`url`,`local_path`],Dt=[`mmproj_repo_id`,`mmproj_revision`,`mmproj_filename`,`mmproj_url`,`mmproj_local_path`],Ot=(e,t)=>{let n=e?.[t];return n==null||n===``?t===`revision`?`main`:null:n},kt=(e,t,n)=>n.every(n=>Ot(e,n)===Ot(t,n)),At=(e,t)=>e?.rank_pooling?`rank pooling needs an embedding context`:t?.embedding||t?.vocab_only?`the LLM is embedding or vocab-only`:Dt.some(t=>Ot(e,t)!=null)&&!kt(e,t,Dt)?`the projectors differ`:null,jt=(e,t)=>{if(t?.backend?.share_llm_context!==!0)return null;let n=t.model,r=(e?.generators||[]).find(e=>e?.type===`ggml-llm`&&kt(e.model,n,Et));return r?At(n,r.model)?null:r:null},Mt=(e,t)=>(e?.generators||[]).find(e=>e?.type===`ggml-decision`&&e.backend?.share_llm_context===!0&&kt(e.model,t,Et))?jt(e,{backend:{share_llm_context:!0},model:t}):null,Nt=e=>{let t=e?.generators||[],n=[];for(let r of t.filter(e=>e?.type===`ggml-decision`)){let i=r.model?.repo_id,a=t.find(e=>e?.type===`ggml-llm`&&kt(e.model,r.model,Et));if(!a)continue;if(r.backend?.share_llm_context!==!0){n.push(`${i}: decisions load their own context (set [generators.backend] share_llm_context = true to share the ggml-llm one)`);continue}let o=At(r.model,a.model);if(o){n.push(`${i}: decisions load their own context (${o})`);continue}n.push(`${i}: decisions share the ggml-llm context`);let s={...e.global?.runtime,...a.runtime,...a.model},c=Number(s.n_parallel)||4,l=Math.floor((Number(s.n_ctx)||0)/c),u=Number(r.runtime?.n_ctx??r.model?.n_ctx)||0;s.kv_unified!==!0&&l>0&&u>l&&n.push(` warning: a decision gets ${l} tokens per slot (n_ctx / n_parallel), less than its n_ctx ${u}; raise the LLM n_ctx or set kv_unified = true`)}return n},Pt=[`ggml-llm`,`mlx-llm`],Ft=m({family:`LLM`,types:Pt}),It={jinja:!0,add_generation_prompt:!0,reasoning_format:`auto`,enable_thinking:!1};function Lt(e,t){return t===`mlx-llm`?e.mlxLlm:e.ggmlLlm}const Rt=y({max:4,resolve:Ft,prepare:async(e,t)=>{await Lt(e,t.type).initContext(t.id,{})}});async function zt(e,t,n=`[LLM]`){return Rt.release(e,t,n)}async function Bt(e,t,n,r=`[LLM]`){return Rt.getOrCreate(e,t,n,r)}function Vt(e){let t=e.timings||{},n=t.prompt_n??t.promptN??0,r=t.cache_n??t.cacheN??0,i=t.predicted_n??t.predictedN??0;return{promptTokens:n||e.prompt_tokens||e.promptTokens||0,cachedTokens:r,completionTokens:i||e.tokens_evaluated||e.tokensEvaluated||e.tokens_predicted||e.tokensPredicted||0}}function Ht(e){return!!(e.stopped_limit||e.stopped_limited||e.context_full)}function Ut(e){let{promptTokens:t,cachedTokens:n,completionTokens:r}=Vt(e),i=t+n;return{prompt_tokens:i,completion_tokens:r,total_tokens:i+r}}const Wt=e=>e?.type===`ggml-decision`,Gt=e=>{let t=new Set,n=new Set;for(let r of(e.generators||[]).filter(Wt)){let e=p(r);t.has(e)&&n.add(e),t.add(e)}return[...n]},Kt=(e,t)=>t===`ggml-llm`?e.ggmlLlm:e.ggmlDecision,qt=m({family:`decision`,types:[`ggml-decision`]}),Jt=y({max:2,resolve:(e,t)=>{let n=qt(e,t),r=jt(e,n.mergedConfig);return r?Ft(e,p(r)):n},prepare:async(e,t)=>{await Kt(e,t.type).initContext(t.id,{})},dispose:async(e,t)=>{t.type===`ggml-llm`&&await e.ggmlLlm.releaseContext(t.id,{})}});async function Yt(e,t,{model:n,...r},i=`[Decision]`){let a=await Jt.getOrCreate(e,t,n,i);try{return await Kt(e,a.type).decide(a.id,{request:r})}finally{await Jt.release(e,a,i)}}var z=class extends Error{param;constructor(e,t){super(t),this.param=e}};const Xt=e=>typeof e==`object`&&!!e&&!Array.isArray(e),Zt=e=>typeof e==`string`&&e.trim()!==``,Qt=e=>/^(0|[1-9]\d{0,9})$/.test(e),$t=(e,t)=>Zt(t)?`${e}: ${t}`:e,en=e=>{if(typeof e==`string`){if(e.trim()===``)throw new z(`input`,"`input` must not be empty");return{state:e}}if(!Array.isArray(e)||e.length===0)throw new z(`input`,"`input` must be a string or a non-empty message array");let t=[],n=[];if(e.forEach((e,r)=>{let i=`input[${r}]`;if(!Xt(e)||e.role!==`user`)throw new z(`${i}.role`,"Only `user` messages are supported");if(e.type!=null&&e.type!==`message`)throw new z(`${i}.type`,"Message `type` must be `message`");let{content:a}=e;if(typeof a==`string`){t.push(a);return}if(!Array.isArray(a))throw new z(`${i}.content`,"`content` must be a string or an array of parts");a.forEach((e,r)=>{let a=`${i}.content[${r}]`;if(e?.type===`input_text`&&typeof e.text==`string`)t.push(e.text);else if(e?.type===`input_image`){if(typeof e.image_url!=`string`||!e.image_url.startsWith(`data:`))throw new z(`${a}.image_url`,`Images must be inline base64 data URLs`);n.push(e.image_url)}else throw new z(`${a}.type`,"Content parts must be `input_text` or `input_image`")})}),n.length>128)throw new z(`input`,`At most 128 images are allowed`);let r=t.filter(e=>e.trim()!==``).join(`
|
|
2
|
+
import{a as e,c as t,d as n,i as r,l as i,n as a,o,r as s,s as c,t as l,u}from"./net-tWFM4EP8.mjs";import{a as d,c as f,d as p,f as m,i as h,l as g,n as _,o as v,p as y,r as b,s as x,t as S,u as C}from"./config-DiYweJ2m.mjs";import{builtinModules as ee,createRequire as w}from"node:module";import{node as T}from"@elysiajs/node";import{Elysia as te,file as ne,sse as E,t as D}from"elysia";import{ReadableStream as re}from"node:stream/web";import O,{mkdir as ie,rm as k,unlink as A,writeFile as ae}from"node:fs/promises";import oe,{randomBytes as se,randomUUID as j,timingSafeEqual as ce}from"node:crypto";import M from"node:path";import N from"node:os";import le from"bytes";import P,{createWriteStream as ue}from"node:fs";import{fileURLToPath as de,pathToFileURL as fe}from"node:url";import{execSync as pe,spawn as me,spawnSync as he}from"node:child_process";import{ZodError as ge,z as F}from"zod";import{importSPKI as _e,jwtVerify as ve}from"jose";import ye from"ms";import be from"node:vm";import{moduleRunnerTransformSync as xe,transformSync as Se}from"oxc-transform";import Ce from"lodash";import we from"moment";import*as Te from"mathjs";import Ee from"voca";import De from"chroma-js";import Oe from"json5";import ke from"qs";import{nanoid as Ae}from"nanoid";import je from"md5";import{Server as Me}from"@modelcontextprotocol/sdk/server/index.js";import{CallToolRequestSchema as Ne,ListToolsRequestSchema as Pe}from"@modelcontextprotocol/sdk/types.js";import{Readable as Fe}from"node:stream";import{pipeline as Ie}from"node:stream/promises";import Le from"@iarna/toml";import{Buffer as Re}from"node:buffer";import ze from"node:dgram";import{Agent as Be}from"@earendil-works/pi-agent-core";import{createProvider as Ve,uuidv7 as He}from"@earendil-works/pi-ai";import{builtinModels as Ue}from"@earendil-works/pi-ai/providers/all";import{stream as We,streamSimple as Ge}from"@earendil-works/pi-ai/api/openai-completions";import Ke from"node:readline/promises";const qe=!(`Bun`in globalThis),I=e=>new te({adapter:qe?T():void 0,...e}),Je=(e,t,n)=>Buffer.from(JSON.stringify({t:e,d:t,ts:n}),`utf8`),Ye=(e,t,n)=>{if(!e)return null;let r=Math.floor(Date.now()/1e3),i=oe.sign(null,Je(t,n,r),e.privateKey).toString(`base64`);return{t,v:`2.0`,d:n,ts:r,kid:e.kid,sig:i}},Xe=e=>{let{workspace:t,serverKeyPair:n}=e;return!t||!n?null:{kid:n.kid,privateKey:oe.createPrivateKey({key:Buffer.from(n.privateKeyPkcs8,`base64`),format:`der`,type:`pkcs8`})}},Ze=D.Object({id:D.String(),name:D.String(),version:D.String(),address:D.String(),addresses:D.Array(D.String()),port:D.Number(),url:D.String(),generators:D.Array(D.Object({type:D.String(),score:D.Optional(D.Number()),hasGpu:D.Optional(D.Boolean()),usableBytes:D.Optional(D.Number())})),authentication:D.Object({required:D.Boolean(),type:D.String(),kid:D.Optional(D.String()),bound:D.Optional(D.Boolean())}),workspace:D.Optional(D.Object({id:D.String(),name:D.Optional(D.String())})),functions:D.Optional(D.Object({enabled:D.Boolean(),count:D.Number()})),envelope:D.Optional(D.Any())}),Qe=e=>[...new Set([e.address,...e.addresses||[]].filter(e=>typeof e==`string`&&e.length>0))],$e=e=>({id:e.id,name:e.name,version:e.version,address:e.address,addresses:Qe(e),port:e.port,url:e.url,generators:e.generators,authentication:e.authentication,workspace:e.workspace,functions:e.functions});var et=(e,t=null)=>{let n=I(),r=e.autodiscover?.http?.path??`/buttress/info`;return n.get(r,({store:{serverInfo:e}})=>{let n=$e(e),r=Ye(t,`ANNOUNCE`,{info:n});return r?{...n,envelope:r}:n},{response:Ze}),n};const tt=e=>`Bun`in globalThis?globalThis.Bun.file(e):ne(e);let nt=null;const rt=async e=>{if(nt&&nt.kid===e.kid)return nt.key;let t=await _e(e.issuerPublicKey,`EdDSA`);return nt={kid:e.kid,key:t},t},it=/^Bearer\s+(.+)$/i,at=(e,t)=>{if(e){let t=e.authorization||e.Authorization;if(t){let e=t.match(it);return e?e[1].trim():t.trim()}}if(t){let e=t.access_token??t.token;if(typeof e==`string`&&e)return e}return null},L=async(e,t)=>{if(!e||!t)return null;try{let{payload:n}=await ve(e,await rt(t),{algorithms:[`EdDSA`]}),r=n;return r.k!==`ba`||r.w_id!==t.id||r.st!==`ws`&&r.st!==`dev`||!r.sid||!r.exp?null:{workspaceId:r.w_id,subjectType:r.st,subjectId:r.sid,jti:r.jti,exp:r.exp}}catch{return null}},ot=(async({headers:e,query:t,set:n,store:r})=>{let i=r.workspaceState?.workspace;if(!i)return;let a=at(e,t);if(!r.agentsService?.verifyInternalToken?.(a)&&!await L(a,i))return n.status=401,n.headers&&(n.headers[`WWW-Authenticate`]=`Bearer`),{error:{code:`UNAUTHORIZED`,message:`Invalid or missing workspace access token`}}}),st=e=>{if(!e)return null;let t=e[`x-buttress-sess-id`]??e[`X-BUTTRESS-SESS-ID`];return t?t.trim():null},ct=(async({headers:e,query:t,set:n,store:r})=>{let i=r.sessions,a=st(e);if(!a||!i)return n.status=401,{error:{code:`SESSION_REQUIRED`,message:`Missing buttress session id`}};let o=i.get(a);if(!o)return n.status=401,{error:{code:`SESSION_INVALID`,message:`Unknown or expired buttress session`}};let s=r.workspaceState?.workspace;if(s&&o.identity){let r=await L(at(e,t),s);if(!r||r.subjectId!==o.identity.subjectId||r.subjectType!==o.identity.subjectType)return n.status=403,{error:{code:`SESSION_FORBIDDEN`,message:`Token identity mismatch for this session`}}}}),lt=(e,t)=>{let n=e.sessions,r=st(t);if(!r||!n)return null;let i=n.get(r);return i?{sessionId:r,session:i}:null},ut=typeof process<`u`&&process.versions!=null&&process.versions.node!=null;var dt=I().onBeforeHandle(ot).onBeforeHandle(ct).post(`/buttress/upload`,async({body:{file:e},headers:t,store:n})=>{let r=lt(n,t);if(!r)return{ok:!1,error:`Session guard mis-wired`};let{session:i}=r;try{let t=ut?e.stream():await e.arrayBuffer();return{ok:!0,filename:await i.fileManager.storeUpload(e.name,t)}}catch(e){return{ok:!1,error:String(e)}}},{body:D.Object({file:D.File()}),response:D.Object({ok:D.Boolean(),filename:D.Optional(D.String()),error:D.Optional(D.String())})}).get(`/buttress/download/:filename`,async({params:{filename:e},headers:t,store:n,set:r})=>{let i=lt(n,t);if(!i)return r.status=500,`Session guard mis-wired`;let{session:a}=i,o=a.fileManager.resolve(e);return o?tt(o):(r.status=404,`File not found`)},{params:D.Object({filename:D.String()})});const ft=(()=>{let e=new Set,t=[],n=(n,r)=>{if(!e.delete(n)||!r)return;let i=Date.now();t.unshift({type:n.type,repoId:n.repoId,filename:n.filename,totalBytes:null,durationMs:i-n.startedAt,finishedAt:new Date(i).toISOString(),...r}),t.length=Math.min(t.length,20)};return{begin(t){let r={type:t.type,repoId:t.repoId,filename:t.filename??null,startedAt:Date.now(),progress:null};return e.add(r),{progress(t){e.has(r)&&Number.isFinite(t)&&(r.progress=Math.round(Math.min(1,Math.max(0,t))*1e3)/1e3)},setRepoId(e){r.repoId=e},complete:()=>n(r,{success:!0}),fail:e=>n(r,{success:!1,error:e instanceof Error?e.message:String(e)}),discard:()=>n(r,null)}},snapshot(){return{active:[...e].map(e=>({type:e.type,repoId:e.repoId,filename:e.filename,receivedBytes:0,totalBytes:null,bytesPerSecond:null,startedAt:new Date(e.startedAt).toISOString(),progress:e.progress})),recent:[...t]}}}})(),pt=(e,t=200)=>e.length>t?`${e.slice(0,t)}…`:e,R=((e=50)=>{let t={calls:{total:0,failed:0},uploads:{total:0,failed:0,bytes:0},downloads:{total:0,missed:0,bytes:0},auth:{total:0,denied:0},daemons:{invocations:0,failed:0}},n={calls:[],uploads:[],downloads:[],auth:[],daemons:[]},r=null,i=(t,n)=>{t.unshift(n),t.length>e&&(t.length=e)};return{recordCall(e){t.calls.total+=1,e.success||(t.calls.failed+=1),i(n.calls,{...e,error:e.error&&pt(e.error),timestamp:new Date().toISOString()})},recordUpload(e){t.uploads.total+=1,e.success?t.uploads.bytes+=e.size:t.uploads.failed+=1,i(n.uploads,{...e,error:e.error&&pt(e.error),timestamp:new Date().toISOString()})},recordDownload(e){t.downloads.total+=1,e.success?t.downloads.bytes+=e.size??0:t.downloads.missed+=1,i(n.downloads,{...e,path:pt(e.path),timestamp:new Date().toISOString()})},recordDaemonActivity(e){t.daemons.invocations+=1,e.success||(t.daemons.failed+=1),i(n.daemons,{...e,error:e.error&&pt(e.error),timestamp:new Date().toISOString()})},setDaemonsProvider(e){r=e},recordAuth(e){t.auth.total+=1,e.outcome!==`allowed`&&(t.auth.denied+=1),i(n.auth,{...e,path:pt(e.path),timestamp:new Date().toISOString()})},snapshot(){let e=[];try{e=r?.()??[]}catch{}return{counters:{calls:{...t.calls},uploads:{...t.uploads},downloads:{...t.downloads},auth:{...t.auth},daemons:{...t.daemons}},daemons:e,history:{calls:[...n.calls],uploads:[...n.uploads],downloads:[...n.downloads],auth:[...n.auth],daemons:[...n.daemons]}}}}})(),mt=M.dirname(de(import.meta.url)),ht=()=>{try{let e=w(import.meta.url).resolve(`@fugood/buttress-ui/package.json`);return M.join(M.dirname(e),`lib`,`status.html`)}catch{return null}},gt=async()=>{let e=ht(),t=[...e?[e]:[],M.join(mt,`..`,`public`,`status.html`),M.join(mt,`..`,`..`,`public`,`status.html`),M.join(M.dirname(process.execPath),`public`,`status.html`)];return(await Promise.all(t.map(e=>O.access(e).then(()=>e,()=>null)))).find(e=>e!==null)??null},_t=e=>{let{status:t}=e;return t?.getFullStatus?t.getFullStatus():{timestamp:new Date().toISOString(),ggmlLlm:{generators:[],history:{}},ggmlStt:{generators:[],history:{}},onnxStt:{generators:[],history:{}},onnxTts:{generators:[],history:{}},ggmlTts:{generators:[],history:{}},ggmlDecision:{generators:[],history:{}}}},vt=async()=>{let e=await gt();if(!e)return console.error(`[Status] Failed to find status.html in candidate paths`),new Response(`Status page not found`,{status:404,headers:{"Content-Type":`text/plain`}});try{let t=await O.readFile(e,`utf-8`);return new Response(t,{headers:{"Content-Type":`text/html; charset=utf-8`}})}catch(e){return console.error(`[Status] Failed to serve status page:`,e),new Response(`Status page not found`,{status:404,headers:{"Content-Type":`text/plain`}})}},yt=e=>e?.functions?{enabled:!0,count:e.functions.count,daemonCount:e.functions.daemons??0,...R.snapshot()}:{enabled:!1},bt=(e,t)=>{if(!e)return null;let{udp:n,http:r}=e,i=!!(n?.announcements?.enabled||n?.requests?.enabled);return{udpPort:i?n.port??null:null,udpAnnouncing:i&&!!(t.workspace&&t.serverKeyPair),httpPath:r?.enabled?r.path??`/buttress/info`:null}},xt=({config:e,endpoints:t,functionsService:n,agentsService:r,workspaceState:i})=>{let a=r?.list()??[];return{openaiCompat:t.openaiCompat,anthropicMessages:t.anthropicMessages,typesafeCompat:t.typesafeCompat,autodiscover:bt(e.autodiscover,i),functions:{enabled:n!==null,count:n?.stats.count??0,daemons:n?.stats.daemons??0,hotReload:n?.config.hotReload??!1},agents:r?{enabled:!0,count:a.length,names:a}:null}},St=e=>typeof e==`string`&&e.trim()!==``?e:null,Ct=e=>e.map(({type:e,model:t})=>({type:e,repoId:St(t?.repo_id)??St(t?.repository),quantization:St(t?.quantization)})),wt=e=>{let{serverInfo:t,startedAt:n,sessions:r,config:i,host:a}=e;return{id:t.id,name:t.name,version:t.version,port:t.port,startedAt:new Date(n).toISOString(),uptimeMs:Date.now()-n,sessions:{open:r.size},generatorTypes:[...new Set(i.generators.map(e=>e.type))],generators:Ct(i.generators),features:xt(e),host:a,caps:t.generators.map(({type:e,score:t,hasGpu:n,usableBytes:r})=>({type:e,score:t??0,hasGpu:n??!1,usableBytes:r??0}))}};var Tt=I().get(`/status`,vt).get(`/status/`,vt).get(`/buttress/status`,({store:e})=>({..._t(e.backend),functions:yt(e.serverInfo),server:wt(e),downloads:ft.snapshot()}));const Et=[`repo_id`,`revision`,`filename`,`quantization`,`url`,`local_path`],Dt=[`mmproj_repo_id`,`mmproj_revision`,`mmproj_filename`,`mmproj_url`,`mmproj_local_path`],Ot=(e,t)=>{let n=e?.[t];return n==null||n===``?t===`revision`?`main`:null:n},kt=(e,t,n)=>n.every(n=>Ot(e,n)===Ot(t,n)),At=(e,t)=>e?.rank_pooling?`rank pooling needs an embedding context`:t?.embedding||t?.vocab_only?`the LLM is embedding or vocab-only`:Dt.some(t=>Ot(e,t)!=null)&&!kt(e,t,Dt)?`the projectors differ`:null,jt=(e,t)=>{if(t?.backend?.share_llm_context!==!0)return null;let n=t.model,r=(e?.generators||[]).find(e=>e?.type===`ggml-llm`&&kt(e.model,n,Et));return r?At(n,r.model)?null:r:null},Mt=(e,t)=>(e?.generators||[]).find(e=>e?.type===`ggml-decision`&&e.backend?.share_llm_context===!0&&kt(e.model,t,Et))?jt(e,{backend:{share_llm_context:!0},model:t}):null,Nt=e=>{let t=e?.generators||[],n=[];for(let r of t.filter(e=>e?.type===`ggml-decision`)){let i=r.model?.repo_id,a=t.find(e=>e?.type===`ggml-llm`&&kt(e.model,r.model,Et));if(!a)continue;if(r.backend?.share_llm_context!==!0){n.push(`${i}: decisions load their own context (set [generators.backend] share_llm_context = true to share the ggml-llm one)`);continue}let o=At(r.model,a.model);if(o){n.push(`${i}: decisions load their own context (${o})`);continue}n.push(`${i}: decisions share the ggml-llm context`);let s={...e.global?.runtime,...a.runtime,...a.model},c=Number(s.n_parallel)||4,l=Math.floor((Number(s.n_ctx)||0)/c),u=Number(r.runtime?.n_ctx??r.model?.n_ctx)||0;s.kv_unified!==!0&&l>0&&u>l&&n.push(` warning: a decision gets ${l} tokens per slot (n_ctx / n_parallel), less than its n_ctx ${u}; raise the LLM n_ctx or set kv_unified = true`)}return n},Pt=[`ggml-llm`,`mlx-llm`],Ft=m({family:`LLM`,types:Pt}),It={jinja:!0,add_generation_prompt:!0,reasoning_format:`auto`,enable_thinking:!1};function Lt(e,t){return t===`mlx-llm`?e.mlxLlm:e.ggmlLlm}const Rt=y({max:4,resolve:Ft,prepare:async(e,t)=>{await Lt(e,t.type).initContext(t.id,{})}});async function zt(e,t,n=`[LLM]`){return Rt.release(e,t,n)}async function Bt(e,t,n,r=`[LLM]`){return Rt.getOrCreate(e,t,n,r)}function Vt(e){let t=e.timings||{},n=t.prompt_n??t.promptN??0,r=t.cache_n??t.cacheN??0,i=t.predicted_n??t.predictedN??0;return{promptTokens:n||e.prompt_tokens||e.promptTokens||0,cachedTokens:r,completionTokens:i||e.tokens_evaluated||e.tokensEvaluated||e.tokens_predicted||e.tokensPredicted||0}}function Ht(e){return!!(e.stopped_limit||e.stopped_limited||e.context_full)}function Ut(e){let{promptTokens:t,cachedTokens:n,completionTokens:r}=Vt(e),i=t+n;return{prompt_tokens:i,completion_tokens:r,total_tokens:i+r}}const Wt=e=>e?.type===`ggml-decision`,Gt=e=>{let t=new Set,n=new Set;for(let r of(e.generators||[]).filter(Wt)){let e=p(r);t.has(e)&&n.add(e),t.add(e)}return[...n]},Kt=(e,t)=>t===`ggml-llm`?e.ggmlLlm:e.ggmlDecision,qt=m({family:`decision`,types:[`ggml-decision`]}),Jt=y({max:2,resolve:(e,t)=>{let n=qt(e,t),r=jt(e,n.mergedConfig);return r?Ft(e,p(r)):n},prepare:async(e,t)=>{await Kt(e,t.type).initContext(t.id,{})},dispose:async(e,t)=>{t.type===`ggml-llm`&&await e.ggmlLlm.releaseContext(t.id,{})}});async function Yt(e,t,{model:n,...r},i=`[Decision]`){let a=await Jt.getOrCreate(e,t,n,i);try{return await Kt(e,a.type).decide(a.id,{request:r})}finally{await Jt.release(e,a,i)}}var z=class extends Error{param;constructor(e,t){super(t),this.param=e}};const Xt=e=>typeof e==`object`&&!!e&&!Array.isArray(e),Zt=e=>typeof e==`string`&&e.trim()!==``,Qt=e=>/^(0|[1-9]\d{0,9})$/.test(e),$t=(e,t)=>Zt(t)?`${e}: ${t}`:e,en=e=>{if(typeof e==`string`){if(e.trim()===``)throw new z(`input`,"`input` must not be empty");return{state:e}}if(!Array.isArray(e)||e.length===0)throw new z(`input`,"`input` must be a string or a non-empty message array");let t=[],n=[];if(e.forEach((e,r)=>{let i=`input[${r}]`;if(!Xt(e)||e.role!==`user`)throw new z(`${i}.role`,"Only `user` messages are supported");if(e.type!=null&&e.type!==`message`)throw new z(`${i}.type`,"Message `type` must be `message`");let{content:a}=e;if(typeof a==`string`){t.push(a);return}if(!Array.isArray(a))throw new z(`${i}.content`,"`content` must be a string or an array of parts");a.forEach((e,r)=>{let a=`${i}.content[${r}]`;if(e?.type===`input_text`&&typeof e.text==`string`)t.push(e.text);else if(e?.type===`input_image`){if(typeof e.image_url!=`string`||!e.image_url.startsWith(`data:`))throw new z(`${a}.image_url`,`Images must be inline base64 data URLs`);n.push(e.image_url)}else throw new z(`${a}.type`,"Content parts must be `input_text` or `input_image`")})}),n.length>128)throw new z(`input`,`At most 128 images are allowed`);let r=t.filter(e=>e.trim()!==``).join(`
|
|
3
3
|
|
|
4
4
|
`);if(r===``&&n.length===0)throw new z(`input`,"`input` has no text or images");return{state:r||(n.length>1?`The attached images.`:`The attached image.`),...n.length>0?{images:n}:{}}},tn=(e,t)=>{let n=new Set;e.forEach((e,r)=>{let{value:i}=e||{};if(typeof i!=`string`&&typeof i!=`boolean`)throw new z(`${t}[${r}].value`,"A choice `value` must be a string or a boolean");let a=`${typeof i}:${i}`;if(n.has(a))throw new z(`${t}[${r}].value`,`Choice values must be unique`);n.add(a)});let r=e.map(({value:e})=>String(e)),i=new Set(r).size===r.length&&!r.some(e=>Qt(e)),a=e.map(({value:e},t)=>({key:i?r[t]:`option_${t+1}`,value:e}));return{options:a,criteria:Object.fromEntries(e.map(({description:e},t)=>[a[t].key,i?Zt(e)?e:null:$t(r[t],e)]))}},nn=e=>{if(!Xt(e))throw new z(null,`Request body must be an object`);let{model:t,input:n,questions:r}=e;if(!Zt(t))throw new z(`model`,"`model` is required");let{state:i,images:a}=en(n);if(!Array.isArray(r)||r.length===0)throw new z(`questions`,"`questions` must be a non-empty array");let o=new Set,s=[],c={};return r.forEach((e,t)=>{let n=`questions[${t}]`;if(!Xt(e))throw new z(n,`A question must be an object`);let{type:r,instructions:i}=e;if(!Zt(i))throw new z(`${n}.instructions`,"`instructions` is required");let a=null;if(e.name!=null){if(typeof e.name!=`string`)throw new z(`${n}.name`,"`name` must be a string");if(o.has(e.name))throw new z(`${n}.name`,`Duplicate question name "${e.name}"`);o.add(e.name),{name:a}=e}let l=`q${t}`;if(r===`predicate`)s.push({id:l,name:a,type:r}),c[l]={type:`noul`,instructions:i};else if(r===`choice`){let{choices:t}=e;if(!Array.isArray(t)||t.length<2||t.length>255)throw new z(`${n}.choices`,`Provide between 2 and 255 choices`);let{options:o,criteria:u}=tn(t,`${n}.choices`);s.push({id:l,name:a,type:r,options:o}),c[l]={type:`choice`,instructions:i,criteria:u}}else if(r===`score`){let{levels:t}=e;if(!Array.isArray(t)||t.length<2||t.length>10)throw new z(`${n}.levels`,`Provide between 2 and 10 levels`);t.forEach((e,t)=>{if(!Zt(e?.label))throw new z(`${n}.levels[${t}].label`,"A level needs a `label`")}),s.push({id:l,name:a,type:r,labels:t.map(e=>e.label)}),c[l]={type:`score`,instructions:i,criteria:t.map(e=>$t(e.label,e.description))}}else throw new z(`${n}.type`,"Question `type` must be `predicate`, `choice` or `score`")}),{model:t,request:{state:i,questions:c,...a?{images:a}:{}},plan:{questions:s}}},B=e=>Number.isFinite(e)?e:0,rn=(e,t,n)=>{let r=t.questions.map(t=>{let n=e.answers?.[t.id]||{},{name:r}=t;if(t.type===`predicate`)return{type:`predicate`,name:r,probability:B(n.noul)};let i=n.probabilities||{};if(t.type===`choice`){let e=t.options.find(e=>e.key===n.choice);return{type:`choice`,name:r,choice:e?e.value:n.choice,probabilities:t.options.map(({key:e,value:t})=>({value:t,probability:B(i[e])})),confidence:B(n.confidence)}}return{type:`score`,name:r,score:B(n.score),probabilities:t.labels.map((e,t)=>({value:t,label:e,probability:B(i[String(t)])})),confidence:B(n.confidence)}}),i=B(e.usage?.input_tokens),a=B(e.usage?.output_tokens);return{model:n,answers:r,usage:{input_tokens:i,input_tokens_details:{cached_tokens:0,cache_write_tokens:0},output_tokens:a,output_tokens_details:{reasoning_tokens:0},total_tokens:i+a}}},an=()=>`chatcmpl-${Date.now()}-${Math.random().toString(36).slice(2,9)}`;async function on(e,t,n,r){let i=e.getReader(),a=``,o=null,s=null,c=`stop`,l={prompt_tokens:0,completion_tokens:0,total_tokens:0};try{let e=!1;for(;!e;){let n=await i.read();if({done:e}=n,e)break;let r=n.value;if(!r)break;let{event:u,data:d}=r;if(u===`token`)d.content!=null&&(a=d.content),d.reasoning_content!=null&&(o=d.reasoning_content);else if(u===`result`)d.content==null?d.text&&(a=d.text):a=d.content,d.reasoning_content!=null&&(o=d.reasoning_content),d.tool_calls?.length>0?(s=d.tool_calls.map((e,n)=>({id:e.id||`call_${t}_${n}`,type:`function`,function:{name:e.function?.name||``,arguments:e.function?.arguments||``}})),c=`tool_calls`):c=d.interrupted||Ht(d)?`length`:`stop`,l=Ut(d);else if(u===`error`)throw Error(d.message)}}finally{C(i)}let u={role:`assistant`,content:a||null};return o&&(u.reasoning_content=o),s&&(u.tool_calls=s),{id:t,object:`chat.completion`,created:n,model:r,choices:[{index:0,message:u,finish_reason:c}],usage:l}}function sn(e,t,n,r){let i=[];return e.forEach((e,a)=>{let o={index:a};n.has(a)||(n.set(a,e.id||`call_${t}_${a}`),o.id=n.get(a),o.type=`function`);let s=e.function?.arguments||``,c=r.get(a)||``,l={};!r.has(a)&&e.function?.name&&(l.name=e.function.name),s.length>c.length&&(l.arguments=s.slice(c.length),r.set(a,s)),Object.keys(l).length>0?(o.function=l,i.push(o)):o.id&&(o.function={name:e.function?.name||``,arguments:``},i.push(o))}),i}async function*cn(e,t,n,r,i){let a=e.getReader(),o=``,s=``,c=new Map,l=new Map;try{let e=!1;for(;!e;){let u=await a.read();if({done:e}=u,e)break;let d=u.value;if(!d)break;let{event:f,data:p}=d;if(f===`token`){let e={};if(p.content!=null){let t=p.content;t.length>o.length&&(e.content=t.slice(o.length),o=t)}if(p.reasoning_content!=null){let t=p.reasoning_content;t.length>s.length&&(e.reasoning_content=t.slice(s.length),s=t)}if(p.tool_calls?.length>0){let n=sn(p.tool_calls,t,l,c);n.length>0&&(e.tool_calls=n)}Object.keys(e).length>0&&(yield E({data:JSON.stringify({id:t,object:`chat.completion.chunk`,created:n,model:r,choices:[{index:0,delta:e,finish_reason:null}]})}))}else if(f===`result`){if(p.tool_calls?.length>0){let e=sn(p.tool_calls,t,l,c);e.length>0&&(yield E({data:JSON.stringify({id:t,object:`chat.completion.chunk`,created:n,model:r,choices:[{index:0,delta:{tool_calls:e},finish_reason:null}]})}))}let e=`stop`;p.tool_calls?.length>0||l.size>0?e=`tool_calls`:(p.interrupted||Ht(p))&&(e=`length`);let a={id:t,object:`chat.completion.chunk`,created:n,model:r,choices:[{index:0,delta:{},finish_reason:e}]};i&&(a.usage=Ut(p)),yield E({data:JSON.stringify(a)})}else f===`error`&&(yield E({data:JSON.stringify({error:{message:p.message,type:`server_error`}})}))}yield E({data:`[DONE]`})}finally{C(a)}}const ln=e=>{let t=e?.model||{};return e.type===`ggml-llm`?!!t.enable_mtmd:Wt(e)?!!(t.mmproj_repo_id||t.mmproj_filename||t.mmproj_url||t.mmproj_local_path):!1},un=(e,t)=>{if(ln(t))return!0;let n=jt(e,t);return n!=null&&ln(n)},dn=e=>{let t=new Map;for(let n of e){let e=Pt.includes(n?.type);if(!e&&!Wt(n))continue;let r=p(n),i=t.get(r)||{image:!1,text:!1,decisions:!1};i.image||=ln(n),e?i.text=!0:i.decisions=!0,t.set(r,i)}let n=Array.from(t).sort(([,e],[,t])=>Number(t.text)-Number(e.text));return new Map(n.map(([e,{image:t,text:n,decisions:r}])=>[e,{input_modalities:t?[`text`,`image`]:[`text`],output_modalities:[...n?[`text`]:[],...r?[`decisions`]:[]]}]))};function fn(){let e=I({prefix:`/oai-compat`});return e.onBeforeHandle(ot),e.get(`/v1/models`,({store:e})=>{let{config:t}=e,n=Array.from(dn(t.generators||[]),([e,t])=>({id:e,object:`model`,created:Math.floor(Date.now()/1e3),owned_by:`local`,architecture:t}));return n.length===0&&n.push({id:`ggml-llm`,object:`model`,created:Math.floor(Date.now()/1e3),owned_by:`local`,architecture:{input_modalities:[`text`],output_modalities:[`text`]}}),{object:`list`,data:n}}),e.post(`/v1/chat/completions`,async function*({body:e,set:t,store:n}){let{config:r,backend:i}=n,{messages:a=[],stream:o=!1,model:s,tools:c,temperature:l,stop:u,top_p:d,max_tokens:f,presence_penalty:p,frequency_penalty:m,tool_choice:h,stream_options:g,enable_thinking:_}=e;if(!a||a.length===0)return t.status=400,{error:{message:`messages is required and must not be empty`,type:`invalid_request_error`}};let v=null;try{v=await Bt(i,r,s,`[OpenAI]`);let e=an(),t=Math.floor(Date.now()/1e3),n=v.repoId||`ggml-llm`,y={...It,messages:a};l!=null&&(y.temperature=l),d!=null&&(y.top_p=d),f!=null&&(y.n_predict=f),u!=null&&(y.stop=Array.isArray(u)?u:[u]),p!=null&&(y.presence_penalty=p),m!=null&&(y.frequency_penalty=m),c!=null&&(y.tools=c),h!=null&&(y.tool_choice=h),y.enable_thinking=_??!1;let b=await Lt(i,v.type).completion(v.id,{options:y});if(!o)return await on(b,e,t,n);yield*cn(b,e,t,n,g?.include_usage===!0)}catch(e){return console.error(`[OpenAI] Chat completion error:`,e),t.status=500,{error:{message:e.message||`Internal server error`,type:`server_error`}}}finally{await zt(i,v,`[OpenAI]`)}},{body:D.Object({model:D.Optional(D.String()),messages:D.Array(D.Any()),stream:D.Optional(D.Boolean()),temperature:D.Optional(D.Number()),top_p:D.Optional(D.Number()),max_tokens:D.Optional(D.Number()),stop:D.Optional(D.Union([D.String(),D.Array(D.String())])),presence_penalty:D.Optional(D.Number()),frequency_penalty:D.Optional(D.Number()),tools:D.Optional(D.Array(D.Any())),tool_choice:D.Optional(D.Any()),stream_options:D.Optional(D.Object({include_usage:D.Optional(D.Boolean())})),enable_thinking:D.Optional(D.Boolean())})}),e.post(`/v1/decisions`,async({body:e,set:t,store:n})=>{let{config:r,backend:i}=n,a=(e,n,r,i=null)=>(t.status=e,{error:{message:r,type:`invalid_request_error`,param:n,code:i}}),o;try{o=nn(e)}catch(e){if(e instanceof z)return a(400,e.param,e.message);throw e}let s=(r.generators||[]).filter(Wt),c=s.map(p),{model:l}=o;if(!c.includes(l))return a(404,`model`,`The decision model "${l}" does not exist. Available: ${c.join(`, `)||`(none)`}`,`model_not_found`);let u=s[c.indexOf(l)];if(o.request.images&&!un(r,u))return a(400,`input`,`The decision model "${l}" takes no images: it has no projector (mmproj) configured`);try{return rn(await Yt(i,r,{model:l,...o.request},`[OpenAI]`),o.plan,l)}catch(e){return console.error(`[OpenAI] Decision failed:`,e?.message||e),t.status=500,{error:{message:e?.message||`Decision failed`,type:`server_error`}}}}),e}const pn=()=>`msg_${Date.now()}${Math.random().toString(36).slice(2,11)}`;function mn(e){let t={},n=[];if(e.system!=null){let t=``;if(typeof e.system==`string`)t=e.system;else if(Array.isArray(e.system))for(let n of e.system)n?.type===`text`&&typeof n.text==`string`&&(t+=n.text);t&&n.push({role:`system`,content:t})}if(!Array.isArray(e.messages))throw Error(`'messages' is required and must be an array`);for(let t of e.messages){let e=t?.role||`user`;if(t?.content==null){if(e===`assistant`)continue;n.push(t);continue}if(typeof t.content==`string`){n.push({role:e,content:t.content});continue}if(!Array.isArray(t.content)){n.push(t);continue}let r=[],i=[],a=[],o=``,s=!1;for(let e of t.content){let t=e?.type||``;if(t===`text`)i.push({type:`text`,text:e.text||``});else if(t===`thinking`)o+=e.thinking||``;else if(t===`image`){let t=e.source||{};if(t.type===`base64`){let e=t.media_type||`image/jpeg`,n=t.data||``;i.push({type:`image_url`,image_url:{url:`data:${e};base64,${n}`}})}else t.type===`url`&&i.push({type:`image_url`,image_url:{url:t.url||``}})}else if(t===`tool_use`)r.push({id:e.id||``,type:`function`,function:{name:e.name||``,arguments:JSON.stringify(e.input??{})}}),s=!0;else if(t===`tool_result`){let t=e.tool_use_id||``,n=``,r=e.content;if(typeof r==`string`)n=r;else if(Array.isArray(r))for(let e of r)e?.type===`text`&&(n+=e.text||``);a.push({role:`tool`,tool_call_id:t,content:n})}}if(i.length>0||s||o){let t={role:e};i.length>0?t.content=i:(s||o)&&(t.content=``),r.length>0&&(t.tool_calls=r),o&&(t.reasoning_content=o),n.push(t)}for(let e of a)n.push(e)}if(t.messages=n,Array.isArray(e.tools)&&(t.tools=e.tools.map(e=>({type:`function`,function:{name:e.name||``,description:e.description||``,parameters:e.input_schema||{}}}))),e.tool_choice&&typeof e.tool_choice==`object`){let n=e.tool_choice.type;n===`auto`?t.tool_choice=`auto`:n===`any`||n===`tool`?t.tool_choice=`required`:n===`none`&&(t.tool_choice=`none`)}else Array.isArray(t.tools)&&t.tools.length>0&&(t.tool_choice=`auto`);e.stop_sequences!=null&&(t.stop=Array.isArray(e.stop_sequences)?e.stop_sequences:[e.stop_sequences]),t.max_tokens=e.max_tokens??4096;for(let n of[`temperature`,`top_p`,`top_k`,`stream`])e[n]!=null&&(t[n]=e[n]);return e.thinking&&typeof e.thinking==`object`&&e.thinking.type===`enabled`&&(t.enable_thinking=!0,e.thinking.budget_tokens!=null&&(t.thinking_budget_tokens=e.thinking.budget_tokens)),t}function hn(e,t){return t?`tool_use`:e.stopping_word||e.stoppingWord?`stop_sequence`:e.interrupted||e.truncated||Ht(e)?`max_tokens`:`end_turn`}function gn(e){let{promptTokens:t,cachedTokens:n,completionTokens:r}=Vt(e);return{cache_read_input_tokens:n,input_tokens:t,output_tokens:r}}async function _n(e,t,n){let r=e.getReader(),i=``,a=``,o=[],s={cache_read_input_tokens:0,input_tokens:0,output_tokens:0},c=`end_turn`,l=null;try{let e=!1;for(;!e;){let t=await r.read();if({done:e}=t,e)break;let n=t.value;if(!n)break;let{event:u,data:d}=n;if(u===`token`)d.content!=null&&(i=d.content),d.reasoning_content!=null&&(a=d.reasoning_content);else if(u===`result`)d.content==null?d.text&&d.reasoning_content==null&&(i=d.text):i=d.content,d.reasoning_content!=null&&(a=d.reasoning_content),Array.isArray(d.tool_calls)&&(o=d.tool_calls),s=gn(d),l=d.stopping_word||d.stoppingWord||null,c=hn(d,o.length>0);else if(u===`error`)throw Error(d.message||`completion error`)}}finally{C(r)}let u=[];a&&u.push({type:`thinking`,thinking:a,signature:``}),i&&u.push({type:`text`,text:i});for(let e of o){let t={};try{t=JSON.parse(e.function?.arguments||`{}`)}catch{t={}}u.push({type:`tool_use`,id:e.id||`toolu_${Math.random().toString(36).slice(2,11)}`,name:e.function?.name||``,input:t})}return{id:t,type:`message`,role:`assistant`,content:u,model:n,stop_reason:c,stop_sequence:l,usage:s}}async function*vn(e,t,n){let r=e.getReader(),i=``,a=``,o=new Map,s=new Map,c=new Map,l=new Set,u=!1,d=!1,f=0,p=-1,m=-1,h=new Map,g={cache_read_input_tokens:0,input_tokens:0,output_tokens:0},_=`end_turn`,v=null,y=!1;function*b(e){for(let n=0;n<e.length;n+=1){let r=e[n],i=r?.function?.arguments||``,a=o.get(n)||``;if(!l.has(n)){let e=r?.id||`toolu_${t}_${n}`,i=r?.function?.name||c.get(n)||``;s.set(n,e),c.set(n,i),h.set(n,f),l.add(n),yield E({event:`content_block_start`,data:JSON.stringify({type:`content_block_start`,index:f,content_block:{type:`tool_use`,id:e,name:i,input:{}}})}),f+=1}let u=h.get(n);i.length>a.length&&(yield E({event:`content_block_delta`,data:JSON.stringify({type:`content_block_delta`,index:u,delta:{type:`input_json_delta`,partial_json:i.slice(a.length)}})}),o.set(n,i))}}try{let e=!1;for(;!e;){let o=await r.read();if({done:e}=o,e)break;let s=o.value;if(!s)break;let{event:c,data:h}=s;if(c===`token`){if(!y){let e=gn(h);yield E({event:`message_start`,data:JSON.stringify({type:`message_start`,message:{id:t,type:`message`,role:`assistant`,content:[],model:n,stop_reason:null,stop_sequence:null,usage:e}})}),y=!0}if(h.reasoning_content!=null){let e=h.reasoning_content;e.length>a.length&&(u||(p=f,f+=1,u=!0,yield E({event:`content_block_start`,data:JSON.stringify({type:`content_block_start`,index:p,content_block:{type:`thinking`,thinking:``}})})),yield E({event:`content_block_delta`,data:JSON.stringify({type:`content_block_delta`,index:p,delta:{type:`thinking_delta`,thinking:e.slice(a.length)}})}),a=e)}if(h.content!=null){let e=h.content;e.length>i.length&&(d||(m=f,f+=1,d=!0,yield E({event:`content_block_start`,data:JSON.stringify({type:`content_block_start`,index:m,content_block:{type:`text`,text:``}})})),yield E({event:`content_block_delta`,data:JSON.stringify({type:`content_block_delta`,index:m,delta:{type:`text_delta`,text:e.slice(i.length)}})}),i=e)}Array.isArray(h.tool_calls)&&h.tool_calls.length>0&&(yield*b(h.tool_calls))}else if(c===`result`){if(!y){let e=gn(h);yield E({event:`message_start`,data:JSON.stringify({type:`message_start`,message:{id:t,type:`message`,role:`assistant`,content:[],model:n,stop_reason:null,stop_sequence:null,usage:e}})}),y=!0}Array.isArray(h.tool_calls)&&h.tool_calls.length>0&&(yield*b(h.tool_calls)),g=gn(h),v=h.stopping_word||h.stoppingWord||null,_=hn(h,l.size>0)}else if(c===`error`){yield E({event:`error`,data:JSON.stringify({type:`error`,error:{type:`api_error`,message:h.message||`completion error`}})});return}}u&&(yield E({event:`content_block_delta`,data:JSON.stringify({type:`content_block_delta`,index:p,delta:{type:`signature_delta`,signature:``}})}),yield E({event:`content_block_stop`,data:JSON.stringify({type:`content_block_stop`,index:p})})),d&&(yield E({event:`content_block_stop`,data:JSON.stringify({type:`content_block_stop`,index:m})}));for(let e of[...l].sort((e,t)=>e-t))yield E({event:`content_block_stop`,data:JSON.stringify({type:`content_block_stop`,index:h.get(e)})});yield E({event:`message_delta`,data:JSON.stringify({type:`message_delta`,delta:{stop_reason:_,stop_sequence:v},usage:{input_tokens:g.input_tokens,cache_read_input_tokens:g.cache_read_input_tokens,output_tokens:g.output_tokens}})}),yield E({event:`message_stop`,data:JSON.stringify({type:`message_stop`})})}finally{C(r)}}function yn(){let e=I({prefix:`/anthropic-messages`});return e.onBeforeHandle(ot),e.post(`/v1/messages`,async function*({body:e,set:t,store:n}){let{config:r,backend:i}=n,a=e,o=null;try{if(!Array.isArray(a.messages)||a.messages.length===0)return t.status=400,{type:`error`,error:{type:`invalid_request_error`,message:`messages is required and must not be empty`}};let e=mn(a);o=await Bt(i,r,a.model,`[Anthropic]`);let n=pn(),s=o.repoId||`ggml-llm`,c={...It,messages:e.messages};e.temperature!=null&&(c.temperature=e.temperature),e.top_p!=null&&(c.top_p=e.top_p),e.top_k!=null&&(c.top_k=e.top_k),e.max_tokens!=null&&(c.n_predict=e.max_tokens),e.stop!=null&&(c.stop=e.stop),e.tools!=null&&(c.tools=e.tools),e.tool_choice!=null&&(c.tool_choice=e.tool_choice),c.enable_thinking=e.enable_thinking??!1,e.thinking_budget_tokens!=null&&(c.thinking_budget_tokens=e.thinking_budget_tokens);let l=await Lt(i,o.type).completion(o.id,{options:c});if(!a.stream)return await _n(l,n,s);yield*vn(l,n,s)}catch(e){return console.error(`[Anthropic] Messages error:`,e),t.status=500,{type:`error`,error:{type:`api_error`,message:e?.message||`Internal server error`}}}finally{await zt(i,o,`[Anthropic]`)}},{body:D.Object({model:D.Optional(D.String()),messages:D.Array(D.Any()),system:D.Optional(D.Any()),max_tokens:D.Optional(D.Number()),stream:D.Optional(D.Boolean()),temperature:D.Optional(D.Number()),top_p:D.Optional(D.Number()),top_k:D.Optional(D.Number()),stop_sequences:D.Optional(D.Union([D.String(),D.Array(D.String())])),tools:D.Optional(D.Array(D.Any())),tool_choice:D.Optional(D.Any()),thinking:D.Optional(D.Any()),metadata:D.Optional(D.Any())})}),e.post(`/v1/messages/count_tokens`,async({body:e,set:t,store:n})=>{let{config:r,backend:i}=n,a=e,o=null;try{let e=mn(a);o=await Bt(i,r,a.model,`[Anthropic]`);let t=Lt(i,o.type),n={messages:e.messages,add_generation_prompt:!0,jinja:!0};e.tools!=null&&(n.tools=e.tools);let s=await t.applyChatTemplate(o.id,n),c=typeof s==`string`?s:s?.prompt||``,l=await t.tokenize(o.id,{text:c,add_special:!0,parse_special:!0});return{input_tokens:(Array.isArray(l)?l:l?.tokens||[]).length}}catch(e){return console.error(`[Anthropic] count_tokens error:`,e),t.status=500,{type:`error`,error:{type:`api_error`,message:e?.message||`Internal server error`}}}finally{await zt(i,o,`[Anthropic]`)}},{body:D.Object({model:D.Optional(D.String()),messages:D.Array(D.Any()),system:D.Optional(D.Any()),tools:D.Optional(D.Array(D.Any())),tool_choice:D.Optional(D.Any())})}),e.get(`/v1/models`,({store:e})=>{let{config:t}=e,n=(t.generators||[]).filter(e=>Pt.includes(e.type)).map(e=>{let t=p(e);return{id:t,type:`model`,display_name:t,created_at:new Date().toISOString()}});return n.length===0&&n.push({id:`ggml-llm`,type:`model`,display_name:`ggml-llm`,created_at:new Date().toISOString()}),{data:n,has_more:!1,first_id:n[0]?.id,last_id:n.at(-1)?.id}}),e}const bn=[`choice`,`score`,`noul`],V=(e,t)=>({loc:[`body`,...e],msg:t,type:`value_error`}),xn=e=>typeof e==`object`&&!!e&&!Array.isArray(e),Sn=e=>{if(!xn(e))return[V([],`Request body must be a JSON object`)];let t=[],{state:n,questions:r,images:i}=e;if((n==null||n===``||![`string`,`object`].includes(typeof n))&&t.push(V([`state`],"`state` must be a non-empty string, object or array")),!xn(r)||Object.keys(r).length===0)t.push(V([`questions`],"`questions` must be a non-empty object"));else for(let[e,n]of Object.entries(r)){let r=[`questions`,e];if(!xn(n)||!bn.includes(n.type)){t.push(V([...r,`type`],`Question type must be one of ${bn}`));continue}let{type:i,criteria:a}=n;if(i===`choice`){let e=xn(a)?Object.keys(a).length:0;(e===0||e>255)&&t.push(V([...r,`criteria`],`A choice needs 1 to 255 options in an object`))}else i===`score`?(!Array.isArray(a)||a.length<2||a.length>10)&&t.push(V([...r,`criteria`],`A score needs an ordered array of 2 to 10 levels`)):a!=null&&!xn(a)&&t.push(V([...r,`criteria`],`Noul criteria must be { true?, false? }`))}return i!==void 0&&(!Array.isArray(i)||i.some(e=>typeof e!=`string`||!e.startsWith(`data:`)))&&t.push(V([`images`],"`images` must be an array of data URLs")),t},Cn=e=>[...new Set((e.generators||[]).filter(Wt).map(p))];function wn(){let e=I({prefix:`/typesafe-compat`});return e.onBeforeHandle(({set:e})=>{e.headers[`x-typesafe-request-id`]=j()}),e.onBeforeHandle(ot),e.get(`/v1/models`,({store:e})=>({models:Cn(e.config).map(e=>({name:e,description:`Buttress decision generator`,release_date:``}))})),e.post(`/v1/systemone`,async({body:e,set:t,store:n})=>{let{config:r,backend:i}=n,a=Sn(e),o=Cn(r),s=e,c=s?.model,l=o.includes(c)?c:void 0;if(a.length===0&&!l){let e=o.join(`, `)||`(none)`;a.push(V([`model`],typeof c==`string`?`Unknown decision model "${c}". Available: ${e}`:`\`model\` is required. Available: ${e}`))}if(a.length>0)return t.status=422,{detail:a};let{state:u,questions:d,images:f}=s;try{return{...await Yt(i,r,{model:l,state:u,questions:d,...f?.length?{images:f}:{}},`[TypeSafe]`),model:l}}catch(e){return console.error(`[TypeSafe] Decision failed:`,e?.message||e),t.status=500,{error:{message:e?.message||`Decision failed`}}}}),e}const Tn=[`.ts`,`.mts`,`.js`,`.mjs`],En=/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/,Dn=new Set([`mcp`,`files`,`upload`]),On=new Set([`cross-site`,`same-site`]),kn=e=>On.has(String(e[`sec-fetch-site`]||``).toLowerCase()),An=(e,t)=>t?t===`*`?!0:e?(Array.isArray(t)?t:[t]).includes(e):!1:!1,jn=e=>{let t=/^\/functions\/([^/]+)\/?$/.exec(e);if(!t)return;let n;try{n=decodeURIComponent(t[1])}catch{return}return Dn.has(n)?void 0:n},Mn=(e,t)=>({error:{code:e,message:t}}),Nn=({allowUnauthenticated:e,allowedOrigins:t,getCustomAuth:n})=>(async({headers:r,query:i,set:a,store:o,request:s,path:c})=>{let l=(c||(s?new URL(s.url).pathname:``)).split(`?`)[0],u=s?.method||`GET`,d=(e,t,n)=>{R.recordAuth({method:u,path:l,outcome:e,mode:t,subject:n})},f=r.origin||r.Origin;if((f||kn(r))&&!An(f,t))return d(`FUNCTIONS_ORIGIN_BLOCKED`),a.status=403,Mn(`FUNCTIONS_ORIGIN_BLOCKED`,`Cross-site browser requests${f?` from origin "${f}"`:``} are not allowed. Add the origin to [functions] cors_allowed_origins to permit browser access.`);let p=null;if(n)try{p=await n()}catch{return d(`FUNCTIONS_AUTH_UNAVAILABLE`),a.status=500,Mn(`FUNCTIONS_AUTH_UNAVAILABLE`,`The custom auth function (_auth) failed to load; calls are rejected until it is fixed.`)}let m=o.workspaceState?.workspace??null,h=at(r,i),g=null;if(!p||p.mode===`both`){let t=p?`both`:`workspace`;if(!m){if(!e)return d(`FUNCTIONS_UNAUTHENTICATED_DISABLED`,t),a.status=403,Mn(`FUNCTIONS_UNAUTHENTICATED_DISABLED`,"Local functions require a workspace binding. Run `bricks buttress bind`, or set [functions] allow_unauthenticated = true to run them unauthenticated.")}else if(g=await L(h,m),!g)return d(`UNAUTHORIZED`,t),a.status=401,a.headers&&(a.headers[`WWW-Authenticate`]=`Bearer`),Mn(`UNAUTHORIZED`,`Invalid or missing workspace access token`);if(!p){d(`allowed`,`workspace`,g?.subjectId);return}}else g=m?await L(h,m):null;let _={method:u,path:l,name:jn(l),headers:r,query:i,token:h,workspaceAuth:{bound:!!m,authenticated:!!g,identity:g}},v;try{v=await p.run(_)}catch(e){return console.warn(`[Functions] custom auth function threw:`,e?.message||e),d(`FUNCTIONS_AUTH_ERROR`,p.mode,g?.subjectId),a.status=500,Mn(`FUNCTIONS_AUTH_ERROR`,`The custom auth function failed`)}let y=typeof v==`object`&&v?v:null;if(v===!0||y?.ok===!0){d(`allowed`,p.mode,g?.subjectId);return}d(`FUNCTIONS_AUTH_REJECTED`,p.mode,g?.subjectId);let b=typeof y?.status==`number`&&y.status>=400&&y.status<=499?y.status:403;return a.status=b,b===401&&a.headers&&(a.headers[`WWW-Authenticate`]=`Bearer`),Mn(`FUNCTIONS_AUTH_REJECTED`,typeof y?.error==`string`&&y.error||`Rejected by the custom auth function`)}),Pn=new Set([`.ts`,`.mts`,`.cts`]),Fn=(e,t)=>{let n=t.map(e=>e.codeframe||e.message||String(e)).join(`
|
|
5
5
|
`);return`Failed to parse ${M.basename(e)}:\n${n}`},In=(e,t)=>{let n=t;if(Pn.has(M.extname(e))){let r=Se(e,t,{sourceType:`module`});if(r.errors?.length)throw SyntaxError(Fn(e,r.errors));n=r.code}let r=xe(e,n);if(r.errors?.length)throw SyntaxError(Fn(e,r.errors));return{code:r.code,deps:r.deps||[],dynamicDeps:r.dynamicDeps||[]}},Ln=Function(`specifier`,`return import(specifier)`),Rn=new Set(ee),zn={sqlite3:()=>import(`sqlite3`),"sqlite-vec":()=>import(`sqlite-vec`)};var Bn=class extends Error{constructor(e){super(e),this.name=`FunctionImportError`}};const Vn=(e={})=>({console,setTimeout,clearTimeout,setInterval,clearInterval,setImmediate,clearImmediate,queueMicrotask,URL,URLSearchParams,TextEncoder,TextDecoder,AbortController,AbortSignal,Buffer,structuredClone,performance,crypto:globalThis.crypto,fetch:globalThis.fetch,atob:globalThis.atob,btoa:globalThis.btoa,Event:globalThis.Event,EventTarget:globalThis.EventTarget,...e}),Hn=async e=>{try{return(await O.stat(e)).isFile()}catch{return!1}},Un=e=>{let t=M.extname(e);if(!t)return[...Tn.map(t=>`${e}${t}`),...Tn.map(t=>M.join(e,`index${t}`))];let n=[e];return t===`.js`&&n.push(`${e.slice(0,-3)}.ts`),t===`.mjs`&&n.push(`${e.slice(0,-4)}.mts`),n},Wn=async(e,t,n)=>{if(e.startsWith(`node:`)){let n=e.slice(5);if(!Rn.has(n))throw new Bn(`Cannot import "${e}" from ${M.basename(t)}: unknown Node builtin.`);return{kind:`builtin`,id:e}}if(Rn.has(e))return{kind:`builtin`,id:`node:${e}`};if(Object.prototype.hasOwnProperty.call(zn,e))return{kind:`package`,id:e};if(!(e.startsWith(`./`)||e.startsWith(`../`))&&!M.isAbsolute(e))throw new Bn(`Cannot import "${e}" from ${M.basename(t)}: local functions may only import Node builtins (e.g. "node:fs/promises"), "sqlite3", "sqlite-vec", and sibling files in the functions directory. Other package imports are not supported.`);let r=M.resolve(M.dirname(t),e),i=M.relative(n,r);if(i.startsWith(`..`)||M.isAbsolute(i))throw new Bn(`Cannot import "${e}" from ${M.basename(t)}: path escapes the functions directory.`);for(let e of Un(r))if(await Hn(e))return{kind:`file`,path:e};throw new Bn(`Cannot import "${e}" from ${M.basename(t)}: file not found.`)},Gn=async(e,{rootDir:t,globals:n={}})=>{let r=be.createContext(Vn(n)),i=new Map,a=new Map,o=new Map,s=e=>{let t=i.get(e);if(t)return Promise.resolve(t);let n=a.get(e);if(n)return n;let r=c(e).finally(()=>{a.delete(e)});return a.set(e,r),r},c=async e=>{let[n,a]=await Promise.all([O.readFile(e,`utf8`),O.stat(e)]);o.set(e,a.mtimeMs);let{code:c}=In(e,n),l={};i.set(e,l);let u=be.runInContext(`(async function (__vite_ssr_exports__, __vite_ssr_import__, __vite_ssr_dynamic_import__, __vite_ssr_exportAll__, __vite_ssr_import_meta__) {
|
|
@@ -675,7 +675,7 @@ Options:
|
|
|
675
675
|
Up to date.`),process.exit(0)),console.log(`\nUpdate available: ${c} -> ${f}. Run 'bricks-buttress update' to install.`),process.exit(1)),m&&(console.log(`
|
|
676
676
|
Already running the latest version.`),await bc({message:`Reinstall anyway?`,defaultValue:!1,autoYes:n})||(console.log(`Skipped.`),process.exit(0)));let h=[`npm`,`bun`,`binary`].includes(l.method);h||console.log(`Cannot auto-detect installer (method: ${l.method}); falling back to binary install.`);let g=h?l.method:`binary`;await bc({message:`Update using "${g}" method?`,defaultValue:!0,autoYes:n})||(console.log(`Cancelled.`),process.exit(0));let _=async e=>{console.log(`\n-> Running ${e} update...`);try{return await no(e,u,l,{ggmlVariant:s}),!0}catch(t){return console.error(`\n${e} update failed: ${t.message}`),!1}},v=await _(g),y=g;!v&&g!==`binary`&&await bc({message:`Try the binary installer as a fallback?`,defaultValue:!0,autoYes:n})&&(y=`binary`,v=await _(`binary`)),v||(console.error(`
|
|
677
677
|
Update failed. See messages above.`),y===`binary`&&console.error(`The previously installed version is still in place.`),process.exit(1)),console.log(`
|
|
678
|
-
Update complete.`),process.exit(0)},Sc=e();var Cc=async()=>{if(process.argv[2]===`update`){await xc(process.argv.slice(3));return}if(process.argv[2]===`agent`){let{runAgentCommand:e}=await import(`./cli-KbjOekMI.mjs`);await e(process.argv.slice(3)),process.exit(0)}if(process.argv[2]===`ui`){let{runUiCommand:e}=await import(`./command-
|
|
678
|
+
Update complete.`),process.exit(0)},Sc=e();var Cc=async()=>{if(process.argv[2]===`update`){await xc(process.argv.slice(3));return}if(process.argv[2]===`agent`){let{runAgentCommand:e}=await import(`./cli-KbjOekMI.mjs`);await e(process.argv.slice(3)),process.exit(0)}if(process.argv[2]===`ui`){let{runUiCommand:e}=await import(`./command-EH7igDhV.mjs`);await e(process.argv.slice(3));return}(process.argv.includes(`--version`)||process.argv.includes(`-v`))&&(console.log(Sc.version),process.exit(0)),(process.argv.includes(`--help`)||process.argv.includes(`-h`))&&(console.log(`
|
|
679
679
|
bricks-buttress v${Sc.version}
|
|
680
680
|
|
|
681
681
|
Buttress server for remote inference with GGML backends.
|
package/lib/mlx-bridge.py
CHANGED
|
@@ -454,6 +454,7 @@ def handle_generate(req_id, params):
|
|
|
454
454
|
**kwargs,
|
|
455
455
|
)
|
|
456
456
|
|
|
457
|
+
should_stop = False
|
|
457
458
|
for response in gen_iter:
|
|
458
459
|
if is_cancelled(req_id):
|
|
459
460
|
clear_cancel(req_id)
|
|
@@ -504,13 +505,22 @@ def handle_generate(req_id, params):
|
|
|
504
505
|
"interrupted": False,
|
|
505
506
|
}
|
|
506
507
|
if last_response:
|
|
508
|
+
generation_tokens = getattr(last_response, "generation_tokens", 0)
|
|
509
|
+
finish_reason = getattr(last_response, "finish_reason", None)
|
|
507
510
|
result_data.update({
|
|
508
511
|
"prompt_tokens": getattr(last_response, "prompt_tokens", 0),
|
|
509
512
|
"prompt_tps": getattr(last_response, "prompt_tps", 0),
|
|
510
|
-
"generation_tokens":
|
|
513
|
+
"generation_tokens": generation_tokens,
|
|
511
514
|
"generation_tps": getattr(last_response, "generation_tps", 0),
|
|
512
515
|
"peak_memory": getattr(last_response, "peak_memory", 0),
|
|
513
|
-
"finish_reason":
|
|
516
|
+
"finish_reason": finish_reason,
|
|
517
|
+
# Same name as llama.node's parallel result, which the completion routes read.
|
|
518
|
+
# mlx_lm reports "length" at max_tokens; mlx_vlm reports no reason, so a run
|
|
519
|
+
# that produced max_tokens tokens without a stop sequence hit the limit.
|
|
520
|
+
"stopped_limit": not should_stop and (
|
|
521
|
+
finish_reason == "length"
|
|
522
|
+
or (finish_reason is None and 0 < max_tokens <= generation_tokens)
|
|
523
|
+
),
|
|
514
524
|
})
|
|
515
525
|
|
|
516
526
|
send({"id": req_id, "event": "result", "data": result_data})
|