Skip to content

Commit e4d5ea9

Browse files
committed
Create pending tasks
1 parent 6b9ed89 commit e4d5ea9

File tree

8 files changed

+176
-54
lines changed

8 files changed

+176
-54
lines changed

benchmark/apps/cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"@benchmark/db": "workspace:^",
1313
"@vscode/test-electron": "^2.4.0",
1414
"gluegun": "^5.1.2",
15-
"p-map": "^7.0.3"
15+
"uuid": "^11.1.0"
1616
},
1717
"devDependencies": {
1818
"@benchmark/eslint-config": "workspace:^",
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import * as path from "path"
2+
import * as fs from "fs"
3+
4+
import { filesystem } from "gluegun"
5+
6+
import { type Language, languages } from "@benchmark/db"
7+
8+
import { exercisesPath } from "./paths.js"
9+
10+
let exercisesByLanguage: Record<Language, string[]> | null = null
11+
12+
export const getExercises = () => {
13+
if (exercisesByLanguage !== null) {
14+
return exercisesByLanguage
15+
}
16+
17+
const getLanguageExercises = (language: Language) =>
18+
fs.existsSync(path.resolve(exercisesPath, language))
19+
? filesystem
20+
.subdirectories(path.resolve(exercisesPath, language))
21+
.map((exercise) => path.basename(exercise))
22+
.filter((exercise) => !exercise.startsWith("."))
23+
: []
24+
25+
exercisesByLanguage = languages.reduce(
26+
(collect, language) => ({ ...collect, [language]: getLanguageExercises(language) }),
27+
{} as Record<Language, string[]>,
28+
)
29+
30+
return exercisesByLanguage
31+
}

benchmark/apps/cli/src/index.ts

Lines changed: 75 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,90 @@
11
import * as fs from "fs"
22
import * as path from "path"
3-
import { fileURLToPath } from "url"
4-
import pMap from "p-map"
3+
import * as os from "os"
54

5+
import { v4 as uuidv4 } from "uuid"
66
import { build, filesystem, GluegunPrompt, GluegunToolbox } from "gluegun"
77
import { runTests } from "@vscode/test-electron"
88

9-
import { type Language, languages, type Run, findRun, createRun, getTask } from "@benchmark/db"
9+
import {
10+
type Language,
11+
languages,
12+
type Run,
13+
findRun,
14+
createRun,
15+
getPendingTask,
16+
createPendingTask,
17+
getTask,
18+
} from "@benchmark/db"
1019

11-
const __dirname = path.dirname(fileURLToPath(import.meta.url))
12-
const extensionDevelopmentPath = path.resolve(__dirname, "..", "..", "..", "..")
13-
const extensionTestsPath = path.resolve(extensionDevelopmentPath, "benchmark/packages/runner/dist")
14-
const exercisesPath = path.resolve(extensionDevelopmentPath, "..", "exercises")
20+
import { __dirname, extensionDevelopmentPath, extensionTestsPath, exercisesPath } from "./paths.js"
21+
import { getExercises } from "./exercises.js"
1522

1623
export const isLanguage = (language: string): language is Language => languages.includes(language as Language)
1724

1825
const run = async (toolbox: GluegunToolbox) => {
1926
const { config, prompt } = toolbox
2027
const id = config.runId ? Number(config.runId) : undefined
21-
let { language, exercise } = config
28+
const { language, exercise } = config
2229

2330
if (language === "all") {
24-
const run = await findOrCreateRun({ id })
25-
await runAll(run)
31+
await runAll(id)
2632
} else if (exercise === "all") {
27-
const run = await findOrCreateRun({ id })
28-
await runLanguage({ run, language })
33+
await runLanguage({ id, language })
2934
} else {
30-
language = language || (await askLanguage(prompt))
31-
exercise = exercise || (await askExercise(prompt, language))
32-
const run = await findOrCreateRun({ id })
33-
await runExercise({ run, language, exercise })
35+
await runLanguageExercise({
36+
id,
37+
language: language || (await askLanguage(prompt)),
38+
exercise: exercise || (await askExercise(prompt, language)),
39+
})
3440
}
3541
}
3642

37-
const runAll = async (run: Run) =>
38-
(await pMap(languages, (language) => runLanguage({ run, language }), { concurrency: 1 })).flatMap(
39-
(language) => language,
40-
)
43+
const runAll = async (id?: number) => {
44+
const run = await findOrCreateRun({ id })
45+
const exercises = getExercises()
4146

42-
const runLanguage = async ({ run, language }: { run: Run; language: Language }) => {
43-
const languagePath = path.resolve(exercisesPath, language)
47+
for (const [language, languageExercises] of Object.entries(exercises)) {
48+
await Promise.all(
49+
languageExercises.map((exercise) =>
50+
findOrCreatePendingTask({ runId: run.id, language: language as Language, exercise }),
51+
),
52+
)
53+
}
4454

45-
if (!fs.existsSync(languagePath)) {
46-
console.error(`Language directory ${languagePath} does not exist`)
47-
process.exit(1)
55+
for (const [language, languageExercises] of Object.entries(exercises)) {
56+
for (const exercise of languageExercises) {
57+
await runExercise({ run, language: language as Language, exercise })
58+
}
4859
}
60+
}
4961

50-
const exercises = filesystem
51-
.subdirectories(languagePath)
52-
.map((exercise) => path.basename(exercise))
53-
.filter((exercise) => !exercise.startsWith("."))
54-
55-
const results = await pMap(
56-
exercises,
57-
async (exercise) => ({
58-
language,
59-
exercise,
60-
result: await runExercise({ run, language, exercise }),
61-
}),
62-
{ concurrency: 1 },
62+
const runLanguage = async ({ id, language }: { id?: number; language: Language }) => {
63+
const run = await findOrCreateRun({ id })
64+
const exercises = getExercises()
65+
const languageExercises = exercises[language]
66+
67+
await Promise.all(
68+
languageExercises.map((exercise) => findOrCreatePendingTask({ runId: run.id, language, exercise })),
6369
)
6470

65-
return results
71+
for (const exercise of languageExercises) {
72+
await runExercise({ run, language, exercise })
73+
}
74+
}
75+
76+
const runLanguageExercise = async ({
77+
id,
78+
language,
79+
exercise,
80+
}: {
81+
id?: number
82+
language: Language
83+
exercise: string
84+
}) => {
85+
const run = await findOrCreateRun({ id })
86+
await findOrCreatePendingTask({ runId: run.id, language, exercise })
87+
return runExercise({ run, language, exercise })
6688
}
6789

6890
const runExercise = async ({ run, language, exercise }: { run: Run; language: Language; exercise: string }) => {
@@ -127,10 +149,20 @@ const askExercise = async (prompt: GluegunPrompt, language: Language) => {
127149
return exercise
128150
}
129151

130-
type FindOrCreateRun = { id?: number; model?: string }
131-
132-
const findOrCreateRun = async ({ id, model = "anthropic/claude-3.7-sonnet" }: FindOrCreateRun) =>
133-
id ? findRun(id) : createRun({ model })
152+
const findOrCreateRun = async ({ id, model = "anthropic/claude-3.7-sonnet" }: { id?: number; model?: string }) =>
153+
id
154+
? findRun(id)
155+
: createRun({ model, pid: process.pid, socketPath: path.resolve(os.tmpdir(), `benchmark-${uuidv4()}.sock`) })
156+
157+
const findOrCreatePendingTask = async ({
158+
runId,
159+
language,
160+
exercise,
161+
}: {
162+
runId: number
163+
language: Language
164+
exercise: string
165+
}) => (await getPendingTask({ runId, language, exercise })) || (await createPendingTask({ runId, language, exercise }))
134166

135167
const main = async () => {
136168
const cli = build()

benchmark/apps/cli/src/paths.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import * as path from "path"
2+
import { fileURLToPath } from "url"
3+
4+
export const __dirname = path.dirname(fileURLToPath(import.meta.url))
5+
6+
export const extensionDevelopmentPath = path.resolve(__dirname, "..", "..", "..", "..")
7+
export const extensionTestsPath = path.resolve(extensionDevelopmentPath, "benchmark/packages/runner/dist")
8+
export const exercisesPath = path.resolve(extensionDevelopmentPath, "..", "exercises")

benchmark/packages/db/src/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,10 @@ export { findRun, createRun, getRuns } from "./queries/runs.js"
1515

1616
export { type Task, type InsertTask, insertTaskSchema } from "./schema.js"
1717
export { findTask, createTask, getTask } from "./queries/tasks.js"
18+
19+
/**
20+
* pendingTasks
21+
*/
22+
23+
export { type PendingTask, type InsertPendingTask, insertPendingTaskSchema } from "./schema.js"
24+
export { findPendingTask, createPendingTask, getPendingTask } from "./queries/pendingTasks.js"
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { and, eq } from "drizzle-orm"
2+
3+
import { db } from "../db.js"
4+
import { type InsertPendingTask, insertPendingTaskSchema, pendingTasks } from "../schema.js"
5+
6+
import { Language } from "../enums.js"
7+
import { RecordNotFoundError, RecordNotCreatedError } from "./errors.js"
8+
9+
export const findPendingTask = async (id: number) => {
10+
const run = await db.query.pendingTasks.findFirst({ where: eq(pendingTasks.id, id) })
11+
12+
if (!run) {
13+
throw new RecordNotFoundError()
14+
}
15+
16+
return run
17+
}
18+
19+
export const createPendingTask = async (args: InsertPendingTask) => {
20+
const result = await db
21+
.insert(pendingTasks)
22+
.values({
23+
...insertPendingTaskSchema.parse(args),
24+
createdAt: new Date(),
25+
})
26+
.returning()
27+
28+
const task = result[0]
29+
30+
if (!task) {
31+
throw new RecordNotCreatedError()
32+
}
33+
34+
return task
35+
}
36+
37+
type GetPendingTask = {
38+
runId: number
39+
language: Language
40+
exercise: string
41+
}
42+
43+
export const getPendingTask = async ({ runId, language, exercise }: GetPendingTask) =>
44+
db.query.pendingTasks.findFirst({
45+
where: and(
46+
eq(pendingTasks.runId, runId),
47+
eq(pendingTasks.language, language),
48+
eq(pendingTasks.exercise, exercise),
49+
),
50+
})

benchmark/packages/db/src/schema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,4 @@ export type InsertPendingTask = z.infer<typeof insertPendingTaskSchema>
9090
* schema
9191
*/
9292

93-
export const schema = { runs, tasks }
93+
export const schema = { runs, tasks, pendingTasks }

benchmark/pnpm-lock.yaml

Lines changed: 3 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)