From 50b08d28ddbbd2dedacae85c92b169de254af478 Mon Sep 17 00:00:00 2001 From: marcus Date: Tue, 16 Apr 2024 21:39:41 +0100 Subject: [PATCH 1/3] Move jsonprompt text from hardcoded to settings value --- lang/en/qtype_aitext.php | 4 ++++ question.php | 18 +++++++++--------- settings.php | 15 ++++++++++++++- version.php | 2 +- 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/lang/en/qtype_aitext.php b/lang/en/qtype_aitext.php index 2e0190c..358b743 100755 --- a/lang/en/qtype_aitext.php +++ b/lang/en/qtype_aitext.php @@ -58,6 +58,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'; +$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'; diff --git a/question.php b/question.php index bcb806d..c4f284f 100755 --- a/question.php +++ b/question.php @@ -136,17 +136,19 @@ public function apply_attempt_state(question_attempt_step $step) { public function grade_response(array $response) : array { $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; - - if ($this->markscheme > '') { + $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 > '') { $prompt .= ' '.$this->markscheme; } else { $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']; } @@ -197,9 +199,7 @@ public function process_feedback(string $feedback) { * @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"} + return 'Return only a JSON object where the JSON object is in the this format: {"feedback":"string","marks":"number"} where marks is a single value summing all marks.\n\n'; } /** diff --git a/settings.php b/settings.php index 919628a..a4f873c 100644 --- a/settings.php +++ b/settings.php @@ -27,6 +27,19 @@ $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)")); + '(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 + )); + } diff --git a/version.php b/version.php index 445bf13..f33250f 100755 --- a/version.php +++ b/version.php @@ -26,7 +26,7 @@ defined('MOODLE_INTERNAL') || die(); $plugin->component = 'qtype_aitext'; -$plugin->version = 2024021800; +$plugin->version = 2024041401; $plugin->requires = 2020110900; $plugin->maturity = MATURITY_BETA; $plugin->dependencies = [ From a681216400b8d2a740fb9ae3461d3cf25677624c Mon Sep 17 00:00:00 2001 From: marcus Date: Tue, 23 Apr 2024 21:43:09 +0100 Subject: [PATCH 2/3] removed function get_json_prompt as it is now taken from settings --- question.php | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/question.php b/question.php index c4f284f..7bef342 100755 --- a/question.php +++ b/question.php @@ -143,6 +143,7 @@ public function grade_response(array $response) : array { $prompt .= ' '.trim($this->aiprompt); if ($this->markscheme > '') { $prompt .= ' '.$this->markscheme; } else { + /** @todo should this be a plugin setting value? */ $prompt .= ' Set marks to null in the json object.'.PHP_EOL; } @@ -193,15 +194,7 @@ public function process_feedback(string $feedback) { } 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 where the JSON object is in the 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 From ab2fe22c7b30befc3c7eaa3625687aba969317ae Mon Sep 17 00:00:00 2001 From: marcus Date: Tue, 23 Apr 2024 22:25:44 +0100 Subject: [PATCH 3/3] Lint fixes --- .../backup_qtype_aitext_plugin.class.php | 4 +- question.php | 10 ++--- settings.php | 40 +++++++++++-------- version.php | 2 +- 4 files changed, 32 insertions(+), 24 deletions(-) diff --git a/backup/moodle2/backup_qtype_aitext_plugin.class.php b/backup/moodle2/backup_qtype_aitext_plugin.class.php index 3d26ad8..da0c26d 100755 --- a/backup/moodle2/backup_qtype_aitext_plugin.class.php +++ b/backup/moodle2/backup_qtype_aitext_plugin.class.php @@ -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); diff --git a/question.php b/question.php index 7bef342..811659a 100755 --- a/question.php +++ b/question.php @@ -138,16 +138,16 @@ public function grade_response(array $response) : array { if (is_array($response)) { $responsetext = strip_tags($response['answer']); $responsetext = '[['.$responsetext.']]'; - $prompt = get_config('qtype_aitext','prompt'); + $prompt = get_config('qtype_aitext', 'prompt'); $prompt = preg_replace("/\[responsetext\]/", $responsetext, $prompt); $prompt .= ' '.trim($this->aiprompt); if ($this->markscheme > '') { $prompt .= ' '.$this->markscheme; } else { - /** @todo should this be a plugin setting value? */ + // Todo should this be a plugin setting value?. $prompt .= ' Set marks to null in the json object.'.PHP_EOL; } - $prompt .= ' '.trim(get_config('qtype_aitext','jsonprompt')); + $prompt .= ' '.trim(get_config('qtype_aitext', 'jsonprompt')); $prompt .= ' respond in the language '.current_language(); $llmresponse = $ai->prompt_completion($prompt); @@ -161,7 +161,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); @@ -189,7 +189,7 @@ public function process_feedback(string $feedback) { } else { $contentobject = (object) [ "feedback" => $feedback, - "marks" => null + "marks" => null, ]; } return $contentobject; diff --git a/settings.php b/settings.php index a4f873c..e5642c8 100644 --- a/settings.php +++ b/settings.php @@ -24,22 +24,30 @@ defined('MOODLE_INTERNAL') || die; if ($ADMIN->fulltree) { - $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 + $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 - )); - + PARAM_RAW, + 20, + 6 + )); } diff --git a/version.php b/version.php index f33250f..50623c5 100755 --- a/version.php +++ b/version.php @@ -26,7 +26,7 @@ defined('MOODLE_INTERNAL') || die(); $plugin->component = 'qtype_aitext'; -$plugin->version = 2024041401; +$plugin->version = 2024042201; $plugin->requires = 2020110900; $plugin->maturity = MATURITY_BETA; $plugin->dependencies = [