ai_rubric_grader 0.1.1
ai_rubric_grader: ^0.1.1 copied to clipboard
LLM-agnostic, rubric-based automatic grading for Dart. Feed it questions, rubrics and student answers; get back structured, clamped scores with per-rubric rationale. Bring your own LLM client.
π― ai_rubric_grader #
LLM-agnostic, rubric-based automatic grading for Dart #
Feed it questions, rubrics and student answers β get back structured, clamped scores with per-rubric rationale. Bring your own LLM.
It's a small, dependency-light, pure-Dart package β no Flutter, no HTTP client baked in β so you can run it server-side (a Cloud Function, a backend, a CLI), which is where your API key belongs.
Why not just ask the model for marks? #
Asking an LLM to "grade this and give me the score" and parsing the free text is
fragile: a stray line or markdown fence silently becomes a 0, and the model can
hand out more points than a rubric allows. ai_rubric_grader fixes that:
| Naive approach | ai_rubric_grader |
|
|---|---|---|
| Output | Free text, hope it parses | Strict JSON, schema-guided |
| Totals | Trust the model's sums | Recomputed from your rubrics |
| Over-awarding | Possible | Clamped to [0, points] |
| Missing item | Crash / wrong total | Treated as 0, never crashes |
| Provider lock-in | Hardcoded | Any LLM via one interface |
| Manual criteria | β | autoGraded: false leaves them for a human |
Install #
dependencies:
ai_rubric_grader: ^0.1.1
or dart pub add ai_rubric_grader.
Usage #
import 'package:ai_rubric_grader/ai_rubric_grader.dart';
final questions = [
Question(
id: 'q1',
prompt: 'What is the derivative of xΒ²?',
answer: '2x', // plain text, LaTeX, transcribed handwritingβ¦
rubrics: const [
Rubric(id: 'r1', description: 'Correct derivative', requirement: 'equals 2x', points: 5),
Rubric(id: 'r2', description: 'Shows working', requirement: 'steps shown', points: 3,
autoGraded: false), // left for a human
],
),
];
final grader = RubricGrader(myLlmClient);
final result = await grader.grade(questions);
print('${result.awarded}/${result.possible} (${result.percentage}%)');
for (final g in result.questionGrades) {
for (final s in g.rubricScores) {
print('${s.rubricId}: ${s.awarded}/${s.possible} β ${s.rationale}');
}
}
Bring your own LLM #
Implement the one-method LlmClient for any provider. Request deterministic
output (temperature: 0) and JSON mode where available.
OpenAI adapter (click to expand)
import 'dart:convert';
import 'package:ai_rubric_grader/ai_rubric_grader.dart';
import 'package:http/http.dart' as http;
class OpenAiClient implements LlmClient {
OpenAiClient(this.apiKey, {this.model = 'gpt-4o-mini'});
final String apiKey;
final String model;
@override
Future<String> complete({required String system, required String user}) async {
final res = await http.post(
Uri.parse('https://api.openai.com/v1/chat/completions'),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer $apiKey',
},
body: jsonEncode({
'model': model,
'temperature': 0,
'response_format': {'type': 'json_object'},
'messages': [
{'role': 'system', 'content': system},
{'role': 'user', 'content': user},
],
}),
);
return (jsonDecode(res.body)['choices'][0]['message']['content']) as String;
}
}
π Security: never embed an LLM API key in a mobile/web client β it ships in the binary and can be extracted. Run the grader behind your backend.
API #
| Type | Purpose |
|---|---|
Rubric |
A criterion: description, requirement, points, autoGraded. |
Question |
prompt, answer, rubrics; exposes totalPoints / autoGradablePoints. |
RubricGrader |
grade(List<Question>) β Future<GradingResult>. |
GradingResult |
awarded, possible, percentage, questionGrades. |
RubricScore |
Per-rubric awarded (clamped), possible, rationale, isFullyMet. |
LlmClient |
The one method you implement to plug in a provider. |
All models are immutable and JSON-serializable (toJson / fromJson).
Related packages #
Part of the Flutter Grading Toolkit:
rubric_builderβ author rubrics in a Flutter UI.handwriting_answer_padβ capture handwritten answers.image_white_backgroundβ prep images for vision models.
Contributing #
Issues and PRs welcome. Run dart test before submitting.
License #
MIT Β© 2026 Muhammad Ali