Skip to main content

learning-commons-evaluators

PyPI version

Python SDK for Learning Commons evaluators.

Supported evaluators

Evaluator family Evaluator
Text Complexity evaluators - Docs Grade Level Appropriateness
Background Knowledge Demands
Vocabulary Complexity
Sentence Structure
Meaning Directness
Purpose Clarity
Organizational Structure
Reference Knowledge Demands
Graphics evaluators - Docs Graphics Complexity
Graphics Accuracy
Feedback evaluators - Docs Strength Acknowledgment
Revision Actionability
Student Response Specificity
Revision Accuracy
Revision Manageability
Withholding Answers
Tone Appropriateness
Academic Standards evaluators - Docs Math Standards Alignment
Durable Skills evaluators - Docs Critical Thinking

Dependencies

  • Python 3.11+
  • The provider clients ship with the package (openai, anthropic, google-genai), along with pydantic, httpx, pillow, and textstat.
  • Credentials are constructor arguments. The SDK reads no provider keys from the environment, so a shell GOOGLE_API_KEY leaves google_api_key unset. A missing key raises ConfigurationError at construction, before any I/O.

Installation

Install the SDK:

pip install learning-commons-evaluators

Pass the key each evaluator's default provider requires (which keys):

GradeLevelAppropriatenessEvaluator(google_api_key="...")       # Google
SentenceStructureEvaluator(openai_api_key="...")                # OpenAI
CriticalThinkingEvaluator(anthropic_api_key="...")              # Anthropic
VocabularyComplexityEvaluator(openai_api_key="...", google_api_key="...")
MathStandardsAlignmentEvaluator(
    anthropic_api_key="...",
    learning_commons_api_key="...",  # Knowledge Graph
)

Quickstart

from learning_commons_evaluators import GradeLevelAppropriatenessEvaluator

# Configure evaluator
evaluator = GradeLevelAppropriatenessEvaluator(google_api_key="...")

# Run an evaluation
evaluation = evaluator.evaluate_sync(text="The cat's out of the bag now.")

# Print evaluation results
print(evaluation.result.grade_band)              # a CCSS band, e.g. "2-3"
print(evaluation.result.alternative_grade_band)  # the band reachable with scaffolding
print(evaluation.result.scaffolding_needed)      # what that band would need
print(evaluation.metadata.model)                 # "google:gemini-3.6-flash"

await evaluator.evaluate(...) is the same call from async code. evaluate_sync runs it to completion with asyncio.run, so call it from synchronous code. Inside a running event loop, await evaluate.

Methods

The package provides the following:

Import them from learning_commons_evaluators. The text-feature helpers live in learning_commons_evaluators.features.

Discovery

from learning_commons_evaluators import get_evaluators, get_evaluator

get_evaluators()
# tuple[EvaluatorMetadata, ...] — every evaluator, in taxonomy order

get_evaluator("text_complexity.ela_reading.vocabulary_complexity")
# EvaluatorMetadata | None — current and historical ids both resolve

get_evaluator("grade-level-appropriateness").id
# "text_complexity.ela_reading.grade_level_appropriateness"

EvaluatorMetadata carries id, stable_id, id_history, name, description, supported_grades, default_providers, required_credentials, and an outcome block naming which result fields hold the verdict and its rationale (None when the evaluator declares no single verdict). required_credentials lists only non-LLM services: ("learning_commons_api_key",) for math standards alignment and () for the other eighteen. Provider keys follow default_providers, so (Provider.GOOGLE,) means supply google_api_key.

Evaluation

