Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/development'
Browse files Browse the repository at this point in the history
  • Loading branch information
marcusgreen committed Apr 29, 2024
2 parents 437526a + ab2fe22 commit fcd1888
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 29 deletions.
4 changes: 2 additions & 2 deletions backup/moodle2/backup_qtype_aitext_plugin.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ protected function define_question_plugin_structure() {
$plugin->add_child($pluginwrapper);

// Now create the qtype own structures.
$aitext = new backup_nested_element('aitext', ['id'], array(
$aitext = new backup_nested_element('aitext', ['id'], [
'aiprompt', 'responseformat', 'responsefieldlines', 'minwordlimit', 'maxwordlimit',
'graderinfo', 'graderinfoformat', 'responsetemplate',
'responsetemplateformat', 'maxbytes'));
'responsetemplateformat', 'maxbytes']);

// Now the own qtype tree.
$pluginwrapper->add_child($aitext);
Expand Down
8 changes: 4 additions & 4 deletions lang/en/qtype_aitext.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,12 @@
*/

$string['acceptedfiletypes'] = 'Accepted file types';
$string['acceptedfiletypes_help'] = 'Accepted file types can be restricted by entering a list of file extensions. If the field is left empty, then all file types are allowed.';

$string['aiprompt'] = 'AI Prompt';
$string['aiprompt_help'] = 'A prompt for the Ai Grader. This is the guideline that AI uses to give feedback on the student response.';
$string['answerfiles'] = 'Answer files';
$string['answertext'] = 'Answer text';
$string['attachmentsoptional'] = 'Attachments are optional';
$string['attachmentsrequired'] = 'Require attachments';
$string['cachedef_stringdata'] = 'Cachedef stringdata';
$string['defaultmarksscheme'] = 'Marks scheme';
$string['defaultmarksscheme_setting'] = 'This will be the default marks scheme for new questions. Questions authors should alter this to suit the question.';
Expand All @@ -41,7 +39,6 @@
$string['thedefaultprompt'] = 'Explain if there is anything wrong with the grammar and spelling in the text.';
$string['disclaimer'] = 'Disclaimer';
$string['disclaimer_setting'] = 'Text appended to each response indicating feedback is from a Large Language Model and not a human';
$string['attachmentsrequired_help'] = 'This option specifies the minimum number of attachments required for a response to be considered gradable.';
$string['err_maxminmismatch'] = 'Maximum word limit must be greater than minimum word limit';
$string['err_maxwordlimit'] = 'Maximum word limit is enabled but is not set';
$string['err_maxwordlimitnegative'] = 'Maximum word limit cannot be a negative number';
Expand All @@ -64,6 +61,10 @@
$string['minwordlimit_help'] = 'If the response requires that students enter text, this is the minimum number of words that each student will be allowed to submit.';
$string['minwordlimitboundary'] = 'This question requires a response of at least {$a->limit} words and you are attempting to submit {$a->count} words. Please expand your response and try again.';
$string['nlines'] = '{$a} lines';
$string['prompt'] = 'Prompt';
$string['prompt_setting'] = 'Wrapper text for the prompt set to the AI System, [responsetext] is whatever the student typed as an answer. The ai prompt value from the question will be appended to this';
$string['jsonprompt'] = 'JSon prompt';
$string['jsonprompt_setting'] = 'Instructions sent to convert the returned value into json';
$string['pluginname'] = 'AI Text';
$string['pluginname_help'] = 'In response to a question, the respondent enters text. A response template may be provided. Responses are given a preliminary grade by an AI system (e.g. ChatGPT) then can be graded manually.';
$string['pluginname_link'] = 'question/type/AI Text';
Expand All @@ -81,7 +82,6 @@
$string['responsefieldlines'] = 'Input box size';
$string['responseformat'] = 'Response format';
$string['responseoptions'] = 'Response options';
$string['responserequired'] = 'Require text';
$string['responsenotrequired'] = 'Text input is optional';
$string['responseisrequired'] = 'Require the student to enter text';
$string['responsetemplate'] = 'Response template';
Expand Down
41 changes: 21 additions & 20 deletions question.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,21 +134,27 @@ public function apply_attempt_state(question_attempt_step $step) {
* @return void
*/
public function grade_response(array $response) : array {
if(!$this->is_complete_response($response)) {
$grade = [0 => 0, question_state::$needsgrading];
return $grade;
}
$ai = new ai\ai();
if (is_array($response)) {
$prompt = 'in [[' . strip_tags($response['answer']) . ']]';
$prompt .= ' analyse the part between [[ and ]] as follows: ';
$prompt .= $this->aiprompt;

$responsetext = strip_tags($response['answer']);
$responsetext = '[['.$responsetext.']]';
$prompt = get_config('qtype_aitext', 'prompt');
$prompt = preg_replace("/\[responsetext\]/", $responsetext, $prompt);
$prompt .= ' '.trim($this->aiprompt);
if ($this->markscheme > '') {
// Tell the LLM how to mark the submission.
$prompt .= " The total score is: $this->defaultmark .";
$prompt .= ' '.$this->markscheme;
} else {
// Todo should this be a plugin setting value?.
$prompt .= ' Set marks to null in the json object.'.PHP_EOL;
}
$prompt .= ' '.$this->get_json_prompt();

$prompt .= ' '.trim(get_config('qtype_aitext', 'jsonprompt'));
$prompt .= ' respond in the language '.current_language();

$llmresponse = $ai->prompt_completion($prompt);
$feedback = $llmresponse['response']['choices'][0]['message']['content'];
}
Expand All @@ -160,7 +166,7 @@ public function grade_response(array $response) : array {
$grade = [0 => 0, question_state::$needsgrading];
} else {
$fraction = $contentobject->marks / $this->defaultmark;
$grade = array($fraction, question_state::graded_state_for_fraction($fraction));
$grade = [$fraction, question_state::graded_state_for_fraction($fraction)];
}
// The -aicontent data is used in question preview. Only needs to happen in preview.
$this->insert_attempt_step_data('-aiprompt', $prompt);
Expand All @@ -180,6 +186,11 @@ public function grade_response(array $response) : array {
* @return \stdClass
*/
public function process_feedback(string $feedback) {
if (preg_match('/\{[^{}]*\}/', $feedback, $matches)) {
// $matches[1] contains the captured text inside the braces
$feedback = $matches[0];
}

$contentobject = json_decode($feedback);
if (json_last_error() === JSON_ERROR_NONE) {
$contentobject->feedback = trim($contentobject->feedback);
Expand All @@ -188,22 +199,12 @@ public function process_feedback(string $feedback) {
} else {
$contentobject = (object) [
"feedback" => $feedback,
"marks" => null
"marks" => null,
];
}
return $contentobject;
}
/**
* Get the LLM string that tells it to return the result as json
*
* @return string
*/
protected function get_json_prompt() :string {
return 'Return only a JSON object which enumerates a set of 2 elements.
The elements should have properties of "feedback" and "marks".
The resulting JSON object should be in this format: {"feedback":"string","marks":"number"}
where marks is a single value summing all marks.\n\n';
}

/**
* Translate into the current language and
* store in a cache
Expand Down
26 changes: 26 additions & 0 deletions settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,31 @@
new lang_string('defaultmarksscheme', 'qtype_aitext'),
new lang_string('defaultmarksscheme_setting', 'qtype_aitext'),
new lang_string('thedefaultmarksscheme', 'qtype_aitext')));

$settings->add(new admin_setting_configtext(
'qtype_aitext/disclaimer',
new lang_string('disclaimer', 'qtype_aitext'),
new lang_string('disclaimer_setting', 'qtype_aitext'),
'(Response provided by ChatGPT)'
));
$settings->add(new admin_setting_configtextarea(
'qtype_aitext/prompt',
new lang_string('prompt', 'qtype_aitext'),
new lang_string('prompt_setting', 'qtype_aitext'),
'in [responsetext] analyse the part between [[ and ]] as follows:',
PARAM_RAW,
20,
3
));
$settings->add(new admin_setting_configtextarea(
'qtype_aitext/jsonprompt',
new lang_string('jsonprompt', 'qtype_aitext'),
new lang_string('jsonprompt_setting', 'qtype_aitext'),
'Return only a JSON object which enumerates a set of 2 elements.The JSON object should be in
this format: {feedback":"string","marks":"number"} where marks is a single value summing all marks.',
PARAM_RAW,
20,
6
));
}

5 changes: 2 additions & 3 deletions version.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,14 @@
* Version information for the essay question type.
*
* @package qtype_aitext
* @subpackage essay
* @copyright 2005 Mark Nielsen
* @copyright 2024 Marcus Green
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

defined('MOODLE_INTERNAL') || die();

$plugin->component = 'qtype_aitext';
$plugin->version = 2024021800;
$plugin->version = 2024042500;
$plugin->requires = 2020110900;
$plugin->maturity = MATURITY_BETA;
$plugin->dependencies = [
Expand Down

0 comments on commit fcd1888

Please sign in to comment.