OverviewFilesCommitsIssuesDocsReleasesPeers

godot-keet commits / 401ccb6

Add combo algos to test suite + keet-daemon has hierarchical clustering
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
401ccb6ce54feb243f53c6d49e1c99b2182321aa
by plankatron <fleeky@gmail.com> · 2026-02-23T13:19:12.000Z

Changed files (1) +57 -0

tests/test-algos.mjs +57 -0
……@@ -0,0 +1,57 @@
1import Pipe from 'bare-pipe'
2import FramedStream from 'framed-stream'
3import b4a from 'b4a'
4
5let rpcId = 0
6const pipe = new Pipe('/tmp/keet-daemon.sock')
7const framed = new FramedStream(pipe)
8const pending = new Map()
9
10framed.on('data', buf => {
11 const msg = JSON.parse(b4a.toString(buf))
12 const cb = pending.get(msg.id)
13 if (cb) { pending.delete(msg.id); cb(msg) }
14})
15
16function call (method, params = {}) {
17 return new Promise((resolve, reject) => {
18 const id = ++rpcId
19 const timeout = setTimeout(() => { pending.delete(id); reject(new Error('timeout: ' + method)) }, 15000)
20 pending.set(id, msg => {
21 clearTimeout(timeout)
22 if (msg.ok !== undefined && msg.data !== undefined) msg.result = msg.data
23 if (msg.error) reject(new Error(msg.error.message || String(msg.error)))
24 else resolve(msg.result)
25 })
26 framed.write(b4a.from(JSON.stringify({ jsonrpc: '2.0', id, method, params })))
27 })
28}
29
30const algos = [
31 {name: "category", features: [{type: "category", weight: 1.0}], threshold: 0.1, minSize: 2},
32 {name: "creator", features: [{type: "creator", weight: 1.0}], threshold: 0.1, minSize: 2},
33 {name: "activity", features: [{type: "activity", weight: 1.0}], threshold: 0.1, minSize: 2},
34 {name: "size", features: [{type: "size", weight: 1.0}], threshold: 0.1, minSize: 2},
35 {name: "topics", features: [{type: "topics", weight: 1.0}], threshold: 0.3, minSize: 2},
36 {name: "members", features: [{type: "members", weight: 1.0}], threshold: 0.5, minSize: 2},
37 // Combos — hierarchical clustering
38 {name: "creator+category", features: [{type: "creator", weight: 1.0}, {type: "category", weight: 0.8}], threshold: 0.3, minSize: 2},
39 {name: "activity+topics", features: [{type: "activity", weight: 1.0}, {type: "topics", weight: 0.7}], threshold: 0.3, minSize: 2},
40 {name: "category+activity", features: [{type: "category", weight: 1.0}, {type: "activity", weight: 0.6}], threshold: 0.2, minSize: 2},
41]
42
43for (const algo of algos) {
44 try {
45 const result = await call('graph.compute', { force: true, algo })
46 const clusters = result.clusters || []
47 const edges = result.edges || []
48 const clusterSummary = clusters.map(c => `${c.label || '?'}(${(c.rooms||[]).length})`).join(', ')
49 console.log(`\n${algo.name}: ${clusters.length} clusters, ${edges.length} edges`)
50 console.log(` clusters: ${clusterSummary || 'none'}`)
51 } catch (err) {
52 console.log(`\n${algo.name}: ERROR — ${err.message}`)
53 }
54}
55
56pipe.destroy()
57setTimeout(() => Bare.exit(0), 500)