Foundation types and utilities for the Sophia assessment system.
This package provides the core vocabulary for person-centered assessment: Moments, Recognition, and scoring strategies. It serves as the foundation that other Sophia packages build upon.
npm install @ethosengine/sophia-core
Sophia uses intentional naming to honor learner dignity:
The unit of assessment content. A moment can be for mastery (graded), discovery (resonance mapping), or reflection (open-ended capture).
interface Moment {
id: string;
purpose: "mastery" | "discovery" | "reflection" | "invitation";
content: PerseusRenderer; // Widget content
hints?: Hint[];
subscaleContributions?: SubscaleMappings; // For discovery/reflection
metadata?: MomentMetadata;
}
The result of processing user input. Contains purpose-specific results.
interface Recognition {
momentId: string;
purpose: AssessmentPurpose;
userInput: UserInputMap;
mastery?: MasteryResult; // For graded assessment
resonance?: ResonanceResult; // For discovery assessment
reflection?: ReflectionResult; // For reflection assessment
timestamp?: number;
}
Result from graded assessment:
interface MasteryResult {
demonstrated: boolean; // Did learner show understanding?
score: number; // Points earned
total: number; // Total possible points
message?: string; // Optional feedback
}
Result from discovery assessment (no “correct” answer):
interface ResonanceResult {
subscaleContributions: Record<string, number>; // e.g., { openness: 0.7 }
selectedChoiceIds?: string[];
confidence?: number;
}
Result from reflection assessment (open-ended capture):
interface ReflectionResult {
userInput: UserInputMap;
subscaleContributions?: Record<string, number>; // Optional
timestamp: number;
textContent?: string; // Extracted text content
}
For discovery and reflection modes, map choices to subscale contributions:
// Map widget ID -> choice ID -> subscale contributions
const subscaleContributions: SubscaleMappings = {
"radio 1": {
"choice-0": { openness: 1, creativity: 0.5 },
"choice-1": { conscientiousness: 1 },
"choice-2": { agreeableness: 1 },
},
};
import {
createMoment,
createMasteryMoment,
createDiscoveryMoment,
createReflectionMoment,
} from "@ethosengine/sophia-core";
// Generic moment
const moment = createMoment("id", "mastery", content, {
hints: [{ content: "Think about..." }],
metadata: { tags: ["algebra"] },
});
// Mastery moment (graded)
const mastery = createMasteryMoment("m-1", content, hints, metadata);
// Discovery moment (requires subscale mappings)
const discovery = createDiscoveryMoment("d-1", content, subscaleMappings, metadata);
// Reflection moment (optional subscale mappings)
const reflection = createReflectionMoment("r-1", content, {
subscaleContributions: subscaleMappings, // Optional
metadata,
});
import { createRecognition } from "@ethosengine/sophia-core";
const recognition = createRecognition("moment-1", "mastery", userInput, {
mastery: { demonstrated: true, score: 1, total: 1 },
timestamp: Date.now(),
});
import {
isMasteryMoment,
isDiscoveryMoment,
isReflectionMoment,
} from "@ethosengine/sophia-core";
if (isMasteryMoment(moment)) {
// moment.purpose === "mastery"
}
import {
hasMasteryResult,
hasResonanceResult,
hasReflectionResult,
hasDemonstrated,
getPrimarySubscale,
} from "@ethosengine/sophia-core";
if (hasMasteryResult(recognition)) {
console.log(recognition.mastery.demonstrated); // TypeScript knows mastery exists
}
if (hasResonanceResult(recognition)) {
const primary = getPrimarySubscale(recognition.resonance);
console.log(`Primary subscale: ${primary}`);
}
if (hasDemonstrated(recognition)) {
console.log("Learner demonstrated mastery!");
}
Scoring strategies define how to process user input and produce Recognition. Different strategies serve different assessment purposes.
interface ScoringStrategy {
readonly id: string;
readonly name: string;
// Check which widgets are empty (not filled in)
getEmptyWidgetIds(
content: PerseusRenderer,
userInput: UserInputMap,
locale: string,
): ReadonlyArray<string>;
// Process input and produce Recognition
recognize(
moment: Moment,
userInput: UserInputMap,
locale: string,
): Recognition;
}
Strategies register themselves at import time:
import { registerScoringStrategy } from "@ethosengine/sophia-core";
const MyScoringStrategy: ScoringStrategy = {
id: "custom",
name: "Custom Strategy",
getEmptyWidgetIds(content, userInput, locale) {
// Return IDs of widgets without valid input
const widgetIds = Object.keys(content.widgets || {});
return widgetIds.filter(id => !userInput[id]);
},
recognize(moment, userInput, locale) {
return {
momentId: moment.id,
purpose: moment.purpose,
userInput,
timestamp: Date.now(),
};
},
};
registerScoringStrategy(MyScoringStrategy);
import {
getScoringStrategy,
getDefaultScoringStrategy,
setDefaultScoringStrategy,
getRegisteredStrategyIds,
hasStrategy,
} from "@ethosengine/sophia-core";
// Get strategy by ID
const strategy = getScoringStrategy("mastery");
// Get default strategy (noop if not set)
const defaultStrategy = getDefaultScoringStrategy();
// Set default strategy
setDefaultScoringStrategy("mastery");
// List all registered strategies
console.log(getRegisteredStrategyIds()); // ["noop", "mastery", "discovery", "reflection"]
// Check if strategy exists
if (hasStrategy("custom")) { ... }
| ID | Package | Purpose | Description |
|---|---|---|---|
noop |
sophia-core | Pass-through | Captures input without processing |
mastery |
perseus-score | Graded | Determines correct/incorrect with scoring |
discovery |
psyche-survey | Resonance | Maps choices to subscale contributions |
reflection |
psyche-survey | Input capture | Captures open-ended responses |
To use non-default strategies, import the providing package:
// Import to register mastery strategy
import "@ethosengine/perseus-score";
// Import to register discovery and reflection strategies
import "@ethosengine/psyche-survey";
For convenience, sophia-core re-exports Perseus types:
export type { PerseusRenderer, Hint, UserInputMap } from "@ethosengine/perseus-core";
sophia-core (this package)
│
└── perseus-core (widget types)
sophia-core has minimal dependencies and serves as the foundation for:
perseus-score - Mastery scoringpsyche-survey - Discovery and reflection scoringsophia - Main rendering packagesophia-element - Web Component distribution