init
This commit is contained in:
commit
0964819cdc
34 files changed
+7912
No files matched your search
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* What focus scoping does to the index, measured without an embedding endpoint.
|
||||
*
|
||||
* The semantic half of the claim — that a focused search ranks better — needs a
|
||||
* live model. This half does not: index size, per-scope chunk counts, and the
|
||||
* cold-start cost they imply are all arithmetic over the chunker, which is
|
||||
* deterministic and offline.
|
||||
*
|
||||
* The cold-start projection uses the throughput actually measured against the
|
||||
* real endpoint on this repository: 2,257 chunks embedded in 90s, about 25
|
||||
* chunks/s. It is a projection from a real figure, not a guess, and it is
|
||||
* labelled as a projection.
|
||||
*
|
||||
* node scripts/scope-measure.mjs [workspace]
|
||||
*/
|
||||
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import { join } from 'node:path'
|
||||
|
||||
import { chunkFile } from '../lib/chunk.js'
|
||||
import { listCodeFiles } from '../lib/walk.js'
|
||||
|
||||
const WORKSPACE = process.argv[2] ?? '/root/workspaces/ha-runtime'
|
||||
|
||||
/** Chunks per second, measured on this repository against the live endpoint. */
|
||||
const MEASURED_CHUNKS_PER_SECOND = 2257 / 90
|
||||
|
||||
/** Count files and chunks for one directory, relative to a base for display. */
|
||||
async function measure(root, label) {
|
||||
const { files } = await listCodeFiles(root)
|
||||
let chunks = 0
|
||||
let lines = 0
|
||||
let skipped = 0
|
||||
for (const file of files) {
|
||||
let content
|
||||
try {
|
||||
content = await readFile(file.absolute, 'utf8')
|
||||
} catch {
|
||||
continue
|
||||
}
|
||||
lines += content.split('\n').length
|
||||
const split = chunkFile({ path: file.path, content })
|
||||
if (split.chunks.length === 0) skipped += 1
|
||||
chunks += split.chunks.length
|
||||
}
|
||||
return { label, files: files.length, chunks, lines, skipped }
|
||||
}
|
||||
|
||||
/** Directories under src/controllers that hold code directly. */
|
||||
async function controllerRoots(root) {
|
||||
const { files } = await listCodeFiles(root)
|
||||
const seen = new Map()
|
||||
for (const file of files) {
|
||||
if (!file.path.startsWith('src/controllers/')) continue
|
||||
const dir = file.path.slice(0, file.path.lastIndexOf('/'))
|
||||
if (dir === '' || dir.endsWith('__tests__')) continue
|
||||
seen.set(dir, true)
|
||||
}
|
||||
return [...seen.keys()].sort()
|
||||
}
|
||||
|
||||
function seconds(chunks) {
|
||||
return chunks / MEASURED_CHUNKS_PER_SECOND
|
||||
}
|
||||
|
||||
function human(sec) {
|
||||
if (sec < 60) return `${sec.toFixed(0)}s`
|
||||
return `${(sec / 60).toFixed(1)}min`
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log(`workspace: ${WORKSPACE}`)
|
||||
console.log(`throughput: ${MEASURED_CHUNKS_PER_SECOND.toFixed(1)} chunks/s (measured on this repo)`)
|
||||
console.log('')
|
||||
|
||||
const whole = await measure(WORKSPACE, 'whole workspace')
|
||||
console.log('=== WHOLE WORKSPACE ===')
|
||||
console.log(` files ${whole.files} chunks ${whole.chunks} lines ${whole.lines.toLocaleString()}`)
|
||||
console.log(` cold index (projected): ${human(seconds(whole.chunks))}`)
|
||||
console.log('')
|
||||
|
||||
const roots = await controllerRoots(WORKSPACE)
|
||||
const scopes = []
|
||||
for (const dir of roots) {
|
||||
scopes.push(await measure(join(WORKSPACE, dir), dir))
|
||||
}
|
||||
scopes.sort((left, right) => right.chunks - left.chunks)
|
||||
|
||||
console.log(`=== ${scopes.length} CONTROLLER SCOPES ===`)
|
||||
console.log(` ${'scope'.padEnd(52)} ${'files'.padStart(5)} ${'chunks'.padStart(7)} ${'share'.padStart(7)} ${'cold'.padStart(7)}`)
|
||||
for (const scope of scopes.slice(0, 10)) {
|
||||
const share = ((scope.chunks / whole.chunks) * 100).toFixed(1)
|
||||
console.log(
|
||||
` ${scope.label.replace('src/controllers/', '').padEnd(52)} ${String(scope.files).padStart(5)} ` +
|
||||
`${String(scope.chunks).padStart(7)} ${`${share}%`.padStart(7)} ${human(seconds(scope.chunks)).padStart(7)}`,
|
||||
)
|
||||
}
|
||||
if (scopes.length > 10) console.log(` … and ${scopes.length - 10} more`)
|
||||
console.log('')
|
||||
|
||||
const median = scopes[Math.floor(scopes.length / 2)]
|
||||
const totalScoped = scopes.reduce((sum, scope) => sum + scope.chunks, 0)
|
||||
console.log('=== WHAT SCOPING BUYS ===')
|
||||
console.log(` median scope : ${median.chunks} chunks (${((median.chunks / whole.chunks) * 100).toFixed(1)}% of the workspace)`)
|
||||
console.log(` largest scope : ${scopes[0].chunks} chunks (${((scopes[0].chunks / whole.chunks) * 100).toFixed(1)}%)`)
|
||||
console.log(` candidate pool : ${whole.chunks} -> ${median.chunks} chunks, a ${(whole.chunks / median.chunks).toFixed(1)}x smaller haystack`)
|
||||
console.log(` cold start (projected): ${human(seconds(whole.chunks))} -> ${human(seconds(median.chunks))}`)
|
||||
console.log(` all scopes together : ${totalScoped} chunks, ${((totalScoped / whole.chunks) * 100).toFixed(1)}% of the workspace`)
|
||||
console.log('')
|
||||
console.log('(the candidate-pool figure is the precision argument; whether ranking actually')
|
||||
console.log(' improves needs the live endpoint, which is currently unable to load the model)')
|
||||
}
|
||||
|
||||
await main()
|
||||
Reference in new issue
Block a user