@fugood/buttress-server 2.25.9 → 2.25.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -4
- package/config/function-samples/README.md +1 -0
- package/config/function-samples/classify-ticket.ts +73 -0
- package/config/sample.toml +22 -0
- package/lib/agent/codemode-worker.d.ts +1 -0
- package/lib/agent/codemode.d.ts +7 -0
- package/lib/agent/mcp.d.ts +2 -3
- package/lib/agent/sessions.d.ts +21 -8
- package/lib/agent/types.d.ts +16 -0
- package/lib/{cli-CtBbHMYQ.mjs → cli-DMI3uIfZ.mjs} +2 -2
- package/lib/{client-DzfRSFcJ.mjs → client-DEUSDK5d.mjs} +1 -1
- package/lib/codemode-BxD8KKWZ.mjs +112 -0
- package/lib/codemode-worker.mjs +3 -0
- package/lib/config-DtcwsKrn.mjs +2 -0
- package/lib/functions/templates.d.ts +1 -1
- package/lib/functions/types.d.ts +7 -1
- package/lib/index.mjs +143 -62
- package/lib/mcp-Cy5Jhrn5.mjs +5 -0
- package/lib/protocol-RJSV46H1.mjs +351 -0
- package/lib/quickjs.wasm +0 -0
- package/lib/routes/decision-shared.d.ts +16 -0
- package/lib/services/ggml-decision.d.ts +44 -0
- package/lib/services/index.d.ts +3 -0
- package/lib/{tui-DbQ0zW-C.mjs → tui-Bl2ADqOw.mjs} +1 -1
- package/lib/types.d.ts +19 -1
- package/package.json +8 -7
- package/public/status.html +104 -1
- package/lib/agent/session-fs.d.ts +0 -3
- package/lib/config-DbRjQnNp.mjs +0 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fugood/buttress-server",
|
|
3
|
-
"version": "2.25.
|
|
3
|
+
"version": "2.25.11",
|
|
4
4
|
"main": "lib/index.mjs",
|
|
5
5
|
"types": "lib/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
],
|
|
20
20
|
"scripts": {
|
|
21
21
|
"typecheck": "tsc --noEmit",
|
|
22
|
-
"build": "tsdown -c rolldown.config.js --config-loader native && tsc --noCheck --emitDeclarationOnly && cp ../buttress-backend-core/src/backends/mlx-bridge.py lib/mlx-bridge.py",
|
|
22
|
+
"build": "tsdown -c rolldown.config.js --config-loader native && tsc --noCheck --emitDeclarationOnly && cp ../buttress-backend-core/src/backends/mlx-bridge.py lib/mlx-bridge.py && cp ../../node_modules/quickjs-wasi/quickjs.wasm lib/quickjs.wasm",
|
|
23
23
|
"build:dist": "$npm_execpath scripts/build-distribution.js",
|
|
24
24
|
"release": "$npm_execpath scripts/build-distribution.js",
|
|
25
25
|
"release-beta": "$npm_execpath scripts/build-distribution.js --beta",
|
|
@@ -37,12 +37,12 @@
|
|
|
37
37
|
],
|
|
38
38
|
"license": "MIT",
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@earendil-works/pi-agent-core": "^0.
|
|
41
|
-
"@earendil-works/pi-ai": "0.
|
|
42
|
-
"@earendil-works/pi-tui": "^0.
|
|
40
|
+
"@earendil-works/pi-agent-core": "^1.0.0",
|
|
41
|
+
"@earendil-works/pi-ai": "1.0.0",
|
|
42
|
+
"@earendil-works/pi-tui": "^1.0.0",
|
|
43
43
|
"@elysiajs/cors": "^1.1.1",
|
|
44
44
|
"@elysiajs/node": "^1.4.2",
|
|
45
|
-
"@fugood/llama.node": "^1.8.0-rc.
|
|
45
|
+
"@fugood/llama.node": "^1.8.0-rc.9",
|
|
46
46
|
"@fugood/whisper.node": "^1.2.0-rc.0",
|
|
47
47
|
"@huggingface/gguf": "^0.3.2",
|
|
48
48
|
"@iarna/toml": "^3.0.0",
|
|
@@ -69,7 +69,8 @@
|
|
|
69
69
|
"zod": "^3.25.76"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
|
-
"@fugood/
|
|
72
|
+
"@fugood/agent-tool-runtime": "^2.25.10",
|
|
73
|
+
"@fugood/bricks-cli": "^2.25.11",
|
|
73
74
|
"@fugood/bricks-devtools-core": "^2.25.5",
|
|
74
75
|
"tsdown": "^0.22.4",
|
|
75
76
|
"typescript": "^7.0.2"
|
package/public/status.html
CHANGED
|
@@ -603,6 +603,27 @@
|
|
|
603
603
|
</div>
|
|
604
604
|
</div>
|
|
605
605
|
|
|
606
|
+
<!-- GGML Typed Decision Status (hidden unless active) -->
|
|
607
|
+
<div class="card" id="ggmlDecisionCard" style="display:none">
|
|
608
|
+
<div class="card-header">
|
|
609
|
+
<span class="card-title">Typed Decision Status</span>
|
|
610
|
+
<span class="badge badge-info" id="ggmlDecisionGeneratorCount">0 generators</span>
|
|
611
|
+
</div>
|
|
612
|
+
<div id="ggmlDecisionGenerators"></div>
|
|
613
|
+
<div class="section">
|
|
614
|
+
<div class="section-title collapsible" onclick="toggleSection(this)">Model Load History</div>
|
|
615
|
+
<div class="collapsible-content" id="ggmlDecisionModelHistory">
|
|
616
|
+
<div class="empty-state">No model load history</div>
|
|
617
|
+
</div>
|
|
618
|
+
</div>
|
|
619
|
+
<div class="section">
|
|
620
|
+
<div class="section-title collapsible" onclick="toggleSection(this)">Decision History</div>
|
|
621
|
+
<div class="collapsible-content" id="ggmlDecisionHistory">
|
|
622
|
+
<div class="empty-state">No decision history</div>
|
|
623
|
+
</div>
|
|
624
|
+
</div>
|
|
625
|
+
</div>
|
|
626
|
+
|
|
606
627
|
<!-- Local Functions (hidden unless enabled) -->
|
|
607
628
|
<div class="card" id="functionsCard" style="display:none">
|
|
608
629
|
<div class="card-header">
|
|
@@ -1049,6 +1070,54 @@
|
|
|
1049
1070
|
})
|
|
1050
1071
|
}
|
|
1051
1072
|
|
|
1073
|
+
function renderGgmlDecisionGenerators(generators) {
|
|
1074
|
+
const container = document.getElementById('ggmlDecisionGenerators')
|
|
1075
|
+
document.getElementById('ggmlDecisionGeneratorCount').textContent =
|
|
1076
|
+
`${generators.length} generator${generators.length !== 1 ? 's' : ''}`
|
|
1077
|
+
|
|
1078
|
+
withScrollPreserve(container, () => {
|
|
1079
|
+
if (generators.length === 0) {
|
|
1080
|
+
container.innerHTML = '<div class="empty-state">No typed decision generators loaded</div>'
|
|
1081
|
+
return
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
container.innerHTML = `
|
|
1085
|
+
<div class="table-wrapper">
|
|
1086
|
+
<div class="table-inner">
|
|
1087
|
+
<table>
|
|
1088
|
+
<thead>
|
|
1089
|
+
<tr>
|
|
1090
|
+
<th>ID</th>
|
|
1091
|
+
<th>Model</th>
|
|
1092
|
+
<th>Type</th>
|
|
1093
|
+
<th>Quant</th>
|
|
1094
|
+
<th>Variant</th>
|
|
1095
|
+
<th>GPU</th>
|
|
1096
|
+
<th>Model Size</th>
|
|
1097
|
+
<th>Context</th>
|
|
1098
|
+
</tr>
|
|
1099
|
+
</thead>
|
|
1100
|
+
<tbody>
|
|
1101
|
+
${generators.map(g => `
|
|
1102
|
+
<tr>
|
|
1103
|
+
<td class="mono">${escapeHtml(g.id?.slice(0, 8) || '-')}</td>
|
|
1104
|
+
<td>${formatModelName(g.repoId)}${g.mmprojFilename ? '<br><span style="font-size:11px;color:var(--text-muted)">+ ' + escapeHtml(g.mmprojFilename) + '</span>' : ''}</td>
|
|
1105
|
+
<td>${escapeHtml(g.decisionType || '-')}</td>
|
|
1106
|
+
<td>${escapeHtml(g.quantization || '-')}</td>
|
|
1107
|
+
<td>${escapeHtml(g.variant || '-')}</td>
|
|
1108
|
+
<td><span class="badge ${g.useGpu ? 'badge-success' : 'badge-info'}">${g.useGpu ? 'yes' : 'no'}</span></td>
|
|
1109
|
+
<td>${formatBytes(g.modelBytes)}</td>
|
|
1110
|
+
<td>${g.hasContext ? '<span class="badge badge-success">loaded</span>' : '-'}</td>
|
|
1111
|
+
</tr>
|
|
1112
|
+
`).join('')}
|
|
1113
|
+
</tbody>
|
|
1114
|
+
</table>
|
|
1115
|
+
</div>
|
|
1116
|
+
</div>
|
|
1117
|
+
`
|
|
1118
|
+
})
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1052
1121
|
// Escape HTML
|
|
1053
1122
|
function escapeHtml(str) {
|
|
1054
1123
|
if (str == null) return ''
|
|
@@ -1077,6 +1146,7 @@
|
|
|
1077
1146
|
const onnxStt = status.onnxStt || {}
|
|
1078
1147
|
const onnxTts = status.onnxTts || {}
|
|
1079
1148
|
const ggmlTts = status.ggmlTts || {}
|
|
1149
|
+
const ggmlDecision = status.ggmlDecision || {}
|
|
1080
1150
|
|
|
1081
1151
|
// LLM
|
|
1082
1152
|
renderLlmGenerators(llm.generators || [])
|
|
@@ -1288,6 +1358,39 @@
|
|
|
1288
1358
|
},
|
|
1289
1359
|
])
|
|
1290
1360
|
|
|
1361
|
+
// GGML typed decisions
|
|
1362
|
+
const hasGgmlDecision = (ggmlDecision.generators?.length > 0) ||
|
|
1363
|
+
(ggmlDecision.history?.modelLoads?.length > 0) ||
|
|
1364
|
+
(ggmlDecision.history?.decisions?.length > 0)
|
|
1365
|
+
document.getElementById('ggmlDecisionCard').style.display = hasGgmlDecision ? '' : 'none'
|
|
1366
|
+
renderGgmlDecisionGenerators(ggmlDecision.generators || [])
|
|
1367
|
+
|
|
1368
|
+
renderHistory('ggmlDecisionModelHistory', ggmlDecision.history?.modelLoads || [], [
|
|
1369
|
+
{ label: 'Time', render: i => `<span class="timestamp">${formatRelativeTime(i.timestamp)}</span>` },
|
|
1370
|
+
{ label: 'Model', render: i => formatModelName(i.repoId) },
|
|
1371
|
+
{ label: 'Type', render: i => escapeHtml(i.decisionType || '-') },
|
|
1372
|
+
{ label: 'Quant', render: i => escapeHtml(i.quantization || '-') },
|
|
1373
|
+
{ label: 'Variant', render: i => escapeHtml(i.variant || '-') },
|
|
1374
|
+
{ label: 'Duration', render: i => `${(i.durationMs / 1000).toFixed(1)}s` },
|
|
1375
|
+
{ label: 'Status', render: i => i.success ?
|
|
1376
|
+
'<span class="badge badge-success">Success</span>' :
|
|
1377
|
+
`<span class="badge badge-error">Failed: ${escapeHtml(i.error || 'Unknown')}</span>`
|
|
1378
|
+
},
|
|
1379
|
+
])
|
|
1380
|
+
|
|
1381
|
+
renderHistory('ggmlDecisionHistory', ggmlDecision.history?.decisions || [], [
|
|
1382
|
+
{ label: 'Time', render: i => `<span class="timestamp">${formatRelativeTime(i.timestamp)}</span>` },
|
|
1383
|
+
{ label: 'Model', render: i => formatModelName(i.repoId) },
|
|
1384
|
+
{ label: 'Questions', render: i => i.questionCount ?? '-' },
|
|
1385
|
+
{ label: 'Images', render: i => i.imageCount || '-' },
|
|
1386
|
+
{ label: 'Tokens', render: i => i.inputTokens ?? '-' },
|
|
1387
|
+
{ label: 'Duration', render: i => `${i.durationMs} ms` },
|
|
1388
|
+
{ label: 'Status', render: i => i.success ?
|
|
1389
|
+
'<span class="badge badge-success">Success</span>' :
|
|
1390
|
+
`<span class="badge badge-error">Failed: ${escapeHtml(i.error || 'Unknown')}</span>`
|
|
1391
|
+
},
|
|
1392
|
+
])
|
|
1393
|
+
|
|
1291
1394
|
renderFunctionsStatus(status.functions || {})
|
|
1292
1395
|
}
|
|
1293
1396
|
|
|
@@ -1447,7 +1550,7 @@
|
|
|
1447
1550
|
const response = await fetch('/buttress/status')
|
|
1448
1551
|
const data = await response.json()
|
|
1449
1552
|
// Direct JSON response (not tRPC wrapped)
|
|
1450
|
-
if (data.ggmlLlm || data.ggmlStt || data.mlxLlm || data.onnxStt || data.onnxTts || data.ggmlTts) {
|
|
1553
|
+
if (data.ggmlLlm || data.ggmlStt || data.mlxLlm || data.onnxStt || data.onnxTts || data.ggmlTts || data.ggmlDecision) {
|
|
1451
1554
|
updateUI(data)
|
|
1452
1555
|
setConnectionStatus('connected')
|
|
1453
1556
|
}
|
package/lib/config-DbRjQnNp.mjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import e from"node:path";import t from"node:os";import n from"bytes";import r from"ms";import i from"node-machine-id";const a=(e={},t={})=>{let n=Array.isArray(e)?[...e]:{...e};return Object.entries(t||{}).forEach(([e,t])=>{t&&typeof t==`object`&&!Array.isArray(t)?n[e]=a(n[e]||{},t):n[e]=t}),n},o=e=>e&&typeof e==`object`?structuredClone(e):null,s=(e,t)=>{let n=o(e)||{},r=o(t)||{};return a(n,r)},c=(e,t)=>a(structuredClone(e.global),t||{}),l=(e,t,n,r)=>{if(e.generators.length>0){let i=e.generators.filter(e=>e?.type===n);if(i.length>0&&r){let a=i.find(e=>t.getModelIdentifier(n,e)===r);if(a)return c(e,a)}}return Object.keys(e.global).length>0?c(e,{}):null},u={udp:{port:8089,announcements:{enabled:!0,interval:5e3},requests:{enabled:!0,responseDelay:100}},http:{enabled:!0,path:`/buttress/info`,cors:!0}},d=e=>e?e===!0?{...u}:a(u,e):null,f=(e,t)=>{if(!e.generators||e.generators.length===0)return t.map(e=>({type:e}));let n=new Set;return e.generators.forEach(e=>{e.type&&n.add(e.type)}),n.size===0?t.map(e=>({type:e})):Array.from(n).map(e=>({type:e}))},p=(e,t,n)=>e===void 0?n:typeof e==`number`?e:t(e)??n,m=6e4,h=1024*1024*50,g=s=>{let c=i.machineIdSync(),l={server:{id:`buttress-${c}`,name:`Buttress Server (${c.slice(-8)})`,port:2080,temp_file_dir:e.join(t.tmpdir(),`.buttress`),session_timeout:m,max_body_size:h},autodiscover:!1},u=a(l,o(s)||{}),f=Array.isArray(u.generators)?u.generators:[],{server:g,generators:_,autodiscover:v,...y}=u;return{autodiscover:d(v),server:{id:g.id,name:g.name,port:g.port,log_level:g.log_level,temp_file_dir:g.temp_file_dir,max_body_size:p(g.max_body_size,n.parse,h),session_timeout:p(g.session_timeout,e=>r(e),m)},global:y,generators:f}},_=/^[A-Za-z0-9_-]{1,64}$/,v=n=>n===`~`||n.startsWith(`~/`)?e.join(t.homedir(),n.slice(1)):n;var y=class extends Error{constructor(e){super(`[Agents] ${e}`),this.name=`AgentsConfigError`}};const b=(e,t,n)=>{if(t==null)return n;if(t===0||t===!1)return null;if(typeof t==`number`){if(Number.isFinite(t)&&t>0)return t}else if(typeof t==`string`){let e;try{e=r(t)}catch{e=void 0}if(typeof e==`number`&&e>0)return e}throw new y(`agents_options.${e} must be a duration string (e.g. "30d") or a positive number of milliseconds; use 0 to disable it`)},x=(e,t)=>{if(t==null)return{};if(typeof t!=`object`||Array.isArray(t))throw new y(`agent "${e}": mcp_servers must be a table`);let n={};for(let[r,i]of Object.entries(t)){if(!_.test(r))throw new y(`agent "${e}": MCP server name "${r}" must match ${_}`);let t=!!(typeof i?.url==`string`&&i.url),a=!!(typeof i?.command==`string`&&i.command);if(t===a)throw new y(`agent "${e}": MCP server "${r}" needs exactly one of url / command`);n[r]={url:t?i.url:void 0,headers:i?.headers&&typeof i.headers==`object`?i.headers:void 0,command:a?i.command:void 0,args:Array.isArray(i?.args)?i.args.map(String):void 0,env:i?.env&&typeof i.env==`object`?i.env:void 0,optional:i?.optional===!0}}return n},S=(e,t,n)=>{let r=e?.name;if(typeof r!=`string`||!_.test(r))throw new y(`[[agents]] #${t+1}: name is required and must match ${_}`);let i=e?.model;if(typeof i!=`string`||!i.includes(`/`))throw new y(`agent "${r}": model must be "provider/model-id" (e.g. "buttress/ggml-org/gpt-oss-20b-GGUF")`);let a=i.indexOf(`/`),o=i.slice(0,a),s=i.slice(a+1);if(!o||!s)throw new y(`agent "${r}": invalid model reference "${i}"`);if(o===`buttress`&&!n.generators.filter(e=>e.type===`ggml-llm`||e.type===`mlx-llm`).some(e=>(e.model?.repo_id||e.model?.repository)===s))throw new y(`agent "${r}": model "${s}" does not match any configured [[generators]] ggml-llm/mlx-llm repo_id`);if(e?.system_prompt!=null&&e?.system_prompt_file!=null)throw new y(`agent "${r}": set system_prompt or system_prompt_file, not both`);let c=[];if(e?.tools!=null){if(!Array.isArray(e.tools))throw new y(`agent "${r}": tools must be an array of function names`);for(let t of e.tools){if(typeof t!=`string`||!_.test(t))throw new y(`agent "${r}": tool name "${t}" must match ${_} (local function names only)`);if(c.includes(t))throw new y(`agent "${r}": duplicate tool "${t}"`);c.push(t)}}if(e?.local_devices!==void 0&&typeof e.local_devices!=`boolean`)throw new y(`agent "${r}": local_devices must be a boolean`);let l=Number(e?.max_turns??30);if(!Number.isInteger(l)||l<1)throw new y(`agent "${r}": max_turns must be a positive integer`);let u=null;if(e?.max_tokens_per_run!=null&&(u=Number(e.max_tokens_per_run),!Number.isInteger(u)||u<1))throw new y(`agent "${r}": max_tokens_per_run must be a positive integer`);let{name:d,model:f,system_prompt:p,system_prompt_file:m,tools:h,local_devices:g=!1,mcp_servers:v,max_turns:b,max_tokens_per_run:S,thinking:C,...w}=e;return{name:r,provider:o,modelId:s,modelRef:i,systemPrompt:typeof p==`string`?p:null,systemPromptFile:typeof m==`string`?m:null,tools:c,localDevices:g,mcpServers:x(r,v),maxTurns:l,maxTokensPerRun:u,thinking:typeof C==`string`?C:void 0,generation:w}},C=(t,{configDir:n=process.cwd()}={})=>{let r=t.global||{},i=r.agents;if(i==null)return null;if(!Array.isArray(i))throw new y(`agents must be an array of tables ([[agents]])`);if(i.length===0)return null;let a=i.map((e,n)=>S(e,n,t)),o=new Set;for(let e of a){if(o.has(e.name))throw new y(`duplicate agent name "${e.name}"`);o.add(e.name)}let s=r.agents_options||{},c=typeof s.sessions_dir==`string`&&s.sessions_dir?s.sessions_dir:`./.buttress-agent/sessions`,l=v(c),u=e.isAbsolute(l)?l:e.resolve(n,l);for(let t of a)if(t.systemPromptFile){let r=v(t.systemPromptFile);t.systemPromptFile=e.isAbsolute(r)?r:e.resolve(n,r)}let d=Number(s.max_depth??2);if(!Number.isInteger(d)||d<1)throw new y(`agents_options.max_depth must be a positive integer`);let f=s.session_max_count,p=500;if(f===0||f===!1)p=null;else if(f!=null&&(p=Number(f),!Number.isInteger(p)||p<1))throw new y(`agents_options.session_max_count must be a positive integer`);return{agents:a,configDir:n,sessionsDir:u,runtimeTokenFile:e.join(e.dirname(u),`runtime-token`),sessionMaxAgeMs:b(`session_max_age`,s.session_max_age,2592e6),sessionMaxCount:p,maxDepth:d,allowUnauthenticated:s.allow_unauthenticated===!0}};export{g as a,o as i,C as n,l as o,s as r,f as s,y as t};
|