@learning-commons/evaluators 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +94 -14
- package/dist/index.cjs +253 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +100 -5
- package/dist/index.d.ts +100 -5
- package/dist/index.js +252 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -117,19 +117,18 @@ await evaluator.evaluate(text: string, grade: string)
|
|
|
117
117
|
|
|
118
118
|
---
|
|
119
119
|
|
|
120
|
-
### 3.
|
|
120
|
+
### 3. Subject Matter Knowledge (SMK) Evaluator
|
|
121
121
|
|
|
122
|
-
|
|
122
|
+
Evaluates the background knowledge demands of educational texts relative to grade level. Determines how much prior subject knowledge a student needs to comprehend the text, based on the Common Core Qualitative Text Complexity Rubric.
|
|
123
123
|
|
|
124
124
|
**Supported Grades:** 3-12
|
|
125
125
|
|
|
126
|
-
**Uses:** Google Gemini
|
|
126
|
+
**Uses:** Google Gemini 3 Flash Preview
|
|
127
127
|
|
|
128
128
|
**Constructor:**
|
|
129
129
|
```typescript
|
|
130
|
-
const evaluator = new
|
|
130
|
+
const evaluator = new SmkEvaluator({
|
|
131
131
|
googleApiKey?: string; // Google API key (required by this evaluator)
|
|
132
|
-
openaiApiKey?: string; // OpenAI API key (required by this evaluator)
|
|
133
132
|
maxRetries?: number; // Optional - Max retry attempts (default: 2)
|
|
134
133
|
telemetry?: boolean | TelemetryOptions; // Optional (default: true)
|
|
135
134
|
logger?: Logger; // Optional - Custom logger
|
|
@@ -145,23 +144,103 @@ await evaluator.evaluate(text: string, grade: string)
|
|
|
145
144
|
**Returns:**
|
|
146
145
|
```typescript
|
|
147
146
|
{
|
|
148
|
-
score:
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
147
|
+
score: 'Slightly complex' | 'Moderately complex' | 'Very complex' | 'Exceedingly complex';
|
|
148
|
+
reasoning: string;
|
|
149
|
+
metadata: {
|
|
150
|
+
model: string;
|
|
151
|
+
processingTimeMs: number;
|
|
152
152
|
};
|
|
153
|
-
reasoning: string; // Combined reasoning from both evaluators
|
|
154
|
-
metadata: EvaluationMetadata;
|
|
155
153
|
_internal: {
|
|
156
|
-
|
|
157
|
-
|
|
154
|
+
identified_topics: string[];
|
|
155
|
+
curriculum_check: string;
|
|
156
|
+
assumptions_and_scaffolding: string;
|
|
157
|
+
friction_analysis: string;
|
|
158
|
+
complexity_score: 'Slightly complex' | 'Moderately complex' | 'Very complex' | 'Exceedingly complex';
|
|
159
|
+
reasoning: string;
|
|
158
160
|
};
|
|
159
161
|
}
|
|
160
162
|
```
|
|
161
163
|
|
|
164
|
+
**Example:**
|
|
165
|
+
```typescript
|
|
166
|
+
import { SmkEvaluator } from '@learning-commons/evaluators';
|
|
167
|
+
|
|
168
|
+
const evaluator = new SmkEvaluator({
|
|
169
|
+
googleApiKey: process.env.GOOGLE_API_KEY,
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const result = await evaluator.evaluate(
|
|
173
|
+
"Hydraulic propulsion works by sucking water at the bow and forcing it sternward.",
|
|
174
|
+
"10"
|
|
175
|
+
);
|
|
176
|
+
console.log(result.score); // "Very complex"
|
|
177
|
+
console.log(result.reasoning);
|
|
178
|
+
console.log(result._internal.identified_topics); // ["hydraulics", "propulsion", "physics"]
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
### 4. Text Complexity Evaluator
|
|
184
|
+
|
|
185
|
+
Composite evaluator that analyzes vocabulary, sentence structure, and subject matter knowledge complexity in parallel.
|
|
186
|
+
|
|
187
|
+
**Supported Grades:** 3-12
|
|
188
|
+
|
|
189
|
+
**Uses:** Google Gemini 2.5 Pro + Google Gemini 3 Flash Preview + OpenAI GPT-4o (composite)
|
|
190
|
+
|
|
191
|
+
**Constructor:**
|
|
192
|
+
```typescript
|
|
193
|
+
const evaluator = new TextComplexityEvaluator({
|
|
194
|
+
googleApiKey?: string; // Google API key (required by this evaluator)
|
|
195
|
+
openaiApiKey?: string; // OpenAI API key (required by this evaluator)
|
|
196
|
+
maxRetries?: number; // Optional - Max retry attempts (default: 2)
|
|
197
|
+
telemetry?: boolean | TelemetryOptions; // Optional (default: true)
|
|
198
|
+
logger?: Logger; // Optional - Custom logger
|
|
199
|
+
logLevel?: LogLevel; // Optional - Logging verbosity (default: WARN)
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**API:**
|
|
204
|
+
```typescript
|
|
205
|
+
await evaluator.evaluate(text: string, grade: string)
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
**Returns:**
|
|
209
|
+
```typescript
|
|
210
|
+
{
|
|
211
|
+
vocabulary: EvaluationResult<TextComplexityLevel> | { error: Error };
|
|
212
|
+
sentenceStructure: EvaluationResult<TextComplexityLevel> | { error: Error };
|
|
213
|
+
subjectMatterKnowledge: EvaluationResult<TextComplexityLevel> | { error: Error };
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Each sub-evaluator result is either a full `EvaluationResult` or `{ error: Error }` if that evaluator failed. An error is only thrown if all three fail.
|
|
218
|
+
|
|
219
|
+
**Example:**
|
|
220
|
+
```typescript
|
|
221
|
+
import { TextComplexityEvaluator } from '@learning-commons/evaluators';
|
|
222
|
+
|
|
223
|
+
const evaluator = new TextComplexityEvaluator({
|
|
224
|
+
googleApiKey: process.env.GOOGLE_API_KEY,
|
|
225
|
+
openaiApiKey: process.env.OPENAI_API_KEY,
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
const result = await evaluator.evaluate("Your text here", "6");
|
|
229
|
+
|
|
230
|
+
if (!('error' in result.vocabulary)) {
|
|
231
|
+
console.log('Vocabulary:', result.vocabulary.score);
|
|
232
|
+
}
|
|
233
|
+
if (!('error' in result.sentenceStructure)) {
|
|
234
|
+
console.log('Sentence structure:', result.sentenceStructure.score);
|
|
235
|
+
}
|
|
236
|
+
if (!('error' in result.subjectMatterKnowledge)) {
|
|
237
|
+
console.log('Subject matter knowledge:', result.subjectMatterKnowledge.score);
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
162
241
|
---
|
|
163
242
|
|
|
164
|
-
###
|
|
243
|
+
### 5. Grade Level Appropriateness Evaluator
|
|
165
244
|
|
|
166
245
|
Determines appropriate grade level for text.
|
|
167
246
|
|
|
@@ -308,6 +387,7 @@ interface BaseEvaluatorConfig {
|
|
|
308
387
|
**Note:** Which API keys are required depends on the evaluator. The SDK validates required keys at runtime based on the evaluator's metadata:
|
|
309
388
|
- **Vocabulary**: Requires both `googleApiKey` and `openaiApiKey`
|
|
310
389
|
- **Sentence Structure**: Requires `openaiApiKey` only
|
|
390
|
+
- **Subject Matter Knowledge**: Requires `googleApiKey` only
|
|
311
391
|
- **Text Complexity**: Requires both `googleApiKey` and `openaiApiKey`
|
|
312
392
|
- **Grade Level Appropriateness**: Requires `googleApiKey` only
|
|
313
393
|
|
package/dist/index.cjs
CHANGED
|
@@ -1769,29 +1769,269 @@ async function evaluateGradeLevelAppropriateness(text, config) {
|
|
|
1769
1769
|
const evaluator = new GradeLevelAppropriatenessEvaluator(config);
|
|
1770
1770
|
return evaluator.evaluate(text);
|
|
1771
1771
|
}
|
|
1772
|
+
var SmkOutputSchema = zod.z.object({
|
|
1773
|
+
identified_topics: zod.z.array(zod.z.string()).describe("List of major subjects/concepts found in the text."),
|
|
1774
|
+
curriculum_check: zod.z.string().describe("Whether the topics are standard K-8 or specialized high school level."),
|
|
1775
|
+
assumptions_and_scaffolding: zod.z.string().describe("What the author assumes the reader knows vs. what is explained."),
|
|
1776
|
+
friction_analysis: zod.z.string().describe("Whether difficulty comes from vocabulary/structure or actual knowledge demands."),
|
|
1777
|
+
complexity_score: TextComplexityLevel.describe("The subject matter knowledge complexity level of the text"),
|
|
1778
|
+
reasoning: zod.z.string().describe("A brief synthesis of why the text fits the chosen complexity level.")
|
|
1779
|
+
});
|
|
1780
|
+
|
|
1781
|
+
// ../../evals/prompts/subject-matter-knowledge/system.txt
|
|
1782
|
+
var system_default2 = `
|
|
1783
|
+
To perform the task of evaluating text complexity based on Subject Matter Knowledge (SMK), strictly adhere to the following instructions.
|
|
1784
|
+
Role
|
|
1785
|
+
You are an expert K-12 Literacy Pedagogue and Text Complexity Evaluator. Your specific focus is analyzing Subject Matter Knowledge (SMK) demands according to the Common Core Qualitative Text Complexity Rubric.
|
|
1786
|
+
Objective
|
|
1787
|
+
Analyze a provided text relative to a target grade_level. You must determine the extent of background knowledge required to comprehend the text. You must distinguish between Common/Standard knowledge (generally lower/moderate complexity) and Specialized/Theoretical knowledge (generally higher complexity).
|
|
1788
|
+
Input Data
|
|
1789
|
+
text: The passage to analyze.
|
|
1790
|
+
grade_level: The target student grade (integer).
|
|
1791
|
+
fk_score: Flesch-Kincaid Grade Level. Note: Use this only as a loose proxy for sentence structure. Do not let a high FK score artificially inflate the Subject Matter Knowledge score if the concepts remain simple.
|
|
1792
|
+
|
|
1793
|
+
1. The Rubric: Subject Matter Knowledge (SMK)
|
|
1794
|
+
1. Slightly Complex
|
|
1795
|
+
Scope: Everyday, practical knowledge, and Introduction to Skills.
|
|
1796
|
+
Concept Type: Concrete, directly observable, and familiar.
|
|
1797
|
+
Key Indicator: "How-to" texts involving familiar objects (e.g., drawing a cupboard, playing a game, family life). Even if specific terms (like "scale" or "measure") are used, if the application is on a common object, it remains Slightly Complex.
|
|
1798
|
+
2. Moderately Complex
|
|
1799
|
+
Scope: Common Discipline-Specific Knowledge or Narrative History.
|
|
1800
|
+
Definition: Topics widely introduced in K-8 curricula (Basic American History, Geography, Earth Science, Biology).
|
|
1801
|
+
Key Characteristic: The text bridges concrete descriptions with abstract themes (e.g., using farming to discuss justice), OR narrates historical events via sensory details.
|
|
1802
|
+
Spatial Reasoning: Texts requiring mental manipulation of maps/routes are generally Moderate, unless the object is a familiar household item (see Slightly Complex).
|
|
1803
|
+
3. Very Complex
|
|
1804
|
+
Scope: Specialized Discipline-Specific, Engineering Mechanics, or Political Theory.
|
|
1805
|
+
Definition: Topics characteristic of High School (9-12) curricula requiring abstract mental models.
|
|
1806
|
+
Key Characteristic: Requires understanding mechanisms (how physics works/propulsion), chemical composition, or undefined political stakes (specific treaties, alliances, or secularization without context).
|
|
1807
|
+
4. Exceedingly Complex
|
|
1808
|
+
Scope: Professional or Academic knowledge.
|
|
1809
|
+
|
|
1810
|
+
2. The Expert Mental Model (Decision Logic)
|
|
1811
|
+
Use these refined rules to categorize cases.
|
|
1812
|
+
Rule A: The "Layers of Meaning" Check
|
|
1813
|
+
Concrete -> Abstract (Moderate): The text describes concrete things (farming) to argue an abstract point (justice, rights).
|
|
1814
|
+
Concrete -> Concrete (Slightly): The text describes concrete things (lines, paper) to achieve a concrete result (drawing a cupboard). Do not over-rank practical instructions.
|
|
1815
|
+
Rule B: The Science & Engineering Boundary
|
|
1816
|
+
Observational (Moderate): Habitats, Water Cycle, observable traits, simple definitions.
|
|
1817
|
+
Mechanistic/Theoretical (Very): Engineering mechanics (how propulsion works via reaction), Instrumentation (using a spectroscope), or Chemical/Atomic theory.
|
|
1818
|
+
Test: Does the text explain how a machine functions using physical principles? If yes, it is Very Complex.
|
|
1819
|
+
Rule C: The History/Social Studies Boundary
|
|
1820
|
+
General/Narrative (Moderate):
|
|
1821
|
+
Sensory: Battle descriptions focusing on sights/sounds (flashes, smoke).
|
|
1822
|
+
Standard Topics: Immigration, Slavery, Government, Geography. Lists of nationalities or religions are "Common Knowledge" for Grades 6-8.
|
|
1823
|
+
Political/Contextual (Very):
|
|
1824
|
+
Implicit Context: Texts assuming knowledge of specific political factions, treaties, or the causes of events without explanation (e.g., "The Allies," "The Front," "The secularization of the clergy").
|
|
1825
|
+
Test: If the reader must know why two groups are fighting or the specific political history of a revolution to understand the text, it is Very Complex.
|
|
1826
|
+
Rule D: The "Technical vs. Practical" Trap
|
|
1827
|
+
Scenario: A text teaches a technical skill (e.g., Technical Drawing/Technology) but applies it to a familiar object (a cupboard).
|
|
1828
|
+
Decision: Slightly Complex.
|
|
1829
|
+
Reasoning: Do not confuse "Technical Vocabulary" (scale, thick lines) with "Theoretical Complexity." If the underlying concept is familiar (furniture), the SMK load is low.
|
|
1830
|
+
|
|
1831
|
+
3. Critical Calibration Examples
|
|
1832
|
+
Text: "Make a rough sketch... How many shelves should the cupboard have?" (Grade 2) -> Slightly Complex.
|
|
1833
|
+
Reasoning: (Rule D/Rule A) Although it mentions "scale" and "technology," the task is concrete and relies on everyday knowledge.
|
|
1834
|
+
Text: "Hydraulic propulsion works by sucking water at the bow and forcing it sternward." (Grade 10) -> Very Complex.
|
|
1835
|
+
Reasoning: (Rule B) Explains a mechanism using physics principles.
|
|
1836
|
+
Text: "The Allies fight the enemy's cavalry; we remember the hospitality to priests during the Revolution." (Grade 6) -> Very Complex.
|
|
1837
|
+
Reasoning: (Rule C) Assumes undefined knowledge of WWI alliances and the specific political history of the French Revolution.
|
|
1838
|
+
Text: "Immigrants from Poland, Italy, and Russia arrived. Most were Catholic or Orthodox." (Grade 7) -> Moderately Complex.
|
|
1839
|
+
Reasoning: (Rule C) Standard K-8 topic. Lists of nationalities are content vocabulary, not specialized theory.
|
|
1840
|
+
|
|
1841
|
+
4. Output Format
|
|
1842
|
+
Return your analysis in a valid JSON object. Do not include markdown formatting.
|
|
1843
|
+
Keys:
|
|
1844
|
+
- identified_topics: List[str] identifying the core subjects.
|
|
1845
|
+
- curriculum_check: String explaining if the topics are "Standard/General" (typical for K-8) or "Specialized/High School" (typical for 9-12).
|
|
1846
|
+
- assumptions_and_scaffolding: String analyzing what the author assumes the reader knows vs what is explained.
|
|
1847
|
+
- friction_analysis: String discussing the gap between Concrete description and Abstract meaning.
|
|
1848
|
+
- complexity_score: String (One of: slightly_complex, moderately_complex, very_complex, exceedingly_complex).
|
|
1849
|
+
- reasoning: String synthesizing the decision.
|
|
1850
|
+
|
|
1851
|
+
`;
|
|
1852
|
+
|
|
1853
|
+
// ../../evals/prompts/subject-matter-knowledge/user.txt
|
|
1854
|
+
var user_default2 = "Analyze:\nText: {text}\nGrade: {grade}\nFK Score: {fk_score}";
|
|
1855
|
+
|
|
1856
|
+
// src/prompts/subject-matter-knowledge/index.ts
|
|
1857
|
+
function getSystemPrompt3() {
|
|
1858
|
+
return system_default2;
|
|
1859
|
+
}
|
|
1860
|
+
function getUserPrompt3(text, grade, fkScore) {
|
|
1861
|
+
return user_default2.replaceAll("{text}", text).replaceAll("{grade}", grade).replaceAll("{fk_score}", fkScore.toString());
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
// src/evaluators/smk.ts
|
|
1865
|
+
var SmkEvaluator = class _SmkEvaluator extends BaseEvaluator {
|
|
1866
|
+
static metadata = {
|
|
1867
|
+
id: "subject-matter-knowledge",
|
|
1868
|
+
name: "Subject Matter Knowledge",
|
|
1869
|
+
description: "Evaluates background knowledge demands of educational texts relative to grade level",
|
|
1870
|
+
supportedGrades: ["3", "4", "5", "6", "7", "8", "9", "10", "11", "12"],
|
|
1871
|
+
requiresGoogleKey: true,
|
|
1872
|
+
requiresOpenAIKey: false
|
|
1873
|
+
};
|
|
1874
|
+
provider;
|
|
1875
|
+
constructor(config) {
|
|
1876
|
+
super(config);
|
|
1877
|
+
this.provider = createProvider({
|
|
1878
|
+
type: "google",
|
|
1879
|
+
model: "gemini-3-flash-preview",
|
|
1880
|
+
apiKey: config.googleApiKey,
|
|
1881
|
+
maxRetries: this.config.maxRetries
|
|
1882
|
+
});
|
|
1883
|
+
}
|
|
1884
|
+
/**
|
|
1885
|
+
* Evaluate subject matter knowledge complexity for a given text and grade level
|
|
1886
|
+
*
|
|
1887
|
+
* @param text - The text to evaluate
|
|
1888
|
+
* @param grade - The target grade level (3-12)
|
|
1889
|
+
* @returns Evaluation result with complexity score and detailed analysis
|
|
1890
|
+
* @throws {ValidationError} If text is empty, too short/long, or grade is invalid
|
|
1891
|
+
* @throws {APIError} If LLM API calls fail (includes AuthenticationError, RateLimitError, NetworkError, TimeoutError)
|
|
1892
|
+
*/
|
|
1893
|
+
async evaluate(text, grade) {
|
|
1894
|
+
this.logger.info("Starting SMK evaluation", {
|
|
1895
|
+
evaluator: "subject-matter-knowledge",
|
|
1896
|
+
operation: "evaluate",
|
|
1897
|
+
grade,
|
|
1898
|
+
textLength: text.length
|
|
1899
|
+
});
|
|
1900
|
+
const startTime = Date.now();
|
|
1901
|
+
const stageDetails = [];
|
|
1902
|
+
try {
|
|
1903
|
+
this.validateText(text);
|
|
1904
|
+
this.validateGrade(grade, new Set(_SmkEvaluator.metadata.supportedGrades));
|
|
1905
|
+
this.logger.debug("Evaluating subject matter knowledge complexity", {
|
|
1906
|
+
evaluator: "subject-matter-knowledge",
|
|
1907
|
+
operation: "smk_evaluation"
|
|
1908
|
+
});
|
|
1909
|
+
const fkScore = calculateFleschKincaidGrade(text);
|
|
1910
|
+
const response = await this.evaluateSmk(text, grade, fkScore);
|
|
1911
|
+
stageDetails.push({
|
|
1912
|
+
stage: "smk_evaluation",
|
|
1913
|
+
provider: "google:gemini-3-flash-preview",
|
|
1914
|
+
latency_ms: response.latencyMs,
|
|
1915
|
+
token_usage: {
|
|
1916
|
+
input_tokens: response.usage.inputTokens,
|
|
1917
|
+
output_tokens: response.usage.outputTokens
|
|
1918
|
+
}
|
|
1919
|
+
});
|
|
1920
|
+
const latencyMs = Date.now() - startTime;
|
|
1921
|
+
const totalTokenUsage = {
|
|
1922
|
+
input_tokens: stageDetails.reduce((sum, s) => sum + (s.token_usage?.input_tokens || 0), 0),
|
|
1923
|
+
output_tokens: stageDetails.reduce((sum, s) => sum + (s.token_usage?.output_tokens || 0), 0)
|
|
1924
|
+
};
|
|
1925
|
+
const result = {
|
|
1926
|
+
score: response.data.complexity_score,
|
|
1927
|
+
reasoning: response.data.reasoning,
|
|
1928
|
+
metadata: {
|
|
1929
|
+
model: "google:gemini-3-flash-preview",
|
|
1930
|
+
processingTimeMs: latencyMs
|
|
1931
|
+
},
|
|
1932
|
+
_internal: response.data
|
|
1933
|
+
};
|
|
1934
|
+
this.sendTelemetry({
|
|
1935
|
+
status: "success",
|
|
1936
|
+
latencyMs,
|
|
1937
|
+
textLength: text.length,
|
|
1938
|
+
grade,
|
|
1939
|
+
provider: "google:gemini-3-flash-preview",
|
|
1940
|
+
tokenUsage: totalTokenUsage,
|
|
1941
|
+
metadata: {
|
|
1942
|
+
stage_details: stageDetails
|
|
1943
|
+
},
|
|
1944
|
+
inputText: text
|
|
1945
|
+
}).catch(() => {
|
|
1946
|
+
});
|
|
1947
|
+
this.logger.info("SMK evaluation completed successfully", {
|
|
1948
|
+
evaluator: "subject-matter-knowledge",
|
|
1949
|
+
operation: "evaluate",
|
|
1950
|
+
grade,
|
|
1951
|
+
score: result.score,
|
|
1952
|
+
processingTimeMs: latencyMs
|
|
1953
|
+
});
|
|
1954
|
+
return result;
|
|
1955
|
+
} catch (error) {
|
|
1956
|
+
const latencyMs = Date.now() - startTime;
|
|
1957
|
+
this.logger.error("SMK evaluation failed", {
|
|
1958
|
+
evaluator: "subject-matter-knowledge",
|
|
1959
|
+
operation: "evaluate",
|
|
1960
|
+
grade,
|
|
1961
|
+
error: error instanceof Error ? error : void 0,
|
|
1962
|
+
processingTimeMs: latencyMs,
|
|
1963
|
+
completedStages: stageDetails.length
|
|
1964
|
+
});
|
|
1965
|
+
const totalTokenUsage = stageDetails.length > 0 ? {
|
|
1966
|
+
input_tokens: stageDetails.reduce((sum, s) => sum + (s.token_usage?.input_tokens || 0), 0),
|
|
1967
|
+
output_tokens: stageDetails.reduce((sum, s) => sum + (s.token_usage?.output_tokens || 0), 0)
|
|
1968
|
+
} : void 0;
|
|
1969
|
+
this.sendTelemetry({
|
|
1970
|
+
status: "error",
|
|
1971
|
+
latencyMs,
|
|
1972
|
+
textLength: text.length,
|
|
1973
|
+
grade,
|
|
1974
|
+
provider: "google:gemini-3-flash-preview",
|
|
1975
|
+
tokenUsage: totalTokenUsage,
|
|
1976
|
+
errorCode: error instanceof Error ? error.name : "UnknownError",
|
|
1977
|
+
metadata: stageDetails.length > 0 ? { stage_details: stageDetails } : void 0,
|
|
1978
|
+
inputText: text
|
|
1979
|
+
}).catch(() => {
|
|
1980
|
+
});
|
|
1981
|
+
if (error instanceof ValidationError) {
|
|
1982
|
+
throw error;
|
|
1983
|
+
}
|
|
1984
|
+
throw wrapProviderError(error, "SMK evaluation failed");
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
/**
|
|
1988
|
+
* Run the SMK evaluation LLM call
|
|
1989
|
+
*/
|
|
1990
|
+
async evaluateSmk(text, grade, fkScore) {
|
|
1991
|
+
const response = await this.provider.generateStructured({
|
|
1992
|
+
messages: [
|
|
1993
|
+
{ role: "system", content: getSystemPrompt3() },
|
|
1994
|
+
{ role: "user", content: getUserPrompt3(text, grade, fkScore) }
|
|
1995
|
+
],
|
|
1996
|
+
schema: SmkOutputSchema,
|
|
1997
|
+
temperature: 0
|
|
1998
|
+
});
|
|
1999
|
+
return {
|
|
2000
|
+
data: response.data,
|
|
2001
|
+
usage: response.usage,
|
|
2002
|
+
latencyMs: response.latencyMs
|
|
2003
|
+
};
|
|
2004
|
+
}
|
|
2005
|
+
};
|
|
2006
|
+
async function evaluateSmk(text, grade, config) {
|
|
2007
|
+
const evaluator = new SmkEvaluator(config);
|
|
2008
|
+
return evaluator.evaluate(text, grade);
|
|
2009
|
+
}
|
|
1772
2010
|
var TextComplexityEvaluator = class _TextComplexityEvaluator extends BaseEvaluator {
|
|
1773
2011
|
static metadata = {
|
|
1774
2012
|
id: "text-complexity",
|
|
1775
2013
|
name: "Text Complexity",
|
|
1776
|
-
description: "Composite evaluator analyzing vocabulary
|
|
2014
|
+
description: "Composite evaluator analyzing vocabulary, sentence structure, and subject matter knowledge complexity",
|
|
1777
2015
|
supportedGrades: ["3", "4", "5", "6", "7", "8", "9", "10", "11", "12"],
|
|
1778
2016
|
requiresGoogleKey: true,
|
|
1779
2017
|
requiresOpenAIKey: true
|
|
1780
2018
|
};
|
|
1781
2019
|
vocabularyEvaluator;
|
|
1782
2020
|
sentenceStructureEvaluator;
|
|
2021
|
+
smkEvaluator;
|
|
1783
2022
|
limit;
|
|
1784
2023
|
constructor(config) {
|
|
1785
2024
|
super(config);
|
|
1786
2025
|
this.vocabularyEvaluator = new VocabularyEvaluator(config);
|
|
1787
2026
|
this.sentenceStructureEvaluator = new SentenceStructureEvaluator(config);
|
|
2027
|
+
this.smkEvaluator = new SmkEvaluator(config);
|
|
1788
2028
|
this.limit = pLimit__default.default(3);
|
|
1789
2029
|
}
|
|
1790
2030
|
/**
|
|
1791
2031
|
* Evaluate text complexity for a given text and grade level
|
|
1792
2032
|
*
|
|
1793
|
-
* Runs vocabulary
|
|
1794
|
-
* If
|
|
2033
|
+
* Runs vocabulary, sentence structure, and SMK evaluations in parallel with concurrency control.
|
|
2034
|
+
* If all three sub-evaluators fail, throws an error. Otherwise returns a result map where
|
|
1795
2035
|
* failed sub-evaluators are represented as `{ error: Error }`.
|
|
1796
2036
|
*
|
|
1797
2037
|
* @param text - The text to evaluate
|
|
@@ -1810,18 +2050,21 @@ var TextComplexityEvaluator = class _TextComplexityEvaluator extends BaseEvaluat
|
|
|
1810
2050
|
this.validateText(text);
|
|
1811
2051
|
this.validateGrade(grade, new Set(_TextComplexityEvaluator.metadata.supportedGrades));
|
|
1812
2052
|
const startTime = Date.now();
|
|
1813
|
-
const [vocabResult, sentenceResult] = await Promise.all([
|
|
2053
|
+
const [vocabResult, sentenceResult, smkResult] = await Promise.all([
|
|
1814
2054
|
this.limit(() => this.runSubEvaluator(this.vocabularyEvaluator, text, grade)),
|
|
1815
|
-
this.limit(() => this.runSubEvaluator(this.sentenceStructureEvaluator, text, grade))
|
|
2055
|
+
this.limit(() => this.runSubEvaluator(this.sentenceStructureEvaluator, text, grade)),
|
|
2056
|
+
this.limit(() => this.runSubEvaluator(this.smkEvaluator, text, grade))
|
|
1816
2057
|
]);
|
|
1817
2058
|
const latencyMs = Date.now() - startTime;
|
|
1818
2059
|
const vocabFailed = "error" in vocabResult;
|
|
1819
2060
|
const sentenceFailed = "error" in sentenceResult;
|
|
1820
|
-
const
|
|
2061
|
+
const smkFailed = "error" in smkResult;
|
|
2062
|
+
const hasFailures = vocabFailed || sentenceFailed || smkFailed;
|
|
1821
2063
|
if (hasFailures) {
|
|
1822
2064
|
const errors = [];
|
|
1823
2065
|
if (vocabFailed) errors.push(`Vocabulary: ${vocabResult.error.message}`);
|
|
1824
2066
|
if (sentenceFailed) errors.push(`Sentence structure: ${sentenceResult.error.message}`);
|
|
2067
|
+
if (smkFailed) errors.push(`Subject matter knowledge: ${smkResult.error.message}`);
|
|
1825
2068
|
this.logger.error("Text complexity evaluation completed with errors", {
|
|
1826
2069
|
evaluator: "text-complexity",
|
|
1827
2070
|
operation: "evaluate",
|
|
@@ -1829,7 +2072,7 @@ var TextComplexityEvaluator = class _TextComplexityEvaluator extends BaseEvaluat
|
|
|
1829
2072
|
errors,
|
|
1830
2073
|
processingTimeMs: latencyMs
|
|
1831
2074
|
});
|
|
1832
|
-
if (vocabFailed && sentenceFailed) {
|
|
2075
|
+
if (vocabFailed && sentenceFailed && smkFailed) {
|
|
1833
2076
|
throw new Error(`Text complexity evaluation failed: ${errors.join("; ")}`);
|
|
1834
2077
|
}
|
|
1835
2078
|
}
|
|
@@ -1850,7 +2093,7 @@ var TextComplexityEvaluator = class _TextComplexityEvaluator extends BaseEvaluat
|
|
|
1850
2093
|
processingTimeMs: latencyMs,
|
|
1851
2094
|
hasFailures
|
|
1852
2095
|
});
|
|
1853
|
-
return { vocabulary: vocabResult, sentenceStructure: sentenceResult };
|
|
2096
|
+
return { vocabulary: vocabResult, sentenceStructure: sentenceResult, subjectMatterKnowledge: smkResult };
|
|
1854
2097
|
}
|
|
1855
2098
|
/**
|
|
1856
2099
|
* Run a sub-evaluator with error handling.
|
|
@@ -1882,6 +2125,7 @@ exports.NetworkError = NetworkError;
|
|
|
1882
2125
|
exports.RateLimitError = RateLimitError;
|
|
1883
2126
|
exports.SentenceAnalysisSchema = SentenceAnalysisSchema;
|
|
1884
2127
|
exports.SentenceStructureEvaluator = SentenceStructureEvaluator;
|
|
2128
|
+
exports.SmkEvaluator = SmkEvaluator;
|
|
1885
2129
|
exports.TextComplexityEvaluator = TextComplexityEvaluator;
|
|
1886
2130
|
exports.TextComplexityLevel = TextComplexityLevel;
|
|
1887
2131
|
exports.TimeoutError = TimeoutError;
|
|
@@ -1892,6 +2136,7 @@ exports.calculateFleschKincaidGrade = calculateFleschKincaidGrade;
|
|
|
1892
2136
|
exports.calculateReadabilityMetrics = calculateReadabilityMetrics;
|
|
1893
2137
|
exports.evaluateGradeLevelAppropriateness = evaluateGradeLevelAppropriateness;
|
|
1894
2138
|
exports.evaluateSentenceStructure = evaluateSentenceStructure;
|
|
2139
|
+
exports.evaluateSmk = evaluateSmk;
|
|
1895
2140
|
exports.evaluateTextComplexity = evaluateTextComplexity;
|
|
1896
2141
|
exports.evaluateVocabulary = evaluateVocabulary;
|
|
1897
2142
|
exports.featuresToJSON = featuresToJSON;
|