Documentation
¶
Overview ¶
Package engram is a storage-agnostic spaced-repetition engine that wraps go-fsrs (the Free Spaced Repetition Scheduler). It knows nothing about any particular storage backend, UI, or transport — callers own persistence via the SkillStore and ReviewStore interfaces and drive the engine explicitly.
The package is deterministic by design: time is always passed in as an explicit now time.Time parameter rather than read from the clock, and any randomness needed by callers (e.g. in the quiz subpackage) is supplied via an injected *rand.Rand. This makes scheduling, queueing, and quiz generation fully reproducible in tests.
Index ¶
- func Accuracy(log []Review) float64
- func CountNewIntroduced(log []Review, now time.Time, loc *time.Location) int
- func DueForecast(cards []CardState, days int, now time.Time, loc *time.Location) []int
- func Streak(log []Review, now time.Time, loc *time.Location) int
- type CardState
- type ContentID
- type Deck
- type DeckID
- type QueueConfig
- type QueueItem
- type Rating
- type Review
- type ReviewStore
- type Scheduler
- type SchedulerOption
- type Skill
- type SkillID
- type SkillStore
- type State
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Accuracy ¶
Accuracy is the share of reviews in log with Rating != Again (Hard, Good, and Easy all count as correct). Returns 0 for an empty log.
func CountNewIntroduced ¶
CountNewIntroduced counts entries in log whose StateBefore is StateNew and whose ReviewedAt falls on the same calendar day as now, both converted to loc (nil defaults to time.UTC).
func DueForecast ¶
DueForecast buckets cards by the calendar day (in loc, nil defaulting to time.UTC) their Due falls on, relative to now's day. Bucket 0 is "due today or earlier" — overdue cards clamp into today's bucket — bucket i (i>0) is now's day + i. Cards due beyond now's day + days - 1 are not counted. Returns a slice of length days, or an empty non-nil slice if days <= 0.
func Streak ¶
Streak returns the number of consecutive calendar days (in loc, nil defaulting to time.UTC) with at least one review, counting back from now's day. If now's day itself has no review yet, the count still starts from yesterday instead of resetting to 0 — a day that "just hasn't been studied yet" doesn't erase an otherwise-current streak. Returns 0 if log is empty or neither today nor yesterday (relative to now) has a review.
Types ¶
type CardState ¶
type CardState struct {
Due time.Time
Stability float64
Difficulty float64
Reps int
Lapses int
State State
LastReview time.Time // zero if never reviewed
}
CardState is the per-user FSRS memory state for one skill.
type QueueConfig ¶
type QueueConfig struct {
MaxNewPerDay int // 0 = unlimited
}
QueueConfig configures NextDue's new-card cap.
type QueueItem ¶
QueueItem pairs a Skill with its current FSRS card state, as loaded from a SkillStore for the skills in a deck.
func NextDue ¶
func NextDue(items []QueueItem, newIntroducedToday int, cfg QueueConfig, now time.Time) (item QueueItem, ok bool)
NextDue picks the next item to study out of items. A due-or-overdue review-state card (State != StateNew, Due <= now) always wins, choosing the earliest Due (ties keep whichever was encountered first, for determinism). Only when no review-state card is due is a new (StateNew) card offered instead — again the earliest-Due one whose Due <= now — and only while newIntroducedToday is below cfg.MaxNewPerDay (0 means unlimited). ok=false with a zero QueueItem means nothing is studyable right now.
type Rating ¶
type Rating int
Rating is the grade a reviewer gives when answering a card.
func RatingForAnswer ¶
RatingForAnswer is the v1 answer→rating policy: wrong → Again, correct → Good.
type Review ¶
type Review struct {
SkillID SkillID
Rating Rating
ReviewedAt time.Time
StabilityBefore float64
DifficultyBefore float64
StabilityAfter float64
DifficultyAfter float64
StateBefore State
ScheduledDays int
ElapsedDays int
}
Review is one append-only review-log entry. SkillID is set by the caller.
type ReviewStore ¶
type ReviewStore interface {
// Append records one review-log entry.
Append(ctx context.Context, r Review) error
// Log returns the review entries at or after since, in the order the store
// chooses (callers that need ordering should sort).
Log(ctx context.Context, since time.Time) ([]Review, error)
}
ReviewStore is the append-only review log for the single user this store is scoped to.
type Scheduler ¶
type Scheduler struct {
// contains filtered or unexported fields
}
Scheduler wraps go-fsrs/v3 and applies engram's fixed defaults (RequestRetention 0.9, MaximumInterval 365 days, fuzz disabled for determinism).
func NewScheduler ¶
func NewScheduler(opts ...SchedulerOption) *Scheduler
NewScheduler builds a Scheduler starting from go-fsrs's defaults, then overrides MaximumInterval to engram's contract default of 365 days (go-fsrs defaults to 36500), and finally applies opts. EnableFuzz is always left false: fuzz injects its own randomness outside the caller's control, which would break engram's determinism guarantee.
func (*Scheduler) NewCardState ¶
NewCardState returns the state for a never-reviewed skill: StateNew, zero reps/lapses/stability/difficulty, zero LastReview, and Due set to now (due immediately).
type SchedulerOption ¶
type SchedulerOption func(*Scheduler)
SchedulerOption configures a Scheduler built by NewScheduler.
func WithMaxInterval ¶
func WithMaxInterval(days int) SchedulerOption
WithMaxInterval sets the maximum interval in days between reviews (default 365).
func WithRetention ¶
func WithRetention(r float64) SchedulerOption
WithRetention sets the target request retention (default 0.9).
func WithWeights ¶
func WithWeights(w []float64) SchedulerOption
WithWeights overrides the FSRS model weights (default: go-fsrs defaults). If w has fewer than 19 elements, only the leading len(w) default weights are overridden and the rest keep their default values; if w has more than 19, the extras are ignored. This never panics.
type Skill ¶
type Skill struct {
ID SkillID
DeckID DeckID
Key string // app-defined answer key, e.g. ISO-639-3 "por"
Label string // human label, e.g. "Portuguese"
}
Skill is a single trackable unit of spaced repetition (e.g. "recognise Portuguese in the Romance group").
type SkillStore ¶
type SkillStore interface {
// Skills returns every skill in the given deck.
Skills(ctx context.Context, deck DeckID) ([]Skill, error)
// Card returns the stored card state for a skill. The bool is false when
// the skill has never been seen (no row), in which case the caller should
// treat it as a fresh card via Scheduler.NewCardState.
Card(ctx context.Context, skill SkillID) (CardState, bool, error)
// PutCard upserts the card state for a skill.
PutCard(ctx context.Context, skill SkillID, cs CardState) error
}
SkillStore persists the set of skills in a deck and the per-skill card state for the single user this store is scoped to.
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
cli
command
Command cli is a small, deterministic, self-playing demo of the engram spaced-repetition engine: it builds a "Romance languages" deck, runs a simulated study session through the scheduler + queue + quiz + memstore packages, and prints a stats summary at the end.
|
Command cli is a small, deterministic, self-playing demo of the engram spaced-repetition engine: it builds a "Romance languages" deck, runs a simulated study session through the scheduler + queue + quiz + memstore packages, and prints a stats summary at the end. |
|
Package memstore is an in-memory implementation of engram's SkillStore and ReviewStore interfaces, intended for tests and small apps.
|
Package memstore is an in-memory implementation of engram's SkillStore and ReviewStore interfaces, intended for tests and small apps. |
|
Package quiz builds multiple-choice exercises over engram skills and grades the answers.
|
Package quiz builds multiple-choice exercises over engram skills and grades the answers. |