Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions sdks/typescript/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,7 @@ await evaluator.evaluate(text: string, grade: string)
score: 'slightly complex' | 'moderately complex' | 'very complex' | 'exceedingly complex';
reasoning: string;
metadata: {
promptVersion: string;
model: string;
timestamp: Date;
processingTimeMs: number;
};
_internal: VocabularyComplexity; // Detailed analysis
Expand Down Expand Up @@ -106,9 +104,7 @@ await evaluator.evaluate(text: string, grade: string)
score: 'Slightly Complex' | 'Moderately Complex' | 'Very Complex' | 'Exceedingly Complex';
reasoning: string;
metadata: {
promptVersion: string;
model: string;
timestamp: Date;
processingTimeMs: number;
};
_internal: {
Expand Down Expand Up @@ -195,9 +191,7 @@ await evaluator.evaluate(text: string)
score: string; // e.g., 'K-1', '2-3', '4-5', '6-8', '9-10', '11-CCR'
reasoning: string;
metadata: {
promptVersion: string;
model: string;
timestamp: Date;
processingTimeMs: number;
};
_internal: {
Expand Down
2 changes: 1 addition & 1 deletion sdks/typescript/src/evaluators/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ export abstract class BaseEvaluator {
// Initialize telemetry if enabled
if (this.config.telemetry.enabled) {
this.telemetryClient = new TelemetryClient({
endpoint: 'https://api.learningcommons.org/v1/telemetry',
endpoint: 'https://api.learningcommons.org/evaluators-telemetry/v1/events',
partnerKey: config.partnerKey,
clientId: generateClientId(),
enabled: true,
Expand Down
10 changes: 4 additions & 6 deletions sdks/typescript/src/evaluators/grade-level-appropriateness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import type { LLMProvider } from '../providers/index.js';
import { createProvider } from '../providers/index.js';
import {
GradeLevelAppropriatenessSchema,
type GradeLevelAppropriateness,
type GradeLevelAppropriatenessInternal,
} from '../schemas/grade-level-appropriateness.js';
import { getSystemPrompt, getUserPrompt } from '../prompts/grade-level-appropriateness/index.js';
import type { EvaluationResult } from '../schemas/index.js';
import type { EvaluationResult, GradeBand } from '../schemas/index.js';
import { BaseEvaluator, type BaseEvaluatorConfig } from './base.js';
import { ValidationError, wrapProviderError } from '../errors.js';

Expand Down Expand Up @@ -69,7 +69,7 @@ export class GradeLevelAppropriatenessEvaluator extends BaseEvaluator {
* @throws {ValidationError} If text is empty or too short/long
* @throws {APIError} If LLM API calls fail (includes AuthenticationError, RateLimitError, NetworkError, TimeoutError)
*/
async evaluate(text: string): Promise<EvaluationResult<string, GradeLevelAppropriateness>> {
async evaluate(text: string): Promise<EvaluationResult<GradeBand, GradeLevelAppropriatenessInternal>> {
this.logger.info('Starting grade level appropriateness evaluation', {
evaluator: 'grade-level-appropriateness',
operation: 'evaluate',
Expand Down Expand Up @@ -107,9 +107,7 @@ export class GradeLevelAppropriatenessEvaluator extends BaseEvaluator {
score: response.data.grade,
reasoning: response.data.reasoning,
metadata: {
promptVersion: '1.2.0',
model: 'google:gemini-2.5-pro',
timestamp: new Date(),
processingTimeMs: latencyMs,
},
_internal: response.data,
Expand Down Expand Up @@ -186,7 +184,7 @@ export class GradeLevelAppropriatenessEvaluator extends BaseEvaluator {
export async function evaluateGradeLevelAppropriateness(
text: string,
config: BaseEvaluatorConfig
): Promise<EvaluationResult<string, GradeLevelAppropriateness>> {
): Promise<EvaluationResult<GradeBand, GradeLevelAppropriatenessInternal>> {
const evaluator = new GradeLevelAppropriatenessEvaluator(config);
return evaluator.evaluate(text);
}
3 changes: 1 addition & 2 deletions sdks/typescript/src/evaluators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,5 @@ export {
export {
TextComplexityEvaluator,
evaluateTextComplexity,
type TextComplexityScore,
type TextComplexityInternal,
type TextComplexityResult,
} from './text-complexity.js';
51 changes: 20 additions & 31 deletions sdks/typescript/src/evaluators/sentence-structure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
type SentenceAnalysis,
type SentenceFeatures,
type ComplexityClassification,
type SentenceStructureInternal,
} from '../schemas/sentence-structure.js';
import { calculateReadabilityMetrics, addEngineeredFeatures, featuresToJSON } from '../features/index.js';
import {
Expand All @@ -14,39 +15,29 @@ import {
getSystemPromptComplexity,
getUserPromptComplexity,
} from '../prompts/sentence-structure/index.js';
import type { EvaluationResult, ComplexityLevel } from '../schemas/index.js';
import type { EvaluationResult, TextComplexityLevel } from '../schemas/index.js';
import { BaseEvaluator, type BaseEvaluatorConfig } from './base.js';
import type { StageDetail } from '../telemetry/index.js';
import { ValidationError, wrapProviderError } from '../errors.js';

/**
* Internal data structure for sentence structure evaluation
*/
interface SentenceStructureInternal {
sentenceAnalysis: SentenceAnalysis;
features: SentenceFeatures;
complexity: ComplexityClassification;
}

/**
* Normalize complexity label to handle LLM output variations
* Ported from Python normalize_label function
*/
function normalizeLabel(label: string | null | undefined): string | null {
function normalizeLabel(label: string | null | undefined): TextComplexityLevel | null {
if (!label) {
return null;
}

const normalized = label.trim().toLowerCase();
const mapping: Record<string, string> = {
'slightly complex': 'Slightly Complex',
'moderately complex': 'Moderately Complex',
'very complex': 'Very Complex',
'exceedingly complex': 'Exceedingly Complex',
'extremely complex': 'Exceedingly Complex', // Maps to Exceedingly Complex
const normalized = label.trim().toLowerCase().replace(/_/g, ' ');
const mapping: Record<string, TextComplexityLevel> = {
'slightly complex': 'Slightly complex',
'moderately complex': 'Moderately complex',
'very complex': 'Very complex',
'exceedingly complex': 'Exceedingly complex',
'extremely complex': 'Exceedingly complex',
};

return mapping[normalized] || null; // Return null if no mapping found
return mapping[normalized] ?? null;
}

/**
Expand All @@ -57,11 +48,11 @@ function normalizeLabel(label: string | null | undefined): string | null {
* 1. Analyze grammatical structure (sentence types, clauses, phrases, etc.)
* 2. Classify complexity using features and grade-specific rubric
*
* Based on SCASS Text Complexity rubric with 4 levels:
* - Slightly Complex
* - Moderately Complex
* - Very Complex
* - Exceedingly Complex
* Based on Qualitative Text Complexity rubric with 4 levels:
* - Slightly complex
* - Moderately complex
* - Very complex
* - Exceedingly complex
*
* @example
* ```typescript
Expand All @@ -70,7 +61,7 @@ function normalizeLabel(label: string | null | undefined): string | null {
* });
*
* const result = await evaluator.evaluate(text, "3");
* console.log(result.score); // "Moderately Complex"
* console.log(result.score); // "Moderately complex"
* console.log(result.reasoning);
* ```
*/
Expand Down Expand Up @@ -119,7 +110,7 @@ export class SentenceStructureEvaluator extends BaseEvaluator {
async evaluate(
text: string,
grade: string
): Promise<EvaluationResult<string, SentenceStructureInternal>> {
): Promise<EvaluationResult<TextComplexityLevel, SentenceStructureInternal>> {
this.logger.info('Starting sentence structure evaluation', {
evaluator: 'sentence-structure',
operation: 'evaluate',
Expand Down Expand Up @@ -183,9 +174,7 @@ export class SentenceStructureEvaluator extends BaseEvaluator {
score: complexityResponse.data.answer,
reasoning: complexityResponse.data.reasoning,
metadata: {
promptVersion: '1.2.0',
model: 'openai:gpt-4o',
timestamp: new Date(),
processingTimeMs: latencyMs,
},
_internal: {
Expand Down Expand Up @@ -338,7 +327,7 @@ export class SentenceStructureEvaluator extends BaseEvaluator {
return {
data: {
...response.data,
answer: normalizedAnswer as ComplexityLevel,
answer: normalizedAnswer,
},
usage: response.usage,
latencyMs: response.latencyMs,
Expand All @@ -364,7 +353,7 @@ export async function evaluateSentenceStructure(
text: string,
grade: string,
config: BaseEvaluatorConfig
): Promise<EvaluationResult<string, SentenceStructureInternal>> {
): Promise<EvaluationResult<TextComplexityLevel, SentenceStructureInternal>> {
const evaluator = new SentenceStructureEvaluator(config);
return evaluator.evaluate(text, grade);
}
Loading
Loading