Files
supabase/apps/studio/evals/scorer-online.ts
Greg Richardson 5f8906a20e fix: add destructive operation guardrails to AI assistant (#45194)
Prevents the AI assistant from helping with local git/filesystem
operations, and adds explicit warnings before irreversible database
operations (DROP TABLE, DELETE without WHERE, etc.).

Adds a `safetyScorer` and eval cases to cover these behaviours.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added a Safety metric to evaluations so assistant responses are scored
for safe handling of destructive or risky requests
* Assistant guidance updated to refuse destructive local VCS/filesystem
actions and require clear warnings for irreversible database operations

* **Tests**
* Added evaluation cases covering safe refusals, clear warnings, and
correct handling of destructive or risky prompts

* **Chores**
  * Enabled Safety metric in online evaluation manifests/handlers
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-06 09:24:21 -06:00

65 lines
2.4 KiB
TypeScript

/**
* Entry point for `braintrust push` to deploy scorers to Braintrust.
*
* Excluded scorers:
* - sqlSyntaxScorer, sqlIdentifierQuotingScorer: use libpg-query (WASM),
* which esbuild cannot bundle for Braintrust's remote infra.
* - toolUsageScorer: requires expected.requiredTools, offline-eval-only.
* - correctnessScorer: requires ground truth (expected output), offline-eval-only.
*/
import braintrust, { type EvalScorer } from 'braintrust'
import {
completenessScorer,
concisenessScorer,
docsFaithfulnessScorer,
goalCompletionScorer,
safetyScorer,
urlValidityScorer,
type AssistantEvalInput,
type AssistantEvalOutput,
type Expected,
} from './scorer'
import manifest from './scorer-online-manifest.json'
const projectId = process.env.BRAINTRUST_PROJECT_ID
if (!projectId && process.env.IS_BRAINTRUST_PUSH)
throw new Error('BRAINTRUST_PROJECT_ID is not set')
// When running in CI, prefix scorers with the branch name to avoid collisions between PRs
// in the staging project. GITHUB_HEAD_REF is only set on PR events, not push events (e.g. master).
const branch = process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME
const prNumber = process.env.GITHUB_PR_NUMBER ? Number(process.env.GITHUB_PR_NUMBER) : undefined
const prefix = process.env.GITHUB_HEAD_REF
? `${process.env.GITHUB_HEAD_REF.replace(/[^a-z0-9-]/gi, '-').toLowerCase()}-`
: ''
const metadata = branch ? { gitBranch: branch, ...(prNumber && { prNumber }) } : undefined
const description = prNumber && branch ? `#${prNumber} · ${branch}` : branch
const handlers = {
'goal-completion': goalCompletionScorer,
conciseness: concisenessScorer,
completeness: completenessScorer,
'docs-faithfulness': docsFaithfulnessScorer,
'url-validity': urlValidityScorer,
// safetyScorer guards on requiresSafetyCheck (an offline-eval concept).
// Online traces have no expected, so we wrap it to always run.
safety: (args) =>
safetyScorer({ ...args, expected: { ...args.expected, requiresSafetyCheck: true } }),
} satisfies Record<string, EvalScorer<AssistantEvalInput, AssistantEvalOutput, Expected>>
// @ts-expect-error - Project ID is only required at build-time
const project = braintrust.projects.create({ id: projectId })
for (const { slug, name } of manifest) {
project.scorers.create({
slug: `${prefix}${slug}`,
name,
description,
handler: handlers[slug as keyof typeof handlers],
ifExists: 'replace',
metadata,
})
}