> For AI agents: the complete documentation index is available at https://open-spaced-repetition.github.io/ts-fsrs/llms.txt, the full documentation bundle is available at https://open-spaced-repetition.github.io/ts-fsrs/llms-full.txt.

# Train and evaluate

Training runs review history through the binding in a few calls. The input is a review-log CSV; the output is a weight array you can hand to `DefaultScheduler` or a custom `defineScheduler`.

The examples import `@open-spaced-repetition/binding`. In Node that is the native napi module; in a browser it resolves to the WASI Preview 1 wasm build (see the [Optimizer overview](https://open-spaced-repetition.github.io/ts-fsrs/guide/optimizer/index.md) for threading).

## Convert review logs to items

`convertCsvToFsrsItems` reads CSV text and groups rows by card into `FSRSItem`s. The columns are:

| Column            | Meaning                                    |
| ----------------- | ------------------------------------------ |
| `card_id`         | Stable identifier for the card.            |
| `review_time`     | Review time as epoch milliseconds.         |
| `review_rating`   | Rating, 1 Again through 4 Easy.            |
| `review_duration` | Time spent on the review, in milliseconds. |
| `review_state`    | Card state at review time.                 |

Pass `nextDayStartsAt` (the hour of day the next day begins) and either an IANA timezone such as `'UTC'` or a fixed UTC offset in minutes. For a `Uint8Array` input the call returns items synchronously:

```ts file="../../../snippets/run-code/optimizer-csv.ts"
import { convertCsvToFsrsItems } from '@open-spaced-repetition/binding'

// One card with two reviews: first review today, second the next day.
const csv = [
  'review_time,card_id,review_rating,review_duration,review_state',
  '1704067200000,card-one,3,1000,0',
  '1704153600000,card-one,4,900,2',
].join('\n')

// The day starts at 04:00 local, in the UTC timezone.
const items = convertCsvToFsrsItems(
  new TextEncoder().encode(csv),
  4,
  'UTC',
  'FSRS-7'
)

console.log(
  JSON.stringify(
    {
      itemCount: items.length,
      reviewsPerItem: items.map((item) => item.reviews.length),
      ratings: items.map((item) => item.reviews.map((review) => review.rating)),
    },
    null,
    2
  )
)

```


▶ Run code

The existing converter keeps returning an array. To normalize arbitrary CSV card identifiers once, use `convertCsvToFsrsItemsWithCardIds`. It returns `{ items, cardIds }`, or a Promise for a stream. The numeric IDs are assigned per retained card within this conversion, stay aligned with items after sorting, and are not the original identifiers. Reuse the same IDs for training and direct evaluation; do not combine IDs from separate conversions.

`computeParameters` and `evaluateWithTimeSeriesSplits` accept optional `options.cardIds`.

```ts file="../../../snippets/run-code/optimizer-card-ids.ts"
import {
  computeParameters,
  convertCsvToFsrsItemsWithCardIds,
  evaluateWithTimeSeriesSplits,
} from '@open-spaced-repetition/binding'

const response = await fetch(`${import.meta.env.BASE_URL}/revlog.csv`, {
  cache: 'force-cache',
})
if (!response.ok) throw new Error(`revlog.csv: ${response.status}`)
if (!response.body) throw new Error('revlog.csv: empty response body')
console.log('Response received. Converting reviews and training…')

const dataset = await convertCsvToFsrsItemsWithCardIds(
  response.body,
  4,
  'UTC',
  'FSRS-7'
)
const optimizedWeights = await computeParameters(dataset.items, {
  modelVersion: 'FSRS-7',
  enableShortTerm: true,
  cardIds: dataset.cardIds,
})
const metrics = await evaluateWithTimeSeriesSplits(dataset.items, {
  modelVersion: 'FSRS-7',
  enableShortTerm: true,
  cardIds: dataset.cardIds,
})

console.log(JSON.stringify({ optimizedWeights, metrics }, null, 2))

```


▶ Run code

Use the same `modelVersion` (`'FSRS-7'` by default, or `'FSRS-6'`) for CSV conversion, training, evaluation, and scheduling. Pass it as the fourth argument to `convertCsvToFsrsItems` and in the training/evaluation options, then call `DefaultScheduler({ version: modelVersion, weights })`. FSRS-7 produces 34 weights and preserves fractional and same-day intervals normalized by the actual local study-day duration; FSRS-6 produces 21 weights and uses whole study days. Same-day follow-up reviews can therefore contribute to FSRS-7 training.

## Train weights

`computeParameters(items, options)` is asynchronous and returns the trained weights. `enableShortTerm` is required; `numRelearningSteps` and `trainingConfig` are optional, and `progress` reports `(current, total)` while training runs:

```ts
const weights = await computeParameters(items, {
  modelVersion: 'FSRS-7',
  enableShortTerm: true,
  enableSchedPenalties: true,
})
```

`enableSchedPenalties` is optional and defaults to `false`. Set it to `true` to add FSRS-7 interval-growth and short-interval penalties to the training loss. It also applies to training inside `evaluateWithTimeSeriesSplits`. It is ignored by FSRS-6, and does not change scheduling formulas.

`trainingConfig` (epochs, batch size, seed, max sequence length, learning rate, and L2 weight) is optional and falls back to the binding's defaults. `computeParameters` runs the real training loop, so call it in a Worker rather than on the main thread. The trainer on the [Playground](https://open-spaced-repetition.github.io/ts-fsrs/playground.md) page runs this same path in a Web Worker and reports progress live. The returned `weights` array is what you pass to `DefaultScheduler({ version: modelVersion, weights })` or a `defineScheduler` config, replacing the model defaults.

## Evaluate fit

`evaluateWithTimeSeriesSplits(items, options)` splits the review history by time and returns a `ModelEvaluation` with `logLoss` and `rmseBins`, letting you judge whether the trained weights predict held-out reviews. `computeOptimalSteps` goes further: given the reviews and a target retention, it recommends learning and relearning step durations.

See the [Optimizer overview](https://open-spaced-repetition.github.io/ts-fsrs/guide/optimizer/index.md) for the two runtimes and install commands.
