mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-28 18:19:28 +00:00
probe-model-picker.mjs times pill-click → menu painted over N rounds; profile-model-picker.mjs wraps one open in a CPU profile with a top-self-time table. Both attach to the perf:serve instance.
80 lines
3 KiB
JavaScript
80 lines
3 KiB
JavaScript
// Model picker open latency probe: click the composer model pill, time until
|
|
// the dropdown/dialog content paints, repeat. Run with --cpuprofile via the
|
|
// harness runner once promoted; standalone for iteration.
|
|
// node scripts/perf/probe-model-picker.mjs [--port 9222] [--rounds 5]
|
|
import { CDP } from './perf/lib/cdp.mjs'
|
|
|
|
const args = process.argv.slice(2)
|
|
const flag = name => {
|
|
const i = args.indexOf(`--${name}`)
|
|
return i >= 0 ? args[i + 1] : undefined
|
|
}
|
|
const port = Number(flag('port') ?? 9222)
|
|
const rounds = Number(flag('rounds') ?? 5)
|
|
const sleep = ms => new Promise(r => setTimeout(r, ms))
|
|
|
|
const cdp = await CDP.connect({ port })
|
|
await cdp.send('Runtime.enable')
|
|
|
|
// Find the pill (dropdown path) — the composer model selector button.
|
|
const PILL = `(() => {
|
|
const btns = [...document.querySelectorAll('button[aria-label]')]
|
|
const pill = btns.find(b => /model/i.test(b.getAttribute('aria-label') || '') && b.closest('[data-slot]'))
|
|
return pill ? (pill.getAttribute('aria-label') || 'found') : null
|
|
})()`
|
|
|
|
console.log('pill:', await cdp.eval(PILL))
|
|
|
|
const MEASURE = `
|
|
(async () => {
|
|
const btns = [...document.querySelectorAll('button[aria-label]')]
|
|
const pill = btns.find(b => /model/i.test(b.getAttribute('aria-label') || ''))
|
|
if (!pill) return JSON.stringify({ error: 'no pill' })
|
|
|
|
const t0 = performance.now()
|
|
pill.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true }))
|
|
pill.dispatchEvent(new PointerEvent('pointerup', { bubbles: true }))
|
|
pill.click()
|
|
|
|
// Wait for menu/dialog content to exist AND paint (double rAF after found).
|
|
const found = await new Promise(resolve => {
|
|
const deadline = performance.now() + 5000
|
|
const check = () => {
|
|
const menu = document.querySelector('[role="menu"], [role="dialog"] [cmdk-list]')
|
|
if (menu && menu.childElementCount > 0) {
|
|
requestAnimationFrame(() => requestAnimationFrame(() => resolve(performance.now())))
|
|
return
|
|
}
|
|
if (performance.now() > deadline) { resolve(null); return }
|
|
requestAnimationFrame(check)
|
|
}
|
|
check()
|
|
})
|
|
|
|
const openMs = found ? found - t0 : null
|
|
const rows = document.querySelectorAll('[role="menu"] [role="menuitem"], [cmdk-item]').length
|
|
|
|
// Close: Escape.
|
|
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }))
|
|
const menu = document.querySelector('[role="menu"], [role="dialog"]')
|
|
menu?.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }))
|
|
await new Promise(r => setTimeout(r, 300))
|
|
|
|
return JSON.stringify({ openMs, rows })
|
|
})()
|
|
`
|
|
|
|
const samples = []
|
|
|
|
for (let i = 0; i < rounds; i++) {
|
|
const raw = await cdp.eval(MEASURE, { awaitPromise: true })
|
|
const r = JSON.parse(raw)
|
|
console.log(`round ${i}:`, r)
|
|
if (typeof r.openMs === 'number') samples.push(r.openMs)
|
|
await sleep(500)
|
|
}
|
|
|
|
samples.sort((a, b) => a - b)
|
|
console.log('\nopen latency ms — min/median/max:',
|
|
Math.round(samples[0]), '/', Math.round(samples[Math.floor(samples.length / 2)]), '/', Math.round(samples.at(-1)))
|
|
cdp.close()
|