Every evaluator class exposes .evaluate(...) and .evaluate_sync(...) (see Supported evaluators for each evaluator's input and output).

Pass the contract's fields as keywords, or pass the evaluator's input model. Passing both raises TypeError.

import asyncio
from learning_commons_evaluators import GradeLevelAppropriatenessEvaluator

evaluator = GradeLevelAppropriatenessEvaluator(google_api_key="...")

# Sync
evaluation = evaluator.evaluate_sync(text="The cat's out of the bag now.")
print(evaluation.result.grade_band)

# Async
async def main() -> None:
  evaluation = await evaluator.evaluate(text="The cat's out of the bag now.")
  print(evaluation.result.grade_band)

if __name__ == "__main__":
  asyncio.run(main())
Evaluator family Evaluator Typical input
Text Complexity GradeLevelAppropriatenessEvaluator { text }
BackgroundKnowledgeDemandsEvaluator { text, grade_level }
VocabularyComplexityEvaluator { text, grade_level }
SentenceStructureEvaluator { text, grade_level }
MeaningDirectnessEvaluator { text, grade_level }
PurposeClarityEvaluator { text, grade_level }
OrganizationalStructureEvaluator { text, grade_level }
ReferenceKnowledgeDemandsEvaluator { text, grade_level }
Graphics GraphicsComplexityEvaluator { text, grade_level, image_paths, figure_labels? }
GraphicsAccuracyEvaluator { image_paths, claim }
Feedback StrengthAcknowledgmentEvaluator { student_text, feedback_text }
RevisionActionabilityEvaluator { student_text, feedback_text }
StudentResponseSpecificityEvaluator { student_text, feedback_text }
RevisionAccuracyEvaluator { student_text, feedback_text }
RevisionManageabilityEvaluator { student_text, feedback_text }
WithholdingAnswersEvaluator { student_text, feedback_text }
ToneAppropriatenessEvaluator { student_text, feedback_text }
Academic Standards MathStandardsAlignmentEvaluator { question, case_identifier_uuid }
Durable Skills CriticalThinkingEvaluator { assignment_text, source_passages, essay_text }

Every call returns an EvaluationResult: evaluator (the registry id), result (that evaluator's output model, keys unchanged), and metadata (model, processing_time_ms, token_usage). metadata.model is "provider:model", joined with + when several models ran. An evaluator whose contract declares several steps sums token usage across them.

grade_level on the text-complexity and graphics-complexity evaluators is an int (3–12). Vocabulary Complexity takes a different model branch for grades 3–4 than for 5–12, and construction validates the union of keys those branches need, so it requires both openai_api_key and google_api_key at every grade.

Each graphics path is a local file, read in your process. The SDK checks format, size, and edge limits before any model call. A file outside those limits raises InputValidationError. For a question with its expected answer, build the claim with compose_graphics_accuracy_claim(question, answer), which produces the text the evaluator was measured with:

from learning_commons_evaluators import (
    GraphicsAccuracyEvaluator,
    compose_graphics_accuracy_claim,
)

evaluation = GraphicsAccuracyEvaluator(google_api_key="...").evaluate_sync(
    image_paths=["figures/apples-in-baskets.png"],
    claim=compose_graphics_accuracy_claim("How many apples are there altogether?", "12"),
)
print(evaluation.result.is_correct, evaluation.result.basis)

source_passages is a list of {title?, author?, text} in the order the student received them. The first item is Source 1. A passage may omit title and author.

The Math Standards Alignment evaluator also exposes .evaluate_by_code(). evaluate / evaluate_sync take the CASE Network UUID. evaluate_by_code is async: it resolves a statement code, then runs the same evaluation.

import asyncio

from learning_commons_evaluators import Jurisdiction, MathStandardsAlignmentEvaluator

math = MathStandardsAlignmentEvaluator(
    anthropic_api_key="...",
    learning_commons_api_key="...",
)

# UUID: evaluate_sync wraps evaluate()
math.evaluate_sync(
    question="…",
    case_identifier_uuid="6ba25656-d7cc-11e8-824f-0242ac160002",
)

# Statement code, as that jurisdiction spells it
asyncio.run(
    math.evaluate_by_code(
        question="…",
        statement_code="3.MD.C.7.d",
        jurisdiction=Jurisdiction.MULTI_STATE,  # optional; Multi-State is Common Core
        grade_level="3",                        # optional; see below
    )
)

Spell the code the way its jurisdiction spells it. A code is a lookup key: Ohio writes 3.MD.7 where Common Core writes 3.MD.C.7. A Common Core code with jurisdiction=Jurisdiction.OHIO raises StandardNotFoundError. grade_level ("K" through "12") is consulted only when one code matches several standards. With no grade, the first copy that has learning components is evaluated. A grade that matches no copy raises InputValidationError. result.statement_code is the Knowledge Graph's own spelling of the copy that was judged, and result.case_identifier_uuid names it. The contract declares no single score, so read_outcome(...).score is None.

Outcomes

from learning_commons_evaluators import get_evaluator, read_outcome

evaluation = evaluator.evaluate_sync(text="The cat's out of the bag now.")
declared = get_evaluator(evaluation.evaluator)
outcome = read_outcome(evaluation, declared.outcome if declared else None)
print(outcome.score)       # the verdict, as one comparable string
print(outcome.reasoning)

read_outcome picks the verdict and rationale named by the evaluator's declared outcome block.

score is a string when the field is present. A boolean verdict is "true" or "false" (Graphics Accuracy). A feedback quality_score of 0 or 1 is "0" or "1". "0" is truthy, so compare explicitly (score == "1"), or read the numeric field from evaluation.result directly. score is None when the evaluator has no declared outcome or the payload is missing that field — a reporting gap. Math Standards Alignment is that case.

Standards

from learning_commons_evaluators import (
    Jurisdiction,
    KnowledgeGraphClient,
    normalize_statement_code,
)

normalize_statement_code("  5.nbt.a.1 ")  # "5.NBT.A.1"

async with KnowledgeGraphClient(api_key) as kg:
    matches = await kg.search_standards("5.NBT.A.1", jurisdiction=Jurisdiction.MULTI_STATE)
    standard = await kg.get_academic_standard(matches[0].case_identifier_uuid)
    components = await kg.get_learning_components(matches[0].case_identifier_uuid)
    component_set = await kg.get_learning_component_set(matches[0].case_identifier_uuid)
Method Returns Raises
search_standards(statement_code, ...) every match, best first (StandardMatch) InputValidationError, StandardNotFoundError, Knowledge Graph errors
get_academic_standard(case_identifier_uuid) one AcademicStandard InputValidationError, Knowledge Graph errors
get_learning_components(case_identifier_uuid) evaluable LearningComponents (descriptions present) InputValidationError, KnowledgeGraphError
get_learning_component_set(case_identifier_uuid) those components plus undescribed_count for components with no description InputValidationError, KnowledgeGraphError

search_standards defaults to Jurisdiction.MULTI_STATE (Common Core). Pass jurisdiction or academic_subject to override that. More than one match means the code is reused across courses or frameworks.

The client owns an HTTP connection pool, so close it (async with, or await kg.aclose()). It is bound to the event loop that first uses it. Keep one client inside one asyncio.run. Evaluators open and close the clients they need for each evaluation, so one evaluator can be reused across evaluate_sync calls.

Text features

These power sentence-structure preprocessing. They are deterministic and do not call a model. Counts come from textstat, which is the library the sentence-structure contract binds for Python.

from learning_commons_evaluators.features import (
    add_engineered_features,
    features_to_json,
    readability_counts,
)

readability_counts(text)          # ReadabilityCounts: counts and flesch_kincaid_grade
add_engineered_features(analysis)  # sentence analysis → features
features_to_json(features)         # JSON string for a prompt

More resources

Check out the docs for more implementation details:

License

MIT

Metadata

Release files for learning-commons-evaluators 1.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for learning-commons-evaluators 1.2.0
File Size Uploaded Provenance Actions
learning_commons_evaluators-1.2.0.tar.gz 228.8 kB With provenance Details for learning_commons_evaluators-1.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for learning-commons-evaluators 1.2.0
File Interpreter ABI Platform Actions
learning_commons_evaluators-1.2.0-py3-none-any.whl Python 3 none any Details for learning_commons_evaluators-1.2.0-py3-none-any.whl

Total release size: 579.7 kB

Release files / learning_commons_evaluators-1.2.0.tar.gz

Download URL learning_commons_evaluators-1.2.0.tar.gz
Size 228.8 kB
Tags Source
SHA-256 checksum
How to use checksums
09ccb522c11d0559301d6301832f728bf62fdf8c658d8bc0e3e289c7147df433
BLAKE2b-256 checksum
How to use checksums
a13ba042d347689c68b4a1230f52bc4628dce46e97c305bbb15f179ce00eac89
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 4, 2026.

Transparency log

Release files / learning_commons_evaluators-1.2.0-py3-none-any.whl

Download URL learning_commons_evaluators-1.2.0-py3-none-any.whl
Size 350.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1c96b8e913a3e0825a02e3ed3d269fb71fd4775516aadb60de854b98e9fd28b9
BLAKE2b-256 checksum
How to use checksums
3753fbd32f95f7228a208daa1a284acd8ee5c3aa81a1cc150183d85307eefc9a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 4, 2026.

Transparency log

Security for learning-commons-evaluators 1.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for learning_commons_evaluators-1.2.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 4, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page