Skip to content
Draft
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
66 changes: 59 additions & 7 deletions sdks/typescript/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ Evaluates vocabulary complexity using the Qual Text Complexity rubric (SAP).
**Constructor:**
```typescript
const evaluator = new VocabularyEvaluator({
googleApiKey: string; // Required - Google API key
openaiApiKey: string; // Required - OpenAI API key
googleApiKey?: string; // Google API key (required by this evaluator)
openaiApiKey?: string; // OpenAI API key (required by this evaluator)
maxRetries?: number; // Optional - Max retry attempts (default: 2)
telemetry?: boolean | TelemetryOptions; // Optional (default: true)
logger?: Logger; // Optional - Custom logger
Expand Down Expand Up @@ -72,14 +72,14 @@ await evaluator.evaluate(text: string, grade: string)

Evaluates sentence structure complexity based on grammatical features.

**Supported Grades:** K-12
**Supported Grades:** 3-12

**Uses:** OpenAI GPT-4o

**Constructor:**
```typescript
const evaluator = new SentenceStructureEvaluator({
openaiApiKey: string; // Required - OpenAI API key
openaiApiKey?: string; // OpenAI API key (required by this evaluator)
maxRetries?: number; // Optional - Max retry attempts (default: 2)
telemetry?: boolean | TelemetryOptions; // Optional (default: true)
logger?: Logger; // Optional - Custom logger
Expand Down Expand Up @@ -108,7 +108,51 @@ await evaluator.evaluate(text: string, grade: string)

---

### 3. Grade Level Appropriateness Evaluator
### 3. Text Complexity Evaluator

Composite evaluator that analyzes both vocabulary and sentence structure complexity in parallel.

**Supported Grades:** 3-12

**Uses:** Google Gemini 2.5 Pro + OpenAI GPT-4o (composite)

**Constructor:**
```typescript
const evaluator = new TextComplexityEvaluator({
googleApiKey?: string; // Google API key (required by this evaluator)
openaiApiKey?: string; // OpenAI API key (required by this evaluator)
maxRetries?: number; // Optional - Max retry attempts (default: 2)
telemetry?: boolean | TelemetryOptions; // Optional (default: true)
logger?: Logger; // Optional - Custom logger
logLevel?: LogLevel; // Optional - Logging verbosity (default: WARN)
});
```

**API:**
```typescript
await evaluator.evaluate(text: string, grade: string)
```

**Returns:**
```typescript
{
score: {
overall: string; // Overall complexity (highest of the two)
vocabulary: string; // Vocabulary complexity score
sentenceStructure: string; // Sentence structure complexity score
};
reasoning: string; // Combined reasoning from both evaluators
metadata: EvaluationMetadata;
_internal: {
vocabulary: EvaluationResult | { error: Error };
sentenceStructure: EvaluationResult | { error: Error };
};
}
```

---

### 4. Grade Level Appropriateness Evaluator

Determines appropriate grade level for text.

Expand All @@ -119,7 +163,7 @@ Determines appropriate grade level for text.
**Constructor:**
```typescript
const evaluator = new GradeLevelAppropriatenessEvaluator({
googleApiKey: string; // Required - Google API key
googleApiKey?: string; // Google API key (required by this evaluator)
maxRetries?: number; // Optional - Max retry attempts (default: 2)
telemetry?: boolean | TelemetryOptions; // Optional (default: true)
logger?: Logger; // Optional - Custom logger
Expand Down Expand Up @@ -229,10 +273,12 @@ See [docs/telemetry.md](./docs/telemetry.md) for telemetry configuration and pri

## Configuration Options

All evaluators support these common options:
All evaluators use the same `BaseEvaluatorConfig` interface:

```typescript
interface BaseEvaluatorConfig {
googleApiKey?: string; // Google API key (required by some evaluators)
openaiApiKey?: string; // OpenAI API key (required by some evaluators)
maxRetries?: number; // Max API retry attempts (default: 2)
telemetry?: boolean | TelemetryOptions; // Telemetry config (default: true)
logger?: Logger; // Custom logger (optional)
Expand All @@ -241,6 +287,12 @@ interface BaseEvaluatorConfig {
}
```

**Note:** Which API keys are required depends on the evaluator. The SDK validates required keys at runtime based on the evaluator's metadata:
- **Vocabulary**: Requires both `googleApiKey` and `openaiApiKey`
- **Sentence Structure**: Requires `openaiApiKey` only
- **Text Complexity**: Requires both `googleApiKey` and `openaiApiKey`
- **Grade Level Appropriateness**: Requires `googleApiKey` only

---

## License
Expand Down
48 changes: 37 additions & 11 deletions sdks/typescript/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions sdks/typescript/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
},
"dependencies": {
"compromise": "^14.13.0",
"p-limit": "^5.0.0",
"syllable": "^5.0.1",
"zod": "^3.22.4"
},
Expand Down
80 changes: 77 additions & 3 deletions sdks/typescript/src/evaluators/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,25 @@ export interface BaseEvaluatorConfig {
logLevel?: LogLevel;
}

/**
* Evaluator metadata interface
* Each evaluator must provide this metadata as static properties
*/
export interface EvaluatorMetadata {
/** Unique identifier for the evaluator (e.g., 'vocabulary', 'sentence-structure') */
readonly id: string;
/** Human-readable name (e.g., 'Vocabulary', 'Sentence Structure') */
readonly name: string;
/** Brief description of what the evaluator does */
readonly description: string;
/** Supported grade levels (e.g., ['3', '4', '5', ...]) */
readonly supportedGrades: readonly string[];
/** Whether this evaluator requires a Google API key */
readonly requiresGoogleKey: boolean;
/** Whether this evaluator requires an OpenAI API key */
readonly requiresOpenAIKey: boolean;
}

/**
* Abstract base class for all evaluators
*
Expand All @@ -88,6 +107,9 @@ export interface BaseEvaluatorConfig {
* - Text validation
* - Grade validation (with overridable default)
* - Metadata creation
*
* Concrete evaluators must implement:
* - static metadata: Provide evaluator metadata (see EvaluatorMetadata interface)
*/
export abstract class BaseEvaluator {
protected telemetryClient?: TelemetryClient;
Expand All @@ -96,9 +118,34 @@ export abstract class BaseEvaluator {
telemetry: Required<TelemetryOptions>;
};

/**
* Static metadata for the evaluator
*
* Concrete evaluators MUST define this property.
*
* @example
* ```typescript
* class MyEvaluator extends BaseEvaluator {
* static readonly metadata = {
* id: 'my-evaluator',
* name: 'My Evaluator',
* description: 'Does something useful',
* supportedGrades: ['3', '4', '5'],
* requiresGoogleKey: true,
* requiresOpenAIKey: false,
* };
* }
* ```
*/
static readonly metadata: EvaluatorMetadata;

constructor(config: BaseEvaluatorConfig) {
// Initialize logger
this.logger = createLogger(config.logger, config.logLevel ?? LogLevel.WARN);

// Validate required API keys based on metadata
this.validateApiKeys(config);

// Normalize telemetry config
const telemetryConfig = this.normalizeTelemetryConfig(config.telemetry);

Expand All @@ -124,6 +171,31 @@ export abstract class BaseEvaluator {
}
}

/**
* Get metadata for this evaluator instance
*/
protected get metadata(): EvaluatorMetadata {
return (this.constructor as typeof BaseEvaluator).metadata;
}

/**
* Validate that required API keys are provided based on metadata
* @throws {ValidationError} If required API keys are missing
*/
private validateApiKeys(config: BaseEvaluatorConfig): void {
if (this.metadata.requiresGoogleKey && !config.googleApiKey) {
throw new ValidationError(
`Google API key is required for ${this.metadata.name} evaluator. Pass googleApiKey in config.`
);
}

if (this.metadata.requiresOpenAIKey && !config.openaiApiKey) {
throw new ValidationError(
`OpenAI API key is required for ${this.metadata.name} evaluator. Pass openaiApiKey in config.`
);
}
}

/**
* Normalize telemetry config to standard format
*/
Expand Down Expand Up @@ -153,10 +225,12 @@ export abstract class BaseEvaluator {
}

/**
* Get the evaluator type identifier (e.g., "vocabulary", "sentence-structure")
* Must be implemented by concrete evaluators
* Get the evaluator type identifier from metadata
* @returns The evaluator type ID (e.g., "vocabulary", "sentence-structure")
*/
protected abstract getEvaluatorType(): string;
protected getEvaluatorType(): string {
return this.metadata.id;
}

/**
* Validate text meets requirements
Expand Down
36 changes: 12 additions & 24 deletions sdks/typescript/src/evaluators/grade-level-appropriateness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,6 @@ import type { EvaluationResult } from '../schemas/index.js';
import { BaseEvaluator, type BaseEvaluatorConfig } from './base.js';
import { ValidationError, wrapProviderError } from '../errors.js';

/**
* Configuration for GradeLevelAppropriatenessEvaluator
*/
export interface GradeLevelAppropriatenessEvaluatorConfig extends BaseEvaluatorConfig {
/** Google API key for grade level evaluation (uses Gemini 2.5 Pro) */
googleApiKey: string;
}

/**
* Grade Level Appropriateness Evaluator
*
Expand Down Expand Up @@ -45,20 +37,21 @@ export interface GradeLevelAppropriatenessEvaluatorConfig extends BaseEvaluatorC
* ```
*/
export class GradeLevelAppropriatenessEvaluator extends BaseEvaluator {
static readonly metadata = {
id: 'grade-level-appropriateness',
name: 'Grade Level Appropriateness',
description: 'Determines appropriate grade level for text with scaffolding recommendations',
supportedGrades: [] as const, // No grade parameter required - evaluates what grade the text is appropriate for
requiresGoogleKey: true,
requiresOpenAIKey: false,
};

private provider: LLMProvider;
private evaluatorConfig: GradeLevelAppropriatenessEvaluatorConfig;

constructor(config: GradeLevelAppropriatenessEvaluatorConfig) {
// Call base constructor for common setup (telemetry, etc.)
constructor(config: BaseEvaluatorConfig) {
// Call base constructor for common setup (telemetry, API key validation, etc.)
super(config);

// Validate required API keys
if (!config.googleApiKey) {
throw new ValidationError('Google API key is required. Pass googleApiKey in config.');
}

this.evaluatorConfig = config;

// Create Google Gemini provider
this.provider = createProvider({
type: 'google',
Expand All @@ -69,11 +62,6 @@ export class GradeLevelAppropriatenessEvaluator extends BaseEvaluator {
});
}

// Implement abstract methods from BaseEvaluator
protected getEvaluatorType(): string {
return 'grade-level-appropriateness';
}

/**
* Evaluate grade level appropriateness for a given text
*
Expand Down Expand Up @@ -205,7 +193,7 @@ export class GradeLevelAppropriatenessEvaluator extends BaseEvaluator {
*/
export async function evaluateGradeLevelAppropriateness(
text: string,
config: GradeLevelAppropriatenessEvaluatorConfig
config: BaseEvaluatorConfig
): Promise<EvaluationResult<GradeLevelAppropriateness>> {
const evaluator = new GradeLevelAppropriatenessEvaluator(config);
return evaluator.evaluate(text);
Expand Down
Loading
Loading