141 lines
4.9 KiB
TypeScript
141 lines
4.9 KiB
TypeScript
#!/usr/bin/env bun
|
|
|
|
import { rmSync } from "fs"
|
|
import path from "path"
|
|
import { Effect, ManagedRuntime } from "effect"
|
|
import { eq } from "drizzle-orm"
|
|
|
|
const args = new Map(
|
|
Bun.argv.slice(2).flatMap((arg) => {
|
|
const match = /^--([^=]+)=(.*)$/.exec(arg)
|
|
return match ? [[match[1], match[2]] as const] : []
|
|
}),
|
|
)
|
|
|
|
const sessions = Number(args.get("sessions") ?? 10_000)
|
|
const messages = Number(args.get("messages") ?? 20)
|
|
const batch = Number(args.get("batch") ?? 1_000)
|
|
const dbPath = path.resolve(args.get("db") ?? path.join("/tmp", "opencode-session-usage-bench.db"))
|
|
const keep = args.get("keep") === "true"
|
|
|
|
if (!keep) rmSync(dbPath, { force: true })
|
|
process.env.OPENCODE_DB = dbPath
|
|
|
|
const started = performance.now()
|
|
const [{ Database }, { SessionTable, MessageTable }] = await Promise.all([
|
|
import("../src/storage/db"),
|
|
import("../src/session/session.sql"),
|
|
])
|
|
|
|
const now = Date.now()
|
|
const expectedCost = sessions * messages * 0.001
|
|
const db = Database.Client().$client
|
|
|
|
console.log(`database: ${dbPath}`)
|
|
console.log(`seed: ${sessions.toLocaleString()} sessions x ${messages.toLocaleString()} assistant messages`)
|
|
|
|
db.transaction(() => {
|
|
db.query(
|
|
`INSERT INTO project (id, worktree, vcs, name, icon_url, icon_url_override, icon_color, time_created, time_updated, time_initialized, sandboxes, commands)
|
|
VALUES ($id, $worktree, NULL, $name, NULL, NULL, NULL, $time, $time, $time, $sandboxes, NULL)`,
|
|
).run({
|
|
$id: "global",
|
|
$worktree: process.cwd(),
|
|
$name: "bench",
|
|
$time: now,
|
|
$sandboxes: JSON.stringify([]),
|
|
})
|
|
})()
|
|
|
|
const insertSession = db.query(
|
|
`INSERT INTO session (
|
|
id, project_id, workspace_id, parent_id, slug, directory, path, title, version, share_url,
|
|
summary_additions, summary_deletions, summary_files, summary_diffs, cost,
|
|
tokens_input, tokens_output, tokens_reasoning, tokens_cache_read, tokens_cache_write,
|
|
revert, permission, agent, model, time_created, time_updated, time_compacting, time_archived
|
|
) VALUES (
|
|
$id, 'global', NULL, NULL, $slug, $directory, $path, $title, 'bench', NULL,
|
|
NULL, NULL, NULL, NULL, 0,
|
|
0, 0, 0, 0, 0,
|
|
NULL, NULL, NULL, NULL, $time, $time, NULL, NULL
|
|
)`,
|
|
)
|
|
const insertMessage = db.query(
|
|
`INSERT INTO message (id, session_id, time_created, time_updated, data)
|
|
VALUES ($id, $session, $time, $time, $data)`,
|
|
)
|
|
|
|
for (let offset = 0; offset < sessions; offset += batch) {
|
|
const end = Math.min(offset + batch, sessions)
|
|
db.transaction(() => {
|
|
for (let i = offset; i < end; i++) {
|
|
const sessionID = `ses_bench_${i.toString().padStart(8, "0")}`
|
|
insertSession.run({
|
|
$id: sessionID,
|
|
$slug: `bench-${i}`,
|
|
$directory: process.cwd(),
|
|
$path: ".",
|
|
$title: `Bench ${i}`,
|
|
$time: now + i,
|
|
})
|
|
for (let j = 0; j < messages; j++) {
|
|
insertMessage.run({
|
|
$id: `msg_bench_${i.toString().padStart(8, "0")}_${j.toString().padStart(4, "0")}`,
|
|
$session: sessionID,
|
|
$time: now + i + j,
|
|
$data: JSON.stringify({
|
|
role: "assistant",
|
|
time: { created: now + i + j, completed: now + i + j + 1 },
|
|
parentID: `msg_parent_${i.toString().padStart(8, "0")}_${j.toString().padStart(4, "0")}`,
|
|
modelID: "bench-model",
|
|
providerID: "bench-provider",
|
|
mode: "build",
|
|
agent: "bench",
|
|
path: { cwd: process.cwd(), root: process.cwd() },
|
|
cost: 0.001,
|
|
tokens: { input: 100, output: 50, reasoning: 10, cache: { read: 5, write: 2 } },
|
|
}),
|
|
})
|
|
}
|
|
}
|
|
})()
|
|
console.log(`seeded ${end.toLocaleString()} sessions`)
|
|
}
|
|
|
|
console.log(`seed time: ${Math.round(performance.now() - started).toLocaleString()}ms`)
|
|
|
|
const [{ DataMigration }, { DataMigrationTable }] = await Promise.all([
|
|
import("../src/data-migration"),
|
|
import("../src/data-migration.sql"),
|
|
])
|
|
const runtime = ManagedRuntime.make(DataMigration.defaultLayer)
|
|
const migrationStarted = performance.now()
|
|
await runtime.runPromise(Effect.gen(function* () {
|
|
return yield* DataMigration.Service
|
|
}))
|
|
|
|
for (;;) {
|
|
const completed = Database.use((db) =>
|
|
db
|
|
.select({ name: DataMigrationTable.name })
|
|
.from(DataMigrationTable)
|
|
.where(eq(DataMigrationTable.name, "session_usage_from_messages"))
|
|
.get(),
|
|
)
|
|
if (completed) break
|
|
await Bun.sleep(100)
|
|
}
|
|
|
|
const migrationMs = performance.now() - migrationStarted
|
|
const totals = db
|
|
.query(`SELECT COUNT(*) AS sessions, SUM(cost) AS cost, SUM(tokens_input) AS input FROM session`)
|
|
.get() as { sessions: number; cost: number; input: number }
|
|
|
|
await runtime.dispose()
|
|
Database.close()
|
|
|
|
console.log(`migration time: ${Math.round(migrationMs).toLocaleString()}ms`)
|
|
console.log(`total time: ${Math.round(performance.now() - started).toLocaleString()}ms`)
|
|
console.log(`result: ${totals.sessions.toLocaleString()} sessions, cost=${totals.cost}, input=${totals.input}`)
|
|
console.log(`expected: cost=${expectedCost}, input=${sessions * messages * 100}`)
|