scorer-online.ts64 lines · main
1/**
2 * Entry point for `braintrust push` to deploy scorers to Braintrust.
3 *
4 * Excluded scorers:
5 * - sqlSyntaxScorer, sqlIdentifierQuotingScorer: use libpg-query (WASM),
6 * which esbuild cannot bundle for Braintrust's remote infra.
7 * - toolUsageScorer: requires expected.requiredTools, offline-eval-only.
8 * - correctnessScorer: requires ground truth (expected output), offline-eval-only.
9 */
10
11import braintrust, { type EvalScorer } from 'braintrust'
12
13import {
14 completenessScorer,
15 concisenessScorer,
16 docsFaithfulnessScorer,
17 goalCompletionScorer,
18 safetyScorer,
19 urlValidityScorer,
20 type AssistantEvalInput,
21 type AssistantEvalOutput,
22 type Expected,
23} from './scorer'
24import manifest from './scorer-online-manifest.json'
25
26const projectId = process.env.BRAINTRUST_PROJECT_ID
27if (!projectId && process.env.IS_BRAINTRUST_PUSH)
28 throw new Error('BRAINTRUST_PROJECT_ID is not set')
29
30// When running in CI, prefix scorers with the branch name to avoid collisions between PRs
31// in the staging project. GITHUB_HEAD_REF is only set on PR events, not push events (e.g. master).
32const branch = process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME
33const prNumber = process.env.GITHUB_PR_NUMBER ? Number(process.env.GITHUB_PR_NUMBER) : undefined
34const prefix = process.env.GITHUB_HEAD_REF
35 ? `${process.env.GITHUB_HEAD_REF.replace(/[^a-z0-9-]/gi, '-').toLowerCase()}-`
36 : ''
37const metadata = branch ? { gitBranch: branch, ...(prNumber && { prNumber }) } : undefined
38const description = prNumber && branch ? `#${prNumber} · ${branch}` : branch
39
40const handlers = {
41 'goal-completion': goalCompletionScorer,
42 conciseness: concisenessScorer,
43 completeness: completenessScorer,
44 'docs-faithfulness': docsFaithfulnessScorer,
45 'url-validity': urlValidityScorer,
46 // safetyScorer guards on requiresSafetyCheck (an offline-eval concept).
47 // Online traces have no expected, so we wrap it to always run.
48 safety: (args) =>
49 safetyScorer({ ...args, expected: { ...args.expected, requiresSafetyCheck: true } }),
50} satisfies Record<string, EvalScorer<AssistantEvalInput, AssistantEvalOutput, Expected>>
51
52// @ts-expect-error - Project ID is only required at build-time
53const project = braintrust.projects.create({ id: projectId })
54
55for (const { slug, name } of manifest) {
56 project.scorers.create({
57 slug: `${prefix}${slug}`,
58 name,
59 description,
60 handler: handlers[slug as keyof typeof handlers],
61 ifExists: 'replace',
62 metadata,
63 })
64}