From 3c6ada456fdcf39538ef8ed37cdae4acc7693f4f Mon Sep 17 00:00:00 2001 From: Marcus Green Date: Mon, 6 May 2024 20:56:24 +0100 Subject: [PATCH] Tidying and lint compliance --- CHANGES.txt | 2 - .../backup_qtype_aitext_plugin.class.php | 6 +- .../restore_qtype_aitext_plugin.class.php | 11 +- changelog.md | 7 + classes/external.php | 158 ++++++++++-------- db/services.php | 32 +++- db/upgrade.php | 4 +- edit_aitext_form.php | 6 +- question.php | 17 +- version.php | 3 +- 10 files changed, 148 insertions(+), 98 deletions(-) delete mode 100644 CHANGES.txt create mode 100644 changelog.md diff --git a/CHANGES.txt b/CHANGES.txt deleted file mode 100644 index 7db2f16..0000000 --- a/CHANGES.txt +++ /dev/null @@ -1,2 +0,0 @@ -Version 200240503 -- Added prompt tester feature \ No newline at end of file diff --git a/backup/moodle2/backup_qtype_aitext_plugin.class.php b/backup/moodle2/backup_qtype_aitext_plugin.class.php index 13e2538..b6b8ae5 100755 --- a/backup/moodle2/backup_qtype_aitext_plugin.class.php +++ b/backup/moodle2/backup_qtype_aitext_plugin.class.php @@ -18,7 +18,7 @@ * Aitext backup * * @package qtype_aitext - * @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com} + * @copyright 2024 Marcus Green * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ @@ -26,7 +26,7 @@ /** * Provides the information to backup aitext questions * - * @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com} + * @copyright 2024 Marcus Green * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class backup_qtype_aitext_plugin extends backup_qtype_plugin { @@ -48,7 +48,7 @@ protected function define_question_plugin_structure() { // Now create the qtype own structures. $aitext = new backup_nested_element('aitext', ['id'], [ - 'aiprompt','markscheme','sampleanswer', 'responseformat', 'responsefieldlines', 'minwordlimit', 'maxwordlimit', + 'aiprompt', 'markscheme', 'sampleanswer', 'responseformat', 'responsefieldlines', 'minwordlimit', 'maxwordlimit', 'graderinfo', 'graderinfoformat', 'responsetemplate', 'responsetemplateformat', 'maxbytes']); diff --git a/backup/moodle2/restore_qtype_aitext_plugin.class.php b/backup/moodle2/restore_qtype_aitext_plugin.class.php index cabee6b..2a5ce1a 100755 --- a/backup/moodle2/restore_qtype_aitext_plugin.class.php +++ b/backup/moodle2/restore_qtype_aitext_plugin.class.php @@ -19,7 +19,8 @@ * * @package qtype_aitext * @subpackage backup-moodle2 - * @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com} + * @copyright 2024 Marcus Green + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ @@ -30,7 +31,7 @@ * restore plugin class that provides the necessary information * needed to restore one aitext qtype plugin * - * @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com} + * @copyright 2024 Marcus Green * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class restore_qtype_aitext_plugin extends restore_qtype_plugin { @@ -39,9 +40,9 @@ class restore_qtype_aitext_plugin extends restore_qtype_plugin { * Returns the paths to be handled by the plugin at question level */ protected function define_question_plugin_structure() { - return array( - new restore_path_element('aitext', $this->get_pathfor('/aitext')) - ); + return [ + new restore_path_element('aitext', $this->get_pathfor('/aitext')), + ]; } /** diff --git a/changelog.md b/changelog.md new file mode 100644 index 0000000..32906c4 --- /dev/null +++ b/changelog.md @@ -0,0 +1,7 @@ +Release 0.01 of the Moodle AIText question type May 2024 +Version 2024050300 +Many thanks to Justin Hunt of Poodll fame https://poodll.com/moodle for contributing code +to allw the testing of prompts from within the question editing form. + +Refined the default prompt settings to ensure Ollama/mistral returns a number when grading + diff --git a/classes/external.php b/classes/external.php index 50a137b..3a06528 100644 --- a/classes/external.php +++ b/classes/external.php @@ -1,74 +1,84 @@ -libdir . '/externallib.php'); -require_once($CFG->dirroot . '/question/engine/bank.php'); - -use tool_aiconnect\ai; - - - -/** - * External class. - * - * @package qtype_aitext - * @author Justin Hunt - poodll.com - */ -class qtype_aitext_external extends external_api -{ - - public static function fetch_ai_grade_parameters() - { - return new external_function_parameters( - array('response' => new external_value(PARAM_TEXT, 'The students response to question'), - 'defaultmark' => new external_value(PARAM_INT, 'The total possible score'), - 'prompt' => new external_value(PARAM_TEXT, 'The AI Prompt'), - 'marksscheme' => new external_value(PARAM_TEXT, 'The marks scheme') - ) - ); - - } - - public static function fetch_ai_grade($response,$defaultmark,$prompt,$marksscheme) - { - //get our AI helper - $ai = new ai\ai(); - - //build an aitext question instance so we can call the same code that the question type uses when it grades - $type = 'aitext'; - \question_bank::load_question_definition_classes($type); - $aiquestion = new qtype_aitext_question(); - $aiquestion->qtype = \question_bank::get_qtype('aitext'); - - //make sure we have the right data for AI to work with - if (!empty($response) && !empty($prompt) && $defaultmark > 0) { - $full_ai_prompt = $aiquestion->build_full_ai_prompt($response, $prompt, $defaultmark, $marksscheme); - $llmresponse = $ai->prompt_completion($full_ai_prompt); - $feedback = $llmresponse['response']['choices'][0]['message']['content']; - $contentobject = $aiquestion->process_feedback($feedback); - }else{ - $contentobject = ["feedback" => "Invalid parameters. Check that you have a sample answer and prompt","marks" => 0]; - } - - //return whatever we have got - return $contentobject; - - } - - public static function fetch_ai_grade_returns() - { - return new external_single_structure([ - 'feedback' => new external_value(PARAM_TEXT, 'text feedback for display to student', VALUE_DEFAULT), - 'marks' => new external_value(PARAM_FLOAT, 'AI grader awarded marks for student response', VALUE_DEFAULT), - ]); - - } - -} \ No newline at end of file +. + +/** + * External + * + * @package qtype_aitext + * @author Justin Hunt - poodll.com + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->libdir . '/externallib.php'); +require_once($CFG->dirroot . '/question/engine/bank.php'); + +use tool_aiconnect\ai; + +/** + * External class. + * + * @package qtype_aitext + * @author Justin Hunt - poodll.com + */ +class qtype_aitext_external extends external_api { + + public static function fetch_ai_grade_parameters() { + return new external_function_parameters( + array('response' => new external_value(PARAM_TEXT, 'The students response to question'), + 'defaultmark' => new external_value(PARAM_INT, 'The total possible score'), + 'prompt' => new external_value(PARAM_TEXT, 'The AI Prompt'), + 'marksscheme' => new external_value(PARAM_TEXT, 'The marks scheme') + ) + ); + + } + + public static function fetch_ai_grade($response, $defaultmark, $prompt, $marksscheme) { + // Get our AI helper. + $ai = new ai\ai(); + + // Build an aitext question instance so we can call the same code that the question type uses when it grades. + $type = 'aitext'; + \question_bank::load_question_definition_classes($type); + $aiquestion = new qtype_aitext_question(); + $aiquestion->qtype = \question_bank::get_qtype('aitext'); + + // Make sure we have the right data for AI to work with. + if (!empty($response) && !empty($prompt) && $defaultmark > 0) { + $fullaiprompt = $aiquestion->build_full_ai_prompt($response, $prompt, $defaultmark, $marksscheme); + $llmresponse = $ai->prompt_completion($fullaiprompt); + $feedback = $llmresponse['response']['choices'][0]['message']['content']; + $contentobject = $aiquestion->process_feedback($feedback); + } else { + $contentobject = ["feedback" => "Invalid parameters. Check that you have a sample answer and prompt", "marks" => 0]; + } + + // Return whatever we have got. + return $contentobject; + + } + + public static function fetch_ai_grade_returns() { + return new external_single_structure([ + 'feedback' => new external_value(PARAM_TEXT, 'text feedback for display to student', VALUE_DEFAULT), + 'marks' => new external_value(PARAM_FLOAT, 'AI grader awarded marks for student response', VALUE_DEFAULT), + ]); + + } + +} diff --git a/db/services.php b/db/services.php index dfa7cef..6c68d7e 100644 --- a/db/services.php +++ b/db/services.php @@ -1,18 +1,42 @@ . + +/** + * Aitext services definition + * + * @package qtype_aitext + * @author Justin Hunt - poodll.com + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + /** - * Services definition. + * External class. * - * @package qtype_minispeak + * @package qtype_aitext * @author Justin Hunt - poodll.com */ +defined('MOODLE_INTERNAL') || die(); $functions = array( 'qtype_aitext_fetch_ai_grade' => array( 'classname' => 'qtype_aitext_external', 'methodname' => 'fetch_ai_grade', 'description' => 'checks a response with the AI grader' , - 'capabilities'=> 'mod/quiz:grade', + 'capabilities' => 'mod/quiz:grade', 'type' => 'read', 'ajax' => true, ), -); \ No newline at end of file +); diff --git a/db/upgrade.php b/db/upgrade.php index 1df17d0..86e2991 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -22,7 +22,6 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -defined('MOODLE_INTERNAL') || die(); /** * Upgrade code for the aitext question type. @@ -37,12 +36,13 @@ function xmldb_qtype_aitext_upgrade($oldversion) { if ($oldversion < 2024050300) { $table = new xmldb_table('qtype_aitext'); + // Used for prompt testing in the edit form. $field = new xmldb_field('sampleanswer', XMLDB_TYPE_TEXT, 'small', null, null, null, null); if (!$dbman->field_exists($table, $field)) { $dbman->add_field($table, $field); } - // cloudpoodll savepoint reached + // Savepoint reached. upgrade_plugin_savepoint(true, 2024050300, 'qtype', 'aitext'); } diff --git a/edit_aitext_form.php b/edit_aitext_form.php index 9afe088..089f0dc 100755 --- a/edit_aitext_form.php +++ b/edit_aitext_form.php @@ -23,7 +23,6 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ - /** * aitext question type editing form. * @@ -69,7 +68,8 @@ protected function definition_inner($mform) { ['maxlen' => 50, 'rows' => 6, 'size' => 30]); $mform->setType('sampleanswer', PARAM_RAW); $mform->addHelpButton('sampleanswer', 'sampleanswer', 'qtype_aitext'); - $mform->addElement('static','sampleanswereval','', '' + $mform->addElement('static', 'sampleanswereval', '', '' . get_string('sampleanswerevaluate', 'qtype_aitext') . '' . '
'); @@ -115,7 +115,7 @@ protected function definition_inner($mform) { $mform->addElement('editor', 'graderinfo', get_string('graderinfo', 'qtype_aitext'), array('rows' => 10), $this->editoroptions); - //load any JS that we need to make things happen, specifically the prompt tester + // Load any JS that we need to make things happen, specifically the prompt tester. $PAGE->requires->js_call_amd('qtype_aitext/editformhelper', 'init', []); } diff --git a/question.php b/question.php index 7f9e778..e47e474 100755 --- a/question.php +++ b/question.php @@ -55,6 +55,14 @@ class qtype_aitext_question extends question_graded_automatically_with_countback /** @var int indicates whether the maximum number of words required */ public $maxwordlimit; + + /** + * used in the question editing interface + * + * @var string + */ + public $sampleanswer; + /** * Information on how to manually grade * @@ -140,8 +148,9 @@ public function grade_response(array $response) : array { } $ai = new ai\ai(); if (is_array($response)) { - $full_ai_prompt = $this->build_full_ai_prompt($response['answer'], $this->aiprompt, $this->defaultmark, $this->markscheme); - $llmresponse = $ai->prompt_completion($full_ai_prompt); + $fullaiprompt = $this->build_full_ai_prompt($response['answer'], $this->aiprompt, + $this->defaultmark, $this->markscheme); + $llmresponse = $ai->prompt_completion($fullaiprompt); $feedback = $llmresponse['response']['choices'][0]['message']['content']; } @@ -155,7 +164,7 @@ public function grade_response(array $response) : array { $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', $full_ai_prompt); + $this->insert_attempt_step_data('-aiprompt', $fullaiprompt); $this->insert_attempt_step_data('-aicontent', $contentobject->feedback); $this->insert_attempt_step_data('-comment', $contentobject->feedback); @@ -173,7 +182,7 @@ public function build_full_ai_prompt($response, $aiprompt, $defaultmark, $marksc $prompt .= ' '.trim($aiprompt); if ($markscheme > '') { - //Tell the LLM how to mark the submission + // Tell the LLM how to mark the submission. $prompt .= " The total score is: $defaultmark ."; $prompt .= ' '.$markscheme; } else { diff --git a/version.php b/version.php index 24f9aee..70e91e2 100755 --- a/version.php +++ b/version.php @@ -27,7 +27,8 @@ $plugin->component = 'qtype_aitext'; $plugin->version = 2024050300; $plugin->requires = 2020110900; +$plugin->release = '0.01'; $plugin->maturity = MATURITY_BETA; $plugin->dependencies = [ - 'tool_aiconnect' => ANY_VERSION + 'tool_aiconnect' => ANY_VERSION, ];