> For AI agents: the complete documentation index is available at https://viteval.dev/llms.txt, the full documentation bundle is available at https://viteval.dev/llms-full.txt.

# Write a custom scorer

Use `createScorer()` when a built-in check does not express your application's
requirements. This example compares order IDs after trimming and normalizing case.

## Prerequisites

- Complete [Getting started](https://viteval.dev/getting-started.md) and keep its Vitest configuration.
- Use string outputs and expected values for this example. No API key is needed.

## Steps


1

### Define the check

Create `src/normalized-order-id.ts`:
```ts
import { createScorer } from 'viteval';

export const normalizedOrderId = createScorer<string>({
  name: 'normalized-order-id',
  score: ({ output, expected }) => ({
    score: output.trim().toUpperCase() === expected ? 1 : 0,
    metadata: { normalized: output.trim().toUpperCase() },
  }),
});
```
The callback can be synchronous or asynchronous. Return a finite score and any
metadata that will help explain the result. Viteval attaches the configured name.

2

### Use the scorer in an evaluation

Replace `src/viteval.eval.ts` with:
```ts
import { evaluate } from 'viteval';
import { normalizedOrderId } from './normalized-order-id';

evaluate('Order extraction', {
  data: [
    { input: 'Please cancel ord-2087.', expected: 'ORD-2087' },
    { input: 'How do I update my address?', expected: '' },
  ],
  task: ({ input }) => input.match(/\bORD-\d+\b/i)?.[0] ?? '',
  scorers: [normalizedOrderId],
  threshold: 1,
});
```

## Verification

```bash
pnpm exec viteval run
```

Both cases pass because the scorer normalizes the extracted ID. Remove
`.toUpperCase()` from the scorer and run again. The lowercase case now fails.
Restore normalization after the check.

## Troubleshooting

### The case fails without a numeric score

Save a JSON report to inspect the score and metadata in the dashboard. Return
`score: null` when your scorer cannot produce a measurement. Null contributes
zero to aggregation, while throwing an error fails the case regardless of its
threshold. Inspect the score metadata or execution errors in a saved report.

## References

- [`createScorer()`](https://viteval.dev/api/scoring/create-scorer.md) for callback types
- [`wrapScorer()`](https://viteval.dev/api/scoring/wrap-scorer.md) to transform values before an existing check
- [Inspect results](https://viteval.dev/guide/results.md) for report setup
