From 28f46b31865e533d7986fb4dca172fe061bbbb4b Mon Sep 17 00:00:00 2001 From: Michael hughes Date: Tue, 5 Mar 2024 22:43:11 +0000 Subject: [PATCH 01/21] solrrag Adding Vector support to SOLR --- search/engine/solr/classes/engine.php | 9 +- search/engine/solr/classes/schema.php | 5 +- .../engine/solrrag/classes/ai/aiprovider.php | 35 +++++ search/engine/solrrag/classes/ai/api.php | 11 ++ search/engine/solrrag/classes/document.php | 41 ++++++ search/engine/solrrag/classes/engine.php | 127 ++++++++++++++++++ search/engine/solrrag/classes/schema.php | 114 ++++++++++++++++ .../engine/solrrag/lang/en/search_solrrag.php | 54 ++++++++ search/engine/solrrag/privacy/provider.php | 108 +++++++++++++++ search/engine/solrrag/settings.php | 98 ++++++++++++++ search/engine/solrrag/setup_schema.php | 57 ++++++++ search/engine/solrrag/version.php | 29 ++++ 12 files changed, 683 insertions(+), 5 deletions(-) create mode 100644 search/engine/solrrag/classes/ai/aiprovider.php create mode 100644 search/engine/solrrag/classes/ai/api.php create mode 100644 search/engine/solrrag/classes/document.php create mode 100644 search/engine/solrrag/classes/engine.php create mode 100644 search/engine/solrrag/classes/schema.php create mode 100644 search/engine/solrrag/lang/en/search_solrrag.php create mode 100644 search/engine/solrrag/privacy/provider.php create mode 100644 search/engine/solrrag/settings.php create mode 100644 search/engine/solrrag/setup_schema.php create mode 100644 search/engine/solrrag/version.php diff --git a/search/engine/solr/classes/engine.php b/search/engine/solr/classes/engine.php index 073a64b2648d..8da39b32021e 100644 --- a/search/engine/solr/classes/engine.php +++ b/search/engine/solr/classes/engine.php @@ -1095,6 +1095,7 @@ protected function add_stored_file($document, $storedfile) { // A giant block of code that is really just error checking around the curl request. try { + $requesturl = $url->out(false); // We have to post the file directly in binary data (not using multipart) to avoid // Solr bug SOLR-15039 which can cause incorrect data when you use multipart upload. // Note this loads the whole file into memory; see limit in file_is_indexable(). @@ -1120,6 +1121,10 @@ protected function add_stored_file($document, $storedfile) { // This is a common error, happening whenever a file fails to index for any reason, so we will make it quieter. if (CLI_SCRIPT && !PHPUNIT_TEST) { mtrace($message); + if(debugging()) { + mtrace($requesturl); + } + // Suspiciion that this fails due to the file contents being PDFs. } } else { // Check for the expected status field. @@ -1301,10 +1306,10 @@ public function is_server_configured() { return get_string('minimumsolr4', 'search_solr'); } } catch (\SolrClientException $ex) { - debugging('Solr client error: ' . html_to_text($ex->getMessage()), DEBUG_DEVELOPER); + debugging('Solr client error: ' . html_to_text($ex->getMessage(). " Server {$this->config->server_hostname}"), DEBUG_DEVELOPER); return get_string('engineserverstatus', 'search'); } catch (\SolrServerException $ex) { - debugging('Solr server error: ' . html_to_text($ex->getMessage()), DEBUG_DEVELOPER); + debugging('Solr server error: ' . html_to_text($ex->getMessage() . " Server {$this->config->server_hostname}"), DEBUG_DEVELOPER); return get_string('engineserverstatus', 'search'); } diff --git a/search/engine/solr/classes/schema.php b/search/engine/solr/classes/schema.php index 743166f2378b..594f3f558ad2 100644 --- a/search/engine/solr/classes/schema.php +++ b/search/engine/solr/classes/schema.php @@ -110,7 +110,7 @@ public function can_setup_server() { * @return bool */ public function setup($checkexisting = true) { - $fields = \search_solr\document::get_default_fields_definition(); + $fields = \search_solrrag\document::get_default_fields_definition(); // Field id is already there. unset($fields['id']); @@ -223,7 +223,6 @@ protected function add_fields($fields, $checkexisting = true) { */ protected function validate_fields(&$fields, $requireexisting = false) { global $CFG; - foreach ($fields as $fieldname => $data) { $url = $this->engine->get_connection_url('/schema/fields/' . $fieldname); $results = $this->curl->get($url); @@ -323,7 +322,7 @@ protected function validate_add_field_result($result) { * @param string $datatype * @return string */ - private function doc_field_to_solr_field($datatype) { + protected function doc_field_to_solr_field($datatype) { $type = $datatype; $solrversion = $this->engine->get_solr_major_version(); diff --git a/search/engine/solrrag/classes/ai/aiprovider.php b/search/engine/solrrag/classes/ai/aiprovider.php new file mode 100644 index 000000000000..9a216d83c823 --- /dev/null +++ b/search/engine/solrrag/classes/ai/aiprovider.php @@ -0,0 +1,35 @@ + [ + 'type' => PARAM_TEXT + ] + ]; + } + + /** + * We're overriding this whilst we don't have a real DB table. + * @param $filters + * @param $sort + * @param $order + * @param $skip + * @param $limit + * @return array + */ + public static function get_records($filters = array(), $sort = '', $order = 'ASC', $skip = 0, $limit = 0) { + $records = []; + $fake = new static(0, (object) [ + 'name' => "Fake AI Provider" + ]); + array_push($records, $fake); + return $records; + } +} \ No newline at end of file diff --git a/search/engine/solrrag/classes/ai/api.php b/search/engine/solrrag/classes/ai/api.php new file mode 100644 index 000000000000..c68e541757d5 --- /dev/null +++ b/search/engine/solrrag/classes/ai/api.php @@ -0,0 +1,11 @@ + array( + 'type' => 'string', + 'stored' => true, + 'indexed' => true + ), + 'solr_fileid' => array( + 'type' => 'string', + 'stored' => true, + 'indexed' => true + ), + 'solr_filecontenthash' => array( + 'type' => 'string', + 'stored' => true, + 'indexed' => true + ), + // Stores the status of file indexing. + 'solr_fileindexstatus' => array( + 'type' => 'int', + 'stored' => true, + 'indexed' => true + ), + // Field to index, but not store, file contents. + 'solr_filecontent' => array( + 'type' => 'text', + 'stored' => false, + 'indexed' => true, + 'mainquery' => true + ), + 'solr_vector' => [ + 'type' => 'knn_vector_10', + 'stored' => true, + 'indexed' => true + + ] + ); +} \ No newline at end of file diff --git a/search/engine/solrrag/classes/engine.php b/search/engine/solrrag/classes/engine.php new file mode 100644 index 000000000000..e0f7bc1a87fa --- /dev/null +++ b/search/engine/solrrag/classes/engine.php @@ -0,0 +1,127 @@ +config); + } + + public function is_server_ready() { + + $configured = $this->is_server_configured(); + if ($configured !== true) { + return $configured; + } + + // As part of the above we have already checked that we can contact the server. For pages + // where performance is important, we skip doing a full schema check as well. + if ($this->should_skip_schema_check()) { + return true; + } + + // Update schema if required/possible. + $schemalatest = $this->check_latest_schema(); + if ($schemalatest !== true) { + return $schemalatest; + } + + // Check that the schema is already set up. + try { + $schema = new schema($this); + $schema->validate_setup(); + } catch (\moodle_exception $e) { + return $e->getMessage(); + } + + return true; + } + + /** + * @see \search_solr\engine + * @param $document + * @return void + * @throws \core_search\engine_exception + */ + protected function process_document_files($document) { + if (!$this->file_indexing_enabled()) { + return; + } + // AI Retrieval support. + // Ideally we'd be using a Moodle AI provider to tell us which LLM to use for generating embeddings, and + // then simply calling the API and get some results back...but we don't have that yet. + // So we'll fudge this for the moment and leverage an OpenAI Web Service API via a simple HTTP request. + + // Maximum rows to process at a time. + $rows = 500; + + // Get the attached files. + $files = $document->get_files(); + + // If this isn't a new document, we need to check the exiting indexed files. + if (!$document->get_is_new()) { + // We do this progressively, so we can handle lots of files cleanly. + list($numfound, $indexedfiles) = $this->get_indexed_files($document, 0, $rows); + $count = 0; + $idstodelete = array(); + + do { + // Go through each indexed file. We want to not index any stored and unchanged ones, delete any missing ones. + foreach ($indexedfiles as $indexedfile) { + $fileid = $indexedfile->solr_fileid; + + if (isset($files[$fileid])) { + // Check for changes that would mean we need to re-index the file. If so, just leave in $files. + // Filelib does not guarantee time modified is updated, so we will check important values. + if ($indexedfile->modified != $files[$fileid]->get_timemodified()) { + continue; + } + if (strcmp($indexedfile->title, $files[$fileid]->get_filename()) !== 0) { + continue; + } + if ($indexedfile->solr_filecontenthash != $files[$fileid]->get_contenthash()) { + continue; + } + if ($indexedfile->solr_fileindexstatus == document::INDEXED_FILE_FALSE && + $this->file_is_indexable($files[$fileid])) { + // This means that the last time we indexed this file, filtering blocked it. + // Current settings say it is indexable, so we will allow it to be indexed. + continue; + } + + // If the file is already indexed, we can just remove it from the files array and skip it. + unset($files[$fileid]); + } else { + // This means we have found a file that is no longer attached, so we need to delete from the index. + // We do it later, since this is progressive, and it could reorder results. + $idstodelete[] = $indexedfile->id; + } + } + $count += $rows; + + if ($count < $numfound) { + // If we haven't hit the total count yet, fetch the next batch. + list($numfound, $indexedfiles) = $this->get_indexed_files($document, $count, $rows); + } + + } while ($count < $numfound); + + // Delete files that are no longer attached. + foreach ($idstodelete as $id) { + // We directly delete the item using the client, as the engine delete_by_id won't work on file docs. + $this->get_search_client()->deleteById($id); + } + } + + // Now we can actually index all the remaining files. + foreach ($files as $file) { + $this->add_stored_file($document, $file); + } + } +} \ No newline at end of file diff --git a/search/engine/solrrag/classes/schema.php b/search/engine/solrrag/classes/schema.php new file mode 100644 index 000000000000..f79017846310 --- /dev/null +++ b/search/engine/solrrag/classes/schema.php @@ -0,0 +1,114 @@ +config = get_config('search_solrrag')) { + throw new \moodle_exception('missingconfig', 'search_solrrag'); + } + + if (empty($this->config->server_hostname) || empty($this->config->indexname)) { + throw new \moodle_exception('missingconfig', 'search_solrrag'); + } + + $this->engine = $engine ?? new engine(); + $this->curl = $this->engine->get_curl_object(); + + // HTTP headers. + $this->curl->setHeader('Content-type: application/json'); + } + + public function validate_setup() { + $fields = \search_solrrag\document::get_default_fields_definition(); + + // Field id is already there. + unset($fields['id']); + + $this->check_index(); + $this->validate_fields($fields, true); + } + + /** + * Checks if the schema existing fields are properly set, triggers an exception otherwise. + * + * @throws \moodle_exception + * @param array $fields + * @param bool $requireexisting Require the fields to exist, otherwise exception. + * @return void + */ + protected function validate_fields(&$fields, $requireexisting = false) { + global $CFG; + foreach ($fields as $fieldname => $data) { + $url = $this->engine->get_connection_url('/schema/fields/' . $fieldname); + $results = $this->curl->get($url); + + if ($this->curl->error) { + throw new \moodle_exception('errorcreatingschema', 'search_solrrag', '', $this->curl->error); + } + + if (!$results) { + throw new \moodle_exception('errorcreatingschema', 'search_solrrag', '', get_string('nodatafromserver', 'search_solrrag')); + } + $results = json_decode($results); + + if ($requireexisting && !empty($results->error) && $results->error->code === 404) { + $a = new \stdClass(); + $a->fieldname = $fieldname; + $a->setupurl = $CFG->wwwroot . '/search/engine/solrrag/setup_schema.php'; + throw new \moodle_exception('errorvalidatingschema', 'search_solrrag', '', $a); + } + + // The field should not exist so we only accept 404 errors. + if (empty($results->error) || (!empty($results->error) && $results->error->code !== 404)) { + if (!empty($results->error)) { + throw new \moodle_exception('errorcreatingschema', 'search_solrrag', '', $results->error->msg); + } else { + // All these field attributes are set when fields are added through this script and should + // be returned and match the defined field's values. + + $expectedsolrfield = $this->doc_field_to_solr_field($data['type']); + if (empty($results->field) || !isset($results->field->type) || + !isset($results->field->multiValued) || !isset($results->field->indexed) || + !isset($results->field->stored)) { + + throw new \moodle_exception('errorcreatingschema', 'search_solrrag', '', + get_string('schemafieldautocreated', 'search_solrrag', $fieldname)); + + } else if ($results->field->type !== $expectedsolrfield || + $results->field->multiValued !== false || + $results->field->indexed !== $data['indexed'] || + $results->field->stored !== $data['stored']) { + + throw new \moodle_exception('errorcreatingschema', 'search_solrrag', '', + get_string('schemafieldautocreated', 'search_solrrag', $fieldname)); + } else { + // The field already exists and it is properly defined, no need to create it. + unset($fields[$fieldname]); + } + } + } + } + } +// public function can_setup_server() { +//print_r($this->engine); +// $status = $this->engine->is_server_configured(); +// if ($status !== true) { +// return $status; +// } +// +// // At this stage we know that the server is properly configured with a valid host:port and indexname. +// // We're not too concerned about repeating the SolrClient::system() call (already called in +// // is_server_configured) because this is just a setup script. +// if ($this->engine->get_solr_major_version() < 5) { +// // Schema setup script only available for 5.0 onwards. +// return get_string('schemasetupfromsolr5', 'search_solr'); +// } +// +// return true; +// } +} \ No newline at end of file diff --git a/search/engine/solrrag/lang/en/search_solrrag.php b/search/engine/solrrag/lang/en/search_solrrag.php new file mode 100644 index 000000000000..3b2b11838878 --- /dev/null +++ b/search/engine/solrrag/lang/en/search_solrrag.php @@ -0,0 +1,54 @@ +fieldname} does not exist. Please follow this link to set up the required fields.'; +$string['errorsolr'] = 'The Solr search engine reported an error: {$a}'; +$string['extensionerror'] = 'The Apache Solr PHP extension is not installed. Please check the documentation.'; +$string['fileindexing'] = 'Enable file indexing'; +$string['fileindexing_help'] = 'If your Solr install supports it, this feature allows Moodle to send files to be indexed.
+You will need to reindex all site contents after enabling this option for all files to be added.'; +$string['fileindexsettings'] = 'File indexing settings'; +$string['maxindexfilekb'] = 'Maximum file size to index (kB)'; +$string['maxindexfilekb_help'] = 'Files larger than this number of kilobytes will not be included in search indexing. If set to zero, files of any size will be indexed.'; +$string['minimumsolr4'] = 'Solr 4.0 is the minimum version required for Moodle'; +$string['missingconfig'] = 'Your Apache Solr server is not yet configured in Moodle.'; +$string['multivaluedfield'] = 'Field "{$a}" returned an array instead of a scalar. Please delete the current index, create a new one and run setup_schema.php before indexing data in Solr.'; +$string['nodatafromserver'] = 'No data from server'; +$string['privacy:metadata'] = 'This plugin sends data externally to a linked Solr search engine. It does not store data locally.'; +$string['privacy:metadata:data'] = 'Personal data passed through from the search subsystem.'; +$string['schemafieldautocreated'] = 'Field "{$a}" already exists in Solr schema. You probably forgot to run this script before indexing data and fields were autocreated by Solr. Please delete the current index, create a new one and run setup_schema.php again before indexing data in Solr.'; +$string['schemasetupfromsolr5'] = 'Your Solr server version is lower than 5.0. This script can only set your schema if your Solr version is 5.0 or higher. You need to manually set the fields in your schema according to \\search_solr\\document::get_default_fields_definition().'; +$string['searchinfo'] = 'Search queries'; +$string['searchinfo_help'] = 'The field to be searched may be specified by prefixing the search query with \'title:\', \'content:\', \'name:\', or \'intro:\'. For example, searching for \'title:news\' would return results with the word \'news\' in the title. + +Boolean operators (\'AND\', \'OR\', \'NOT\') may be used to combine or exclude keywords. + +Wildcard characters (\'*\' or \'?\' ) may be used to represent characters in the search query.'; +$string['setupok'] = 'The schema is ready to be used.'; +$string['solrauthpassword'] = 'HTTP authentication password'; +$string['solrauthuser'] = 'HTTP authentication username'; +$string['solrindexname'] = 'Index name'; +$string['solrhttpconnectionport'] = 'Port'; +$string['solrhttpconnectiontimeout'] = 'Timeout'; +$string['solrhttpconnectiontimeout_desc'] = 'The HTTP connection timeout is the maximum time in seconds allowed for the HTTP data transfer operation.'; +$string['solrinfo'] = 'Solr'; +$string['solrnotselected'] = 'Solr engine is not the configured search engine'; +$string['solrserverhostname'] = 'Host name'; +$string['solrserverhostname_desc'] = 'Domain name of the Solr server.'; +$string['solrsecuremode'] = 'Secure mode'; +$string['solrsetting'] = 'Solr settings'; +$string['solrsslcainfo'] = 'SSL CA certificates name'; +$string['solrsslcainfo_desc'] = 'File name holding one or more CA certificates to verify peer with'; +$string['solrsslcapath'] = 'SSL CA certificates path'; +$string['solrsslcapath_desc'] = 'Directory path holding multiple CA certificates to verify peer with'; +$string['solrsslcert'] = 'SSL certificate'; +$string['solrsslcert_desc'] = 'File name to a PEM-formatted private certificate'; +$string['solrsslkey'] = 'SSL key'; +$string['solrsslkey_desc'] = 'File name to a PEM-formatted private key'; +$string['solrsslkeypassword'] = 'SSL key password'; +$string['solrsslkeypassword_desc'] = 'Password for PEM-formatted private key file'; diff --git a/search/engine/solrrag/privacy/provider.php b/search/engine/solrrag/privacy/provider.php new file mode 100644 index 000000000000..7475b9e4d081 --- /dev/null +++ b/search/engine/solrrag/privacy/provider.php @@ -0,0 +1,108 @@ +. + +/** + * Privacy class for requesting user data. + * + * @package search_solr + * @copyright 2018 David Monllao + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +namespace search_solr\privacy; + +defined('MOODLE_INTERNAL') || die(); + +use core_privacy\local\metadata\collection; +use core_privacy\local\request\contextlist; +use core_privacy\local\request\approved_contextlist; +use core_privacy\local\request\approved_userlist; +use core_privacy\local\request\userlist; + +/** + * Provider for the search_solr plugin. + * + * @copyright 2018 David Monllao + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class provider implements + // This search engine plugin does not store any data itself. + // It has no database tables, and it purely acts as a conduit, sending data externally. + // This plugin is capable of determining which users have data within it. + \core_privacy\local\metadata\provider, + \core_privacy\local\request\core_userlist_provider, + \core_privacy\local\request\plugin\provider { + + /** + * Returns meta data about this system. + * + * @param collection $collection The initialised collection to add items to. + * @return collection A listing of user data stored through this system. + */ + public static function get_metadata(collection $collection) : collection { + return $collection->add_external_location_link('solr', ['data' => 'privacy:metadata:data'], + 'privacy:metadata'); + } + + /** + * Get the list of contexts that contain user information for the specified user. + * + * @param int $userid The user to search. + * @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin. + */ + public static function get_contexts_for_userid(int $userid) : contextlist { + return new contextlist(); + } + + /** + * Get the list of users who have data within a context. + * + * @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination. + */ + public static function get_users_in_context(userlist $userlist) { + } + + /** + * Export all user data for the specified user, in the specified contexts. + * + * @param approved_contextlist $contextlist The approved contexts to export information for. + */ + public static function export_user_data(approved_contextlist $contextlist) { + } + + /** + * Delete all data for all users in the specified context. + * + * @param context $context The specific context to delete data for. + */ + public static function delete_data_for_all_users_in_context(\context $context) { + } + + /** + * Delete all user data for the specified user, in the specified contexts. + * + * @param approved_contextlist $contextlist The approved contexts and user information to delete information for. + */ + public static function delete_data_for_user(approved_contextlist $contextlist) { + } + + /** + * Delete multiple users within a single context. + * + * @param approved_userlist $userlist The approved context and user information to delete information for. + */ + public static function delete_data_for_users(approved_userlist $userlist) { + } +} diff --git a/search/engine/solrrag/settings.php b/search/engine/solrrag/settings.php new file mode 100644 index 000000000000..85b427e5c75b --- /dev/null +++ b/search/engine/solrrag/settings.php @@ -0,0 +1,98 @@ +. + +/** + * Solr search engine settings. + * + * @package search_solrrag + * @copyright 2015 Daniel Neis + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +if ($ADMIN->fulltree) { + + if (!during_initial_install()) { + if (!function_exists('solr_get_version')) { + $settings->add(new admin_setting_heading('search_solrrag_settings', '', get_string('extensionerror', 'search_solrrag'))); + + } else { + $settings->add(new admin_setting_heading('search_solrrag_connection', + new lang_string('connectionsettings', 'search_solrrag'), '')); + $settings->add(new admin_setting_configtext('search_solrrag/server_hostname', new lang_string('solrserverhostname', 'search_solrrag'), new lang_string('solrserverhostname_desc', 'search_solrrag'), '127.0.0.1', PARAM_HOST)); + $settings->add(new admin_setting_configtext('search_solrrag/indexname', new lang_string('solrindexname', 'search_solrrag'), '', '', PARAM_ALPHANUMEXT)); + $settings->add(new admin_setting_configcheckbox('search_solrrag/secure', new lang_string('solrsecuremode', 'search_solrrag'), '', 0, 1, 0)); + + $secure = get_config('search_solrrag', 'secure'); + $defaultport = !empty($secure) ? 8443 : 8983; + $settings->add(new admin_setting_configtext('search_solrrag/server_port', new lang_string('solrhttpconnectionport', 'search_solrrag'), '', $defaultport, PARAM_INT)); + $settings->add(new admin_setting_configtext('search_solrrag/server_username', new lang_string('solrauthuser', 'search_solrrag'), '', '', PARAM_RAW)); + $settings->add(new admin_setting_configpasswordunmask('search_solrrag/server_password', new lang_string('solrauthpassword', 'search_solrrag'), '', '')); + $settings->add(new admin_setting_configtext('search_solrrag/server_timeout', new lang_string('solrhttpconnectiontimeout', 'search_solrrag'), new lang_string('solrhttpconnectiontimeout_desc', 'search_solrrag'), 30, PARAM_INT)); + $settings->add(new admin_setting_configtext('search_solrrag/ssl_cert', new lang_string('solrsslcert', 'search_solrrag'), new lang_string('solrsslcert_desc', 'search_solrrag'), '', PARAM_RAW)); + $settings->add(new admin_setting_configtext('search_solrrag/ssl_key', new lang_string('solrsslkey', 'search_solrrag'), new lang_string('solrsslkey_desc', 'search_solrrag'), '', PARAM_RAW)); + $settings->add(new admin_setting_configpasswordunmask('search_solrrag/ssl_keypassword', new lang_string('solrsslkeypassword', 'search_solrrag'), new lang_string('solrsslkeypassword_desc', 'search_solrrag'), '')); + $settings->add(new admin_setting_configtext('search_solrrag/ssl_cainfo', new lang_string('solrsslcainfo', 'search_solrrag'), new lang_string('solrsslcainfo_desc', 'search_solrrag'), '', PARAM_RAW)); + $settings->add(new admin_setting_configtext('search_solrrag/ssl_capath', new lang_string('solrsslcapath', 'search_solrrag'), new lang_string('solrsslcapath_desc', 'search_solrrag'), '', PARAM_RAW)); + + $settings->add(new admin_setting_heading('search_solrrag_fileindexing', + new lang_string('fileindexsettings', 'search_solrrag'), '')); + $settings->add(new admin_setting_configcheckbox('search_solrrag/fileindexing', + new lang_string('fileindexing', 'search_solrrag'), + new lang_string('fileindexing_help', 'search_solrrag'), 1)); + $settings->add(new admin_setting_configtext('search_solrrag/maxindexfilekb', + new lang_string('maxindexfilekb', 'search_solrrag'), + new lang_string('maxindexfilekb_help', 'search_solrrag'), '2097152', PARAM_INT)); + + // Alternate connection. + $settings->add(new admin_setting_heading('search_solrrag_alternatesettings', + new lang_string('searchalternatesettings', 'admin'), + new lang_string('searchalternatesettings_desc', 'admin'))); + $settings->add(new admin_setting_configtext('search_solrrag/alternateserver_hostname', + new lang_string('solrserverhostname', 'search_solrrag'), + new lang_string('solrserverhostname_desc', 'search_solrrag'), '127.0.0.1', PARAM_HOST)); + $settings->add(new admin_setting_configtext('search_solrrag/alternateindexname', + new lang_string('solrindexname', 'search_solrrag'), '', '', PARAM_ALPHANUMEXT)); + $settings->add(new admin_setting_configcheckbox('search_solrrag/alternatesecure', + new lang_string('solrsecuremode', 'search_solrrag'), '', 0, 1, 0)); + + $secure = get_config('search_solrrag', 'alternatesecure'); + $defaultport = !empty($secure) ? 8443 : 8983; + $settings->add(new admin_setting_configtext('search_solrrag/alternateserver_port', + new lang_string('solrhttpconnectionport', 'search_solrrag'), '', $defaultport, PARAM_INT)); + $settings->add(new admin_setting_configtext('search_solrrag/alternateserver_username', + new lang_string('solrauthuser', 'search_solrrag'), '', '', PARAM_RAW)); + $settings->add(new admin_setting_configpasswordunmask('search_solrrag/alternateserver_password', + new lang_string('solrauthpassword', 'search_solrrag'), '', '')); + $settings->add(new admin_setting_configtext('search_solrrag/alternatessl_cert', + new lang_string('solrsslcert', 'search_solrrag'), + new lang_string('solrsslcert_desc', 'search_solrrag'), '', PARAM_RAW)); + $settings->add(new admin_setting_configtext('search_solrrag/alternatessl_key', + new lang_string('solrsslkey', 'search_solrrag'), + new lang_string('solrsslkey_desc', 'search_solrrag'), '', PARAM_RAW)); + $settings->add(new admin_setting_configpasswordunmask('search_solrrag/alternatessl_keypassword', + new lang_string('solrsslkeypassword', 'search_solrrag'), + new lang_string('solrsslkeypassword_desc', 'search_solrrag'), '')); + $settings->add(new admin_setting_configtext('search_solrrag/alternatessl_cainfo', + new lang_string('solrsslcainfo', 'search_solrrag'), + new lang_string('solrsslcainfo_desc', 'search_solrrag'), '', PARAM_RAW)); + $settings->add(new admin_setting_configtext('search_solrrag/alternatessl_capath', + new lang_string('solrsslcapath', 'search_solrrag'), + new lang_string('solrsslcapath_desc', 'search_solrrag'), '', PARAM_RAW)); + } + } +} diff --git a/search/engine/solrrag/setup_schema.php b/search/engine/solrrag/setup_schema.php new file mode 100644 index 000000000000..921f39c19a7e --- /dev/null +++ b/search/engine/solrrag/setup_schema.php @@ -0,0 +1,57 @@ +. + +/** + * Adds moodle fields to solr schema. + * + * Schema REST API write actions are only available from Solr 4.4 onwards. + * + * The schema should be managed and mutable to allow this script + * to add new fields to the schema. + * + * @link https://cwiki.apache.org/confluence/display/solr/Managed+Schema+Definition+in+SolrConfig + * @package search_solr + * @copyright 2015 David Monllao {@link http://www.davidmonllao.com} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once(__DIR__ . '/../../../config.php'); +require_once($CFG->libdir.'/adminlib.php'); + +require_login(null, false); +require_capability('moodle/site:config', context_system::instance()); + +$returnurl = new moodle_url('/admin/settings.php', array('section' => 'manageglobalsearch')); + +$schema = new \search_solrrag\schema(); + +$status = $schema->can_setup_server(); +if ($status !== true) { + + $PAGE->set_context(context_system::instance()); + $PAGE->set_url(new moodle_url('/search/engine/solrrag/setup_schema.php')); + + echo $OUTPUT->header(); + echo $OUTPUT->notification($status, \core\output\notification::NOTIFY_ERROR); + echo $OUTPUT->box($OUTPUT->action_link($returnurl, get_string('continue')), 'generalbox centerpara'); + echo $OUTPUT->footer(); + + exit(1); +} + +$schema->setup(); + +redirect($returnurl, get_string('setupok', 'search_solr'), 4); diff --git a/search/engine/solrrag/version.php b/search/engine/solrrag/version.php new file mode 100644 index 000000000000..f6a3a028a05f --- /dev/null +++ b/search/engine/solrrag/version.php @@ -0,0 +1,29 @@ +. + +/** + * Version info. + * + * @package search_solr + * @copyright 2015 Daniel Neis Araujo + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$plugin->version = 2024030400; +$plugin->requires = 2023100400; +$plugin->component = 'search_solrrag'; From b24cbd1f89e9de0e557e6c6efe73afb7b6475271 Mon Sep 17 00:00:00 2001 From: Michael hughes Date: Wed, 6 Mar 2024 12:49:08 +0000 Subject: [PATCH 02/21] solrrag stashing code --- search/engine/solr/classes/document.php | 3 +- .../engine/solrrag/classes/ai/aiprovider.php | 51 +++- search/engine/solrrag/classes/ai/api.php | 3 + search/engine/solrrag/classes/document.php | 36 +++ search/engine/solrrag/classes/engine.php | 218 ++++++++++++------ 5 files changed, 237 insertions(+), 74 deletions(-) diff --git a/search/engine/solr/classes/document.php b/search/engine/solr/classes/document.php index 6bf427785488..56a4faff9eb3 100644 --- a/search/engine/solr/classes/document.php +++ b/search/engine/solr/classes/document.php @@ -187,6 +187,7 @@ protected function apply_defaults() { * @return array */ public function export_file_for_engine($file) { + debugging('Using solrrag\document'); $data = $this->export_for_engine(); // Content is index in the main document. @@ -202,7 +203,7 @@ public function export_file_for_engine($file) { $data['solr_fileindexstatus'] = self::INDEXED_FILE_TRUE; $data['title'] = $file->get_filename(); $data['modified'] = self::format_time_for_engine($file->get_timemodified()); - + $data['solr_vector'] = 'Hello'; return $data; } } diff --git a/search/engine/solrrag/classes/ai/aiprovider.php b/search/engine/solrrag/classes/ai/aiprovider.php index 9a216d83c823..871dda8c6891 100644 --- a/search/engine/solrrag/classes/ai/aiprovider.php +++ b/search/engine/solrrag/classes/ai/aiprovider.php @@ -2,19 +2,67 @@ // We're mocking a core Moodle "AI" Subsystem a la Oauth 2 namespace core\ai; - +use \core\persistent; class AIProvider extends persistent { // Ultimately this would extend a persistent. + public function __construct($id = 0, stdClass $record = null) { + if ($id > 0) { + $this->raw_set('id', $id); + $this->raw_set('name', "Fake AI Provider"); + $this->raw_set('allowembeddings', true); + $this->raw_set('allowquery', true); + } + } + protected static function define_properties() { return [ 'name' => [ 'type' => PARAM_TEXT + ], + 'allowembeddings' => [ + 'type' => PARAM_BOOL + ], + 'allowquery' => [ + 'type' => PARAM_BOOL ] ]; } + public function use_for_embeddings(): bool { + return $this->get('allowembeddings'); + } + + public function use_for_query():bool { + return $this->get('allowquery'); + } + public function embed_documents(array $documents) { + // Go send the documents off to a back end and then return array of each document's vectors. + print_r($documents); + return [ + [0.0053587136790156364, + -0.0004999046213924885, + 0.038883671164512634, + -0.003001077566295862, + -0.00900818221271038] + ]; + } + + /** + * @param $document + * @return array + */ + public function embed_query($document): array { + print_r($document); + // Send document to back end and return the vector + return [0.0053587136790156364, + -0.0004999046213924885, + 0.038883671164512634, + -0.003001077566295862, + -0.00900818221271038 + ]; + } /** * We're overriding this whilst we don't have a real DB table. * @param $filters @@ -27,6 +75,7 @@ protected static function define_properties() public static function get_records($filters = array(), $sort = '', $order = 'ASC', $skip = 0, $limit = 0) { $records = []; $fake = new static(0, (object) [ + 'id' => 1, 'name' => "Fake AI Provider" ]); array_push($records, $fake); diff --git a/search/engine/solrrag/classes/ai/api.php b/search/engine/solrrag/classes/ai/api.php index c68e541757d5..b3317f6980e5 100644 --- a/search/engine/solrrag/classes/ai/api.php +++ b/search/engine/solrrag/classes/ai/api.php @@ -8,4 +8,7 @@ class api { public static function get_all_providers() { return array_values(AIProvider::get_records()); } + public static function get_provider(int $id): AIProvider { + return new AIProvider($id); + } } \ No newline at end of file diff --git a/search/engine/solrrag/classes/document.php b/search/engine/solrrag/classes/document.php index 9bc250de6ba5..5c3ada09a5ec 100644 --- a/search/engine/solrrag/classes/document.php +++ b/search/engine/solrrag/classes/document.php @@ -38,4 +38,40 @@ class document extends \search_solr\document { ] ); + + /** + * Export the data for the given file in relation to this document. + * + * @param \stored_file $file The stored file we are talking about. + * @return array + */ + public function export_file_for_engine($file) { + $data = $this->export_for_engine(); + + // Content is index in the main document. + unset($data['content']); + unset($data['description1']); + unset($data['description2']); + + // Going to append the fileid to give it a unique id. + $data['id'] = $data['id'].'-solrfile'.$file->get_id(); + $data['type'] = \core_search\manager::TYPE_FILE; + $data['solr_fileid'] = $file->get_id(); + $data['solr_filecontenthash'] = $file->get_contenthash(); + $data['solr_fileindexstatus'] = self::INDEXED_FILE_TRUE; + $data['solr_vector'] = null; + $data['title'] = $file->get_filename(); + $data['modified'] = self::format_time_for_engine($file->get_timemodified()); + + return $data; + } + + /** + * Returns the "content" of the documents for embedding. + * This may use some sort of external system. + * @return void + */ + public function fetch_document_contents() { + + } } \ No newline at end of file diff --git a/search/engine/solrrag/classes/engine.php b/search/engine/solrrag/classes/engine.php index e0f7bc1a87fa..b0cd9aaa2b65 100644 --- a/search/engine/solrrag/classes/engine.php +++ b/search/engine/solrrag/classes/engine.php @@ -4,16 +4,34 @@ use search_solrrag\document; use search_solrrag\schema; - +// Fudge autoloading! +require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/api.php"); +require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/aiprovider.php"); +use \core\ai\AIProvider; class engine extends \search_solr\engine { + + /** + * @var AIProvider AI rovider object to use to generate embeddings. + */ + protected ?AIProvider $embeddingprovider = null; + public function __construct(bool $alternateconfiguration = false) { -// debugging("Solrrag construct"); parent::__construct($alternateconfiguration); -// var_dump($this->config); + // AI Retrieval support. + // Set up AI provider if it's available. + // Ideally we'd be using a Moodle AI provider to tell us which LLM to use for generating embeddings, and + // then simply calling the API and get some results back...but we don't have that yet. + // So we'll fudge this for the moment and leverage an OpenAI Web Service API via a simple HTTP request. + $aiproviderid = 1; + $aiprovider = \core\ai\api::get_provider($aiproviderid); + if ($aiprovider->use_for_embeddings()) { + $this->embeddingprovider = $aiprovider; + } } - public function is_server_ready() { + public function is_server_ready() + { $configured = $this->is_server_configured(); if ($configured !== true) { @@ -44,84 +62,140 @@ public function is_server_ready() { } /** - * @see \search_solr\engine - * @param $document + * Adds a file to the search engine. + * + * Notes about Solr and Tika indexing. We do not send the mime type, only the filename. + * Tika has much better content type detection than Moodle, and we will have many more doc failures + * if we try to send mime types. + * + * @param \search_solr\document $document + * @param \stored_file $storedfile * @return void - * @throws \core_search\engine_exception */ - protected function process_document_files($document) { - if (!$this->file_indexing_enabled()) { + protected function add_stored_file($document, $storedfile) + { + $filedoc = $document->export_file_for_engine($storedfile); + /** + * Should we event attempt to get vectors. + */ + if (!is_null($this->embeddingprovider)) { + // garnish $filedoc with the embedding vector. It would be nice if this could be done + // via the export_file_for_engine() call above, that has no awareness of the engine. + $embeddings = $this->embeddingprovider->embed_documents([$filedoc]); + $filedoc['solr_vector'] = "[". implode(",", $embeddings[0]) ."]"; + print_r($filedoc); + } else { + // potentially warn that selected provider can't be used for + // generating embeddings for RAG. + } + + + // Used the underlying implementation + + if (!$this->file_is_indexable($storedfile)) { + // For files that we don't consider indexable, we will still place a reference in the search engine. + $filedoc['solr_fileindexstatus'] = document::INDEXED_FILE_FALSE; + $this->add_solr_document($filedoc); return; } - // AI Retrieval support. - // Ideally we'd be using a Moodle AI provider to tell us which LLM to use for generating embeddings, and - // then simply calling the API and get some results back...but we don't have that yet. - // So we'll fudge this for the moment and leverage an OpenAI Web Service API via a simple HTTP request. - // Maximum rows to process at a time. - $rows = 500; - - // Get the attached files. - $files = $document->get_files(); - - // If this isn't a new document, we need to check the exiting indexed files. - if (!$document->get_is_new()) { - // We do this progressively, so we can handle lots of files cleanly. - list($numfound, $indexedfiles) = $this->get_indexed_files($document, 0, $rows); - $count = 0; - $idstodelete = array(); - - do { - // Go through each indexed file. We want to not index any stored and unchanged ones, delete any missing ones. - foreach ($indexedfiles as $indexedfile) { - $fileid = $indexedfile->solr_fileid; - - if (isset($files[$fileid])) { - // Check for changes that would mean we need to re-index the file. If so, just leave in $files. - // Filelib does not guarantee time modified is updated, so we will check important values. - if ($indexedfile->modified != $files[$fileid]->get_timemodified()) { - continue; - } - if (strcmp($indexedfile->title, $files[$fileid]->get_filename()) !== 0) { - continue; - } - if ($indexedfile->solr_filecontenthash != $files[$fileid]->get_contenthash()) { - continue; - } - if ($indexedfile->solr_fileindexstatus == document::INDEXED_FILE_FALSE && - $this->file_is_indexable($files[$fileid])) { - // This means that the last time we indexed this file, filtering blocked it. - // Current settings say it is indexable, so we will allow it to be indexed. - continue; - } - - // If the file is already indexed, we can just remove it from the files array and skip it. - unset($files[$fileid]); - } else { - // This means we have found a file that is no longer attached, so we need to delete from the index. - // We do it later, since this is progressive, and it could reorder results. - $idstodelete[] = $indexedfile->id; - } - } - $count += $rows; + $curl = $this->get_curl_object(); - if ($count < $numfound) { - // If we haven't hit the total count yet, fetch the next batch. - list($numfound, $indexedfiles) = $this->get_indexed_files($document, $count, $rows); - } + $url = $this->get_connection_url('/update/extract'); - } while ($count < $numfound); + // Return results as XML. + $url->param('wt', 'xml'); - // Delete files that are no longer attached. - foreach ($idstodelete as $id) { - // We directly delete the item using the client, as the engine delete_by_id won't work on file docs. - $this->get_search_client()->deleteById($id); - } + // This will prevent solr from automatically making fields for every tika output. + $url->param('uprefix', 'ignored_'); + + // Control how content is captured. This will keep our file content clean of non-important metadata. + $url->param('captureAttr', 'true'); + // Move the content to a field for indexing. + $url->param('fmap.content', 'solr_filecontent'); + + // These are common fields that matches the standard *_point dynamic field and causes an error. + $url->param('fmap.media_white_point', 'ignored_mwp'); + $url->param('fmap.media_black_point', 'ignored_mbp'); + + // Copy each key to the url with literal. + // We place in a temp name then copy back to the true field, which prevents errors or Tika overwriting common field names. + foreach ($filedoc as $key => $value) { + // This will take any fields from tika that match our schema and discard them, so they don't overwrite ours. + $url->param('fmap.' . $key, 'ignored_' . $key); + // Place data in a tmp field. + $url->param('literal.mdltmp_' . $key, $value); + // Then move to the final field. + $url->param('fmap.mdltmp_' . $key, $key); } - // Now we can actually index all the remaining files. - foreach ($files as $file) { - $this->add_stored_file($document, $file); + // This sets the true filename for Tika. + $url->param('resource.name', $storedfile->get_filename()); + + // A giant block of code that is really just error checking around the curl request. + try { + $requesturl = $url->out(false); + // We have to post the file directly in binary data (not using multipart) to avoid + // Solr bug SOLR-15039 which can cause incorrect data when you use multipart upload. + // Note this loads the whole file into memory; see limit in file_is_indexable(). + $result = $curl->post($url->out(false), $storedfile->get_content()); + + $code = $curl->get_errno(); + $info = $curl->get_info(); + + // Now error handling. It is just informational, since we aren't tracking per file/doc results. + if ($code != 0) { + // This means an internal cURL error occurred error is in result. + $message = 'Curl error ' . $code . ' while indexing file with document id ' . $filedoc['id'] . ': ' . $result . '.'; + debugging($message, DEBUG_DEVELOPER); + } else if (isset($info['http_code']) && ($info['http_code'] !== 200)) { + // Unexpected HTTP response code. + $message = 'Error while indexing file with document id ' . $filedoc['id']; + // Try to get error message out of msg or title if it exists. + if (preg_match('|]*name="msg"[^>]*>(.*?)|i', $result, $matches)) { + $message .= ': ' . $matches[1]; + } else if (preg_match('|]*>([^>]*)|i', $result, $matches)) { + $message .= ': ' . $matches[1]; + } + // This is a common error, happening whenever a file fails to index for any reason, so we will make it quieter. + if (CLI_SCRIPT && !PHPUNIT_TEST) { + mtrace($message); + if (debugging()) { + mtrace($requesturl); + } + // Suspiciion that this fails due to the file contents being PDFs. + } + } else { + // Check for the expected status field. + if (preg_match('|]*name="status"[^>]*>(\d*)|i', $result, $matches)) { + // Now check for the expected status of 0, if not, error. + if ((int)$matches[1] !== 0) { + $message = 'Unexpected Solr status code ' . (int)$matches[1]; + $message .= ' while indexing file with document id ' . $filedoc['id'] . '.'; + debugging($message, DEBUG_DEVELOPER); + } else { + // The document was successfully indexed. + return; + } + } else { + // We received an unprocessable response. + $message = 'Unexpected Solr response while indexing file with document id ' . $filedoc['id'] . ': '; + $message .= strtok($result, "\n"); + debugging($message, DEBUG_DEVELOPER); + } + } + } catch (\Exception $e) { + // There was an error, but we are not tracking per-file success, so we just continue on. + debugging('Unknown exception while indexing file "' . $storedfile->get_filename() . '".', DEBUG_DEVELOPER); } + + // If we get here, the document was not indexed due to an error. So we will index just the base info without the file. + $filedoc['solr_fileindexstatus'] = document::INDEXED_FILE_ERROR; + $this->add_solr_document($filedoc); + + + // It would have been nice to use the underlying solr code, but its too tightly integrated + // with talking to solr. + //return parent::add_stored_file($document, $storedfile); } } \ No newline at end of file From 273181044547aabc0b7a05402e985f522095e6ec Mon Sep 17 00:00:00 2001 From: Michael hughes Date: Wed, 6 Mar 2024 21:52:31 +0000 Subject: [PATCH 03/21] solrrag populating solr with a vector 1536 or 3072 in length --- search/cli/result.txt | 2617 +++++++++++++++++ search/cli/result2.txt | 64 + .../engine/solrrag/classes/ai/aiprovider.php | 31 +- search/engine/solrrag/classes/document.php | 12 +- search/engine/solrrag/classes/engine.php | 74 +- 5 files changed, 2771 insertions(+), 27 deletions(-) create mode 100644 search/cli/result.txt create mode 100644 search/cli/result2.txt diff --git a/search/cli/result.txt b/search/cli/result.txt new file mode 100644 index 000000000000..0fb10804a7f5 --- /dev/null +++ b/search/cli/result.txt @@ -0,0 +1,2617 @@ +Running full reindex of site +============================ +Processing area: Text block content + No new documents to index. +Processing area: Courses + Processed 5 records containing 5 documents (1 batch), in 0.4 seconds. +Processing area: Course custom fields + No new documents to index. +Processing area: Course sections + No new documents to index. +Processing area: Messages - received + No new documents to index. +Processing area: Messages - sent + No new documents to index. +Processing area: Course Teacher + No new documents to index. +Processing area: Users + Processed 103 records containing 103 documents (2 batches), in 0.5 seconds. +Processing area: Assignment - activity information + Processed 22 records containing 22 documents (1 batch), in 0.2 seconds. +Processing area: Attendance - activity information + Processed 1 records containing 1 documents, in 0.1 seconds. +Processing area: BigBlueButton - activity information + No new documents to index. +Processing area: BigBlueButton - tags information + No new documents to index. +Processing area: Book - resource information + No new documents to index. +Processing area: Book - chapters + No new documents to index. +Processing area: Chat - activity information + No new documents to index. +Processing area: Choice - activity information + No new documents to index. +Processing area: Database - activity information + No new documents to index. +Processing area: Database - entries + No new documents to index. +Processing area: Feedback - activity information + No new documents to index. +Processing area: Folder +Array +( + [0] => Array + ( + [areaid] => mod_folder-activity + [id] => mod_folder-activity-1-solrfile287 + [itemid] => 1 + [title] => Assessment_and_Feedback_Policy.pdf + [contextid] => 268 + [courseid] => 4 + [owneruserid] => 0 + [modified] => 2024-03-04T17:17:08Z + [type] => 2 + [solr_filegroupingid] => mod_folder-activity-1 + [solr_fileid] => 287 + [solr_filecontenthash] => 6de274633203c03b996b248e841f2a1f17cdb537 + [solr_fileindexstatus] => 1 + [solr_vector] => + ) + +) +++ http://172.22.0.6:8983/solr/moodle/update/extract?wt=xml&uprefix=ignored_&captureAttr=true&fmap.content=solr_filecontent&fmap.media_white_point=ignored_mwp&fmap.media_black_point=ignored_mbp&fmap.areaid=ignored_areaid&literal.mdltmp_areaid=mod_folder-activity&fmap.mdltmp_areaid=areaid&fmap.id=ignored_id&literal.mdltmp_id=mod_folder-activity-1-solrfile287&fmap.mdltmp_id=id&fmap.itemid=ignored_itemid&literal.mdltmp_itemid=1&fmap.mdltmp_itemid=itemid&fmap.title=ignored_title&literal.mdltmp_title=Assessment_and_Feedback_Policy.pdf&fmap.mdltmp_title=title&fmap.contextid=ignored_contextid&literal.mdltmp_contextid=268&fmap.mdltmp_contextid=contextid&fmap.courseid=ignored_courseid&literal.mdltmp_courseid=4&fmap.mdltmp_courseid=courseid&fmap.owneruserid=ignored_owneruserid&literal.mdltmp_owneruserid=0&fmap.mdltmp_owneruserid=owneruserid&fmap.modified=ignored_modified&literal.mdltmp_modified=2024-03-04T17%3A17%3A08Z&fmap.mdltmp_modified=modified&fmap.type=ignored_type&literal.mdltmp_type=2&fmap.mdltmp_type=type&fmap.solr_filegroupingid=ignored_solr_filegroupingid&literal.mdltmp_solr_filegroupingid=mod_folder-activity-1&fmap.mdltmp_solr_filegroupingid=solr_filegroupingid&fmap.solr_fileid=ignored_solr_fileid&literal.mdltmp_solr_fileid=287&fmap.mdltmp_solr_fileid=solr_fileid&fmap.solr_filecontenthash=ignored_solr_filecontenthash&literal.mdltmp_solr_filecontenthash=6de274633203c03b996b248e841f2a1f17cdb537&fmap.mdltmp_solr_filecontenthash=solr_filecontenthash&fmap.solr_fileindexstatus=ignored_solr_fileindexstatus&literal.mdltmp_solr_fileindexstatus=1&fmap.mdltmp_solr_fileindexstatus=solr_fileindexstatus&fmap.solr_vector=ignored_solr_vector&literal.mdltmp_solr_vector&fmap.mdltmp_solr_vector=solr_vector&resource.name=Assessment_and_Feedback_Policy.pdf&extractOnly=true ++ +* line 142 of /search/engine/solrrag/classes/engine.php: call to debugging() +* line 970 of /search/engine/solr/classes/engine.php: call to search_solrrag\engine->add_stored_file() +* line 776 of /search/engine/solr/classes/engine.php: call to search_solr\engine->process_document_files() +* line 318 of /search/classes/engine.php: call to search_solr\engine->add_document_batch() +* line 1283 of /search/classes/manager.php: call to core_search\engine->add_documents() +* line 108 of /search/cli/indexer.php: call to core_search\manager->index() +++ Got SOLR update/extract response ++ +* line 184 of /search/engine/solrrag/classes/engine.php: call to debugging() +* line 970 of /search/engine/solr/classes/engine.php: call to search_solrrag\engine->add_stored_file() +* line 776 of /search/engine/solr/classes/engine.php: call to search_solr\engine->process_document_files() +* line 318 of /search/classes/engine.php: call to search_solr\engine->add_document_batch() +* line 1283 of /search/classes/manager.php: call to core_search\engine->add_documents() +* line 108 of /search/cli/indexer.php: call to core_search\manager->index() + + + + + 0 + 64 + +<?xml version="1.0" encoding="UTF-8"?> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta name="date" +content="2019-05-29T13:52:29Z"/> +<meta name="pdf:PDFVersion" +content="1.5"/> +<meta name="xmp:CreatorTool" + content="Acrobat PDFMaker 15 for Word"/> +<meta +name="pdf:docinfo:title" + content="Assessment and Feedback Policy"/> +<meta +name="Company" content="University of Strathclyde"/> +<meta +name="stream_content_type" content="application/json"/> +<meta +name="pdf:hasXFA" content="false"/> +<meta +name="access_permission:can_print_degraded" content="true"/> +<meta +name="subject" content="Version 1.2"/> +<meta name="language" +content="EN-GB"/> +<meta name="dc:format" + content="application/pdf; version=1.5"/> +<meta +name="pdf:docinfo:creator_tool" + content="Acrobat PDFMaker 15 for Word"/> +<meta +name="access_permission:fill_in_form" content="true"/> +<meta +name="pdf:encrypted" content="false"/> +<meta name="dc:title" + content="Assessment and Feedback Policy"/> +<meta +name="xmp:CreateDate" content="2019-05-29T14:52:24Z"/> +<meta +name="modified" content="2019-05-29T13:52:29Z"/> +<meta +name="pdf:docinfo:custom:SourceModified" +content="D:20190528113809"/> +<meta name="cp:subject" +content="Version 1.2"/> +<meta name="pdf:docinfo:subject" + content="Version 1.2"/> +<meta +name="pdf:docinfo:custom:_TemplateID" content="TC017730709991"/> +<meta +name="pdf:hasMarkedContent" content="true"/> +<meta name="xmp:ModifyDate" + content="2019-05-29T14:52:29Z"/> +<meta +name="pdf:docinfo:creator" content="Ashley"/> +<meta name="meta:author" +content="Ashley"/> +<meta name="meta:creation-date" +content="2019-05-29T13:52:24Z"/> +<meta name="created" + content="2019-05-29T13:52:24Z"/> +<meta +name="access_permission:extract_for_accessibility" +content="true"/> +<meta name="Creation-Date" +content="2019-05-29T13:52:24Z"/> +<meta name="resourceName" + content="Assessment_and_Feedback_Policy.pdf"/> +<meta +name="Author" content="Ashley"/> +<meta name="producer" + content="Adobe PDF Library 15.0"/> +<meta +name="pdf:docinfo:producer" content="Adobe PDF Library 15.0"/> +<meta +name="_TemplateID" content="TC017730709991"/> +<meta +name="dc:description" content="Version 1.2"/> +<meta +name="access_permission:modify_annotations" content="true"/> +<meta +name="dc:creator" content="Ashley"/> +<meta name="description" +content="Version 1.2"/> +<meta name="dcterms:created" +content="2019-05-29T13:52:24Z"/> +<meta name="Last-Modified" + content="2019-05-29T13:52:29Z"/> +<meta +name="dcterms:modified" content="2019-05-29T13:52:29Z"/> +<meta +name="xmpMM:DocumentID" + content="uuid:19d0024b-2633-4ae5-a2f5-e0f1b491b856"/> +<meta +name="Last-Save-Date" content="2019-05-29T13:52:29Z"/> +<meta +name="pdf:docinfo:modified" content="2019-05-29T13:52:29Z"/> +<meta +name="meta:save-date" content="2019-05-29T13:52:29Z"/> +<meta +name="Content-Type" content="application/pdf"/> +<meta name="stream_size" +content="101489"/> +<meta name="xmp:MetadataDate" +content="2019-05-29T14:52:29Z"/> +<meta name="X-Parsed-By" + content="org.apache.tika.parser.DefaultParser"/> +<meta +name="X-Parsed-By" + content="org.apache.tika.parser.pdf.PDFParser"/> +<meta +name="creator" content="Ashley"/> +<meta name="dc:language" +content="EN-GB"/> +<meta name="pdf:producer" + content="Adobe PDF Library 15.0"/> +<meta +name="access_permission:assemble_document" content="true"/> +<meta +name="xmpTPg:NPages" content="9"/> +<meta name="pdf:hasXMP" + content="true"/> +<meta +name="access_permission:extract_content" content="true"/> +<meta +name="pdf:docinfo:custom:Company" + content="University of Strathclyde"/> +<meta +name="access_permission:can_print" content="true"/> +<meta +name="SourceModified" content="D:20190528113809"/> +<meta +name="access_permission:can_modify" content="true"/> +<meta +name="pdf:docinfo:created" content="2019-05-29T13:52:24Z"/> +<title>Assessment and Feedback Policy</title> +</head> +<body> + <div class="page"> + +<p/> +<p> +</p> +<p> +</p> +<p> +</p> +<p> ASSESSMENT AND +FEEDBACK POLICY +</p> +<p> +</p> +<p>Version No. Description Author Approval Effective Date +</p> +<p>1.2 Assessment and +</p> +<p>Feedback Policy – +</p> +<p>applicable to both +</p> +<p>undergraduate and +</p> +<p>postgraduate taught +</p> +<p>courses +</p> +<p>Assessment +</p> +<p>and +</p> +<p>Feedback +</p> +<p>Working +</p> +<p>Group +</p> +<p>Senate From academic +</p> +<p>year 2019-20 +</p> +<p> Version 1.2 +</p> +<p> + +the place of useful learning +The University of Strathclyde is a charitable body, registered in +Scotland, number SC015263 +</p> +<p> +</p> +<p> + + </p> +<p/> +</div> +<div class="page"> + +<p/> +<p> Assessment and Feedback Policy +</p> +<p> 1 +</p> +<p> +</p> +<p>1. RATIONALE +</p> +<p>In higher education, “assessment” describes any process that involves the evaluation or +appraisal of a student’s knowledge, understanding, skills, attitudes or abilities. In line with the +QAA UK Quality Code for Higher Education, assessment is taken to be an integral component +of teaching and learning, and serves multiple purposes. In addition to enabling evaluation and +measurement of students’ learning, effective assessment shapes and enhances student +learning. Assessments should therefore be designed to facilitate students’ attainment of +intended learning outcomes, and permit the measurement of such attainments against explicit +criteria. A holistic approach to assessment at course level can also support the development of +distinctive Strathclyde graduate attributes: graduates that are engaged, enquiring, enterprising, +and ethically and globally aware. Recent publications on assessment and feedback in higher +education suggest a ‘transformation’ is required to ensure that assessment and feedback +practices enable students to develop such attributes and the skills needed to be lifelong +learners in the 21st century.1 +</p> +<p>This approach recognises that assessment is central to learning and teaching, and is not +designed solely to measure student learning. Also essential to enhancing learning, is the +provision of continuous feedback to students on their learning; it is recognised that feedback +takes different forms (e.g. replies to posts on a discussion forum), but in relation to assessment, +useful feedback is feedback that is specific in informing learners the extent to which they have +met published assessment criteria, and explains to them what they need to do to improve. +Ensuring feedback is clear, specific, and adopts a supportive tone is most likely to foster +student engagement with feedback. The role of feedback in effectively supporting student +learning should be recognised at the assessment design stage, where factors such as the +timing of the release of feedback and the scheduling of subsequent assessments are +considered (particularly where feedback can be used by students to improve the development +of future work), as well as the provision of opportunities for students to clarify feedback. +</p> +<p>The University expects that feedback will normally be returned to students within 15 working +days of assessment submission2. The University’s VLE, Myplace, provides a platform for the +electronic return of grades and feedback, and where permitted by the nature of an assessment, +staff are expected to implement the submission of assessments online through Myplace, in +order to promote effectiveness and efficiency in each stage of the assessment and feedback +process. In addition, Myplace should be used to manage assessments that are submitted offline +in order to implement the Policy and Procedure on Extensions to Coursework Submission and +Procedures for the Recording and Publication of Marks. Staff should also be cognisant of +specific issues relevant to equality, diversity and inclusion in designing assessments and +providing feedback, which may affect particular groups of students.3 +</p> +<p> +1 For example: Boud, D. &amp; Falachikov, N. (Eds.). (2007). Rethinking Assessment in Higher Education. Oxon: +Routledge; Higher Education Academy. (2012). A Marked Improvement. Transforming assessment in higher +education. York: The Higher Education Academy; Merry, S., Price, M., Carless, D. &amp; Taras, M. (Eds.). (2013). +Reconceptualising Feedback in Higher Education. Developing Dialogue with Students. Oxon: Routledge; +Carless, D. (2015). Excellence in university assessment: learning from award-winning practice. London: +Routledge. +2 Exceptions to this are dissertations or assessments with credit weightings of 40 credits or higher, and where +double marking is required. (See the Policy on Moderation and Double Marking). +3 Staff are advised to consult the University’s equality impact assessment information where relevant. </p> +<p/> +<div class="annotation"> + <a href="https://www.qaa.ac.uk/quality-code/advice-and-guidance/assessment">https://www.qaa.ac.uk/quality-code/advice-and-guidance/assessment</a> +</div> +<div class="annotation"> + <a href="https://www.strath.ac.uk/equalitydiversity/eia/">https://www.strath.ac.uk/equalitydiversity/eia/</a> +</div> +</div> +<div class="page"> + +<p/> +<p> Assessment and Feedback Policy +</p> +<p> 2 +</p> +<p> +</p> +<p>Previous work on assessment at Strathclyde (through the Re-Engineering Assessment +Practices4 and Peer Evaluation in Education Review5 projects, together with the work on Peer +and Community Personal Development Planning), have emphasised the importance of +providing students with opportunities to self- and peer-assess, and for students to develop the +capacity to evaluate, reflect on6 and manage their own learning through the effective use of +feedback. Published research also highlights the importance of developing students’ +understanding of the criteria and standards by which their work is assessed, and suggests that +providing students with the opportunity to analyse and discuss the qualities of carefully selected +exemplars is an effective learning activity to achieve this7. It is widely recognised that these will, +in the long-term, help develop attitudes and skills that foster engagement and independence in +learning. + +The aim of the current policy is to develop an institution-wide approach to assessment and +feedback that enhances the effectiveness of assessment in aiding students to achieve the +necessary knowledge and skills described in a class or programme’s intended learning +outcomes. The policy is underpinned by principles of assessment and feedback (outlined in +section 3) that reflect this aim and a commitment to ensuring our assessment and feedback +practices are fair, transparent, and continuously reviewed. In short, to ensure they continue to +be fit for purpose. This policy should be adhered to across the University. The implementation +of the policy will be monitored by the Quality Assurance Committee. +</p> +<p> +2. SCOPE +</p> +<p>This policy applies to all undergraduate and postgraduate taught programmes offered by the +University. This policy should be read in conjunction with other relevant University policies and +procedures. The University website contains policies and procedures on a range of +assessment-related matters, including: ensuring equality, guidance for staff on supporting +students with a disability, guides for staff marking assessments in undergraduate and +postgraduate taught courses, guidance for students and staff on avoiding plagiarism, guidance +for staff in dealing with possible cases of plagiarism, policies on classifying honours degrees +and other degree awards, and on awarding motivational merit and distinction, guidance on the +compensation scheme, the role of external examiners in taught courses, the procedure for +dealing with student discipline cases, guidance for staff on the appeals procedure and +accounting for students’ personal circumstances. +</p> +<p> +</p> +<p> +</p> +<p> + +4 REAP Project, accessed at http://www.reap.ac.uk/ +5 PEER Project, accessed from: http://www.reap.ac.uk/PEER.aspx. The site contains a PEER toolkit +containing guidance for staff on using peer review. +6 Winstone, N. &amp; Nash. R. (2016). The Developing Engagement with Feedback Toolkit (DEFT). The Higher +Education Academy. https://www.heacademy.ac.uk/knowledge-hub/developing-engagement-feedback-toolkit- +deft +7 Carless, D. &amp; Boud, D. (2018): The development of student feedback literacy: enabling uptake of feedback, +Assessment &amp; Evaluation in Higher Education, DOI: 10.1080/02602938.2018.1463354 </p> +<p/> +<div class="annotation"> + <a href="http://www.strath.ac.uk/staff/policies/academic/">http://www.strath.ac.uk/staff/policies/academic/</a> +</div> +<div class="annotation"> + <a href="http://www.reap.ac.uk/">http://www.reap.ac.uk/</a> +</div> +<div class="annotation"> + <a href="http://www.reap.ac.uk/PEER.aspx">http://www.reap.ac.uk/PEER.aspx</a> +</div> +<div class="annotation"> + <a href="https://www.heacademy.ac.uk/knowledge-hub/developing-engagement-feedback-toolkit-deft">https://www.heacademy.ac.uk/knowledge-hub/developing-engagement-feedback-toolkit-deft</a> +</div> +<div class="annotation"> + <a href="https://www.heacademy.ac.uk/knowledge-hub/developing-engagement-feedback-toolkit-deft">https://www.heacademy.ac.uk/knowledge-hub/developing-engagement-feedback-toolkit-deft</a> +</div> +</div> +<div class="page"> + +<p/> +<p> Assessment and Feedback Policy +</p> +<p> 3 +</p> +<p> +</p> +<p>3. ASSESSMENT AND FEEDBACK PRINCIPLES + +</p> +<p>These principles have been widely consulted on across the University, and must be adhered to +throughout the institution. +</p> +<p>PRINCIPLE 1. ASSESSMENT AND FEEDBACK PRACTICES PROMOTE EFFECTIVE +STUDENT LEARNING +</p> +<p>1.1 Assessment and feedback activities are designed to foster student engagement, to +support and measure students’ attainment of knowledge, understanding, and +transferable skills. +</p> +<p>1.2 A range of assessment methods are used, increasing in complexity across a +programme, and taking into consideration student and staff workloads. +</p> +<p>1.3 Assessment and feedback practices align with intended learning outcomes and +assessment criteria, and provide opportunities for students to be active participants in +the process, including opportunities for students to discuss feedback. +</p> +<p>1.4 Timely, constructive, and supportive feedback helps students understand the extent to +which they have fulfilled the assessment criteria, and supports students to use their +feedback in the development of subsequent work. +</p> +<p>1.5 The timing of assessments and return of feedback should be scheduled to enable +students to use feedback to inform their approach to subsequent assessments. The +release of feedback should consider the ability of students to gain further feedback and +support, and avoid release in close proximity to periods where the university is closed +</p> +<p>1.6 Where examinations are used, students should receive at least general feedback soon +after the examination. Students who wish to view their exam scripts following the +provision of general feedback should be facilitated to do so under supervision of staff. +</p> +<p>PRINCIPLE 2. ASSESSMENT AND FEEDBACK PRACTICES ARE APPROPRIATE, FAIR, +AND TRANSPARENT +</p> +<p>2.1 Assessment tasks are appropriate to disciplinary and/or professional contexts. +2.2 Assessment applies rigorous academic standards related to and across, the discipline(s) +</p> +<p>or professional context and is based on clearly defined assessment criteria. +2.3 Assessment grading and feedback is based solely on students’ achievement against +</p> +<p>criteria and standards. +2.4 Assessment and feedback practices are fair, inclusive and accessible to all students. +</p> +<p>PRINCIPLE 3. ASSESSMENT AND FEEDBACK PRACTICES ARE CLEARLY +COMMUNICATED TO STUDENTS AND STAFF +</p> +<p>3.1 Students must be made aware at the beginning of a class of the details of assessments, +including the purpose, weighting, and timing of assessment. +</p> +<p>3.2 Students must be made aware at the beginning of a class of the nature and timing of +feedback. Where unexpected problems cause a delay in the return of feedback within 15 +working days, an explanation will be provided to students, along with a new date for the +return of feedback. +</p> +<p>3.3 All students and staff are made aware at the start of a class of the criteria and standards +used to assess and provide feedback on students’ work. </p> +<p/> +</div> +<div class="page"> + +<p/> +<p> Assessment and Feedback Policy +</p> +<p> 4 +</p> +<p> +</p> +<p>3.4 Students must be made aware of relevant policies and procedures around academic +honesty in assessment, and of procedures associated with personal circumstances, +extensions to the deadlines of coursework, and late submission of coursework. +</p> +<p>3.5 There are opportunities for students and staff to engage in a dialogue around +assessment and feedback, including opportunities for students to clarify feedback. +</p> +<p>3.6 In each programme the processes for marking, moderation, and feedback are +appropriate and fair and are explained to students and staff. +</p> +<p>PRINCIPLE 4. ASSESSMENT AND FEEDBACK PRACTICES ARE CONTINUOUSLY +REVIEWED +</p> +<p>4.1 The timing of assessment activities at cohort level, including the timing of examinations, +are monitored and reviewed, in order to minimise bunching. +</p> +<p>4.2 Assessment and feedback activities, and the outcomes of assessment, are reviewed via +the class and course approval and/or review processes, and external examining +processes. +</p> +<p>4.3 Assessment and feedback activities are continuously reviewed to ensure effective +alignment with a programme’s intended learning outcomes and graduate attributes. +</p> +<p>4.4 Opportunities to develop effective practice and innovation in assessment and feedback +are available to all staff involved in assessment. +</p> +<p> +4. ANONYMOUS MARKING +</p> +<p>4.1 Anonymous marking (when the identity of a student is not known to a marker and/or exam +board) will be used for all assessments wherever possible and appropriate, including formal +written examinations at both undergraduate and postgraduate levels. +</p> +<p>4.2 Where anonymous marking is not possible, for example where an alternative method of +assessment has been used for a student with a disability, additional scrutiny by a second +marker may be appropriate. +</p> +<p>4.3 Other exceptions will be where the mode of assessment (e.g., presentations or oral +examinations) prevents anonymity, or where there is pedagogical justification (e.g., the use +of ipsative feedback). +</p> +<p>4.4 Double marking is effective practice for assessments that are not marked anonymously and +for dissertations, or comparable assessments. +</p> +<p> +5. RESPONSIBILITIES FOR IMPLEMENTATION +</p> +<p>5.1 The University assumes responsibility for: +- Providing staff with opportunities to develop effective practice in assessment and +</p> +<p>feedback through the Organisational Staff and Development Unit (Strathclyde Teaching +Excellence Programme) Education Enhancement (the University’s VLE, Myplace offers +various tools to support effective practice in assessment and feedback, including +handling peer review/assessment activities, online assessment submission and return of +feedback, and enabling efficient communication with different student and staff groups; +the Sharing Practice in Enhancing Learning and Teaching (SPELT) platform, and </p> +<p/> +<div class="annotation"> + <a href="https://www.strath.ac.uk/hr/learninganddevelopment/step/">https://www.strath.ac.uk/hr/learninganddevelopment/step/</a> +</div> +<div class="annotation"> + <a href="https://www.strath.ac.uk/hr/learninganddevelopment/step/">https://www.strath.ac.uk/hr/learninganddevelopment/step/</a> +</div> +<div class="annotation"> + <a href="http://spelt.strath.ac.uk/pages/viewpage.action;jsessionid=01EB624BAAB6873613854F3DC2EE7433?pageId=8979188">http://spelt.strath.ac.uk/pages/viewpage.action;jsessionid=01EB624BAAB6873613854F3DC2EE7433?pageId=8979188</a> +</div> +</div> +<div class="page"> + +<p/> +<p> Assessment and Feedback Policy +</p> +<p> 5 +</p> +<p> +</p> +<p>TESTA), the Disability and Wellbeing Service (for ensuring assessments are accessible +to all students), and the Equality and Diversity Office; +</p> +<p>- Providing adequate resources, including information technology systems, to support +effective practice (Information Services Directorate, Student Business, Education +Enhancement); and +</p> +<p>- Monitoring implementation of this policy through the Quality Assurance Committee +(Education Enhancement). +</p> +<p>5.2 Faculties are responsible for: overseeing and receiving reports from programme exam +boards; scrutinising external examiners’ reports and responding to them; ensuring +consistency in the implementation of this and other related policies across programmes, +and sharing effective practice within and between faculties. +</p> +<p>5.3 Heads of Department/School are responsible for: instructing colleagues within their +Department/School to note and adhere to the Assessment and Feedback Policy and +Procedures for Recording and Publication of Marks, Procedures on Preparing and +Conducting Exams, and other related policies such as the Policy on the Late Submission of +Coursework, Policy on Extensions to Coursework Submission, the Policy on Moderation +and Double Marking, and Guidance on Marking Assessments in Undergraduate and +Postgraduate Taught courses; and for ensuring that marking of assessments is sufficiently +resourced to enable feedback to be returned within 15 working days of assessment +submission. +</p> +<p>5.4 Programme Exam Boards are responsible for: making sure assessments across a +programme are marked fairly; ensuring university and faculty regulations are adhered to; +and responding to points made by External Examiners. +</p> +<p>5.5 Programme Leaders/Directors are responsible for ensuring a coherent and effective +approach to assessment and feedback is taken across a programme of study. This +approach will involve: +</p> +<p>- Ensuring a range of assessment methods are used throughout a programme of study, +as appropriate to the discipline; +</p> +<p>- Monitoring the intensity and equity of assessments across and between programmes, +with due consideration to joint honours degrees; +</p> +<p>- Designing and reviewing the Assessment Schedule and Exam Timetable within a +programme and requesting amendments where there is assessment/exam bunching; +</p> +<p>- Developing effective moderation and/or double marking procedures, in line with the +Policy on Moderation and Double Marking; +</p> +<p>- Ensuring procedures are in place for monitoring students’ use of the Policies and +Procedures on Extensions to Coursework Submission and Late Submission of +Coursework, as a mechanism for identifying students who may require support; and +</p> +<p>- Ensuring procedures related to assuring fairness in assessment, such as moderation +and the role of External Examiners, are communicated to students. +</p> +<p>5.6 Year Coordinators and/or Module Coordinators are responsible for: +</p> +<p>- Having an overview of summative assessment activities within a programme of study to +avoid, where possible, a concentration of assessment deadlines, including examinations; </p> +<p/> +</div> +<div class="page"> + +<p/> +<p> Assessment and Feedback Policy +</p> +<p> 6 +</p> +<p> +</p> +<p>- Making sure the details of all assessments are provided at the start of a class; +- Ensuring assessment criteria that is aligned to intended learning outcomes and the +</p> +<p>University’s Guidance on Marking Assessments in Undergraduate and Postgraduate +Taught programmes, is transparent and made available to students at the start of a +class; +</p> +<p>- Providing opportunities for students to use feedback prior to the submission of any +subsequent related assessments; +</p> +<p>- Working with students to develop effective practice in assessment and feedback, +including: providing students with opportunities to develop their understanding of the +criteria and standards used to assess their work and supporting students to engage with +and use feedback; +</p> +<p>- Obtaining feedback from students in relation to assessment and feedback for module(s) +for which they are responsible, and for communicating any actions on this feedback to +students; and +</p> +<p>- For undertaking effective moderation and/or double marking procedures. +</p> +<p>5.7 Staff involved in assessing students’ work are responsible for: +</p> +<p>- Designing assessments that effectively facilitate and measure students’ achievement of +intended learning outcomes; +</p> +<p>- Assessing students’ work according to published assessment criteria which are aligned +to intended learning outcomes and the University’s Guidance on Marking Assessments +in Undergraduate and Postgraduate Taught programmes; +</p> +<p>- Providing timely, informative and helpful feedback which enables students to further +improve their learning and performance wherever possible; +</p> +<p>- Informing students when, where and how feedback will be provided; +- Engaging in dialogue with students about assessment and feedback; and +- Continuously reviewing their approaches to assessment and feedback to reflect effective +</p> +<p>practice. +</p> +<p>5.8 Students are expected to be responsible for their own learning through: +</p> +<p>- Understanding the requirements of individual assessments, and actively engaging with +assessment tasks by devoting appropriate time and effort; +</p> +<p>- Developing an understanding of the relationship between intended learning outcomes +and assessment criteria, and standards in their programme of study; +</p> +<p>- Ensuring their academic work is authentic and honestly produced; +- Finding out where, how and when work is submitted and how and when feedback is +</p> +<p>provided; +- Actively engaging, reflecting, and using provided feedback; +- Understanding the academic policies and procedures related to assessment and +</p> +<p>feedback, including Personal Circumstances Procedure, Policy and Procedures for the +Late Submission of Coursework, Policy on Moderation and Double Marking, Policy on +Extensions to the Submission of Coursework, and Compensation and Motivational Merit; +</p> +<p>- Seeking academic support when needed, for example, if feedback needs to be clarified; +and +</p> +<p>- Participating in the development of assessment and feedback practices at class and +programme levels. </p> +<p/> +</div> +<div class="page"> + +<p/> +<p> Assessment and Feedback Policy +</p> +<p> 7 +</p> +<p> +</p> +<p>6. DEFINITIONS OF TERMS + +Assessment The process of measuring the performance of students +</p> +<p>(as in examinations, assignments and other assessable +work) that enables students to monitor their progress +and contributes to their academic results. + +</p> +<p>Assessment criteria + +</p> +<p> +</p> +<p>Specific criteria against which pieces of work are +assessed. An understanding of the criteria must be +shared by markers and students. Feedback should +relate to these criteria. +</p> +<p>Assessment bunching The perception of assessments within a programme of +study being scheduled too closely together. + +</p> +<p>Assessment schedule + +</p> +<p>A timeline of all cohort level assessment. +</p> +<p>Exam bunching Where the scheduling of two exams falls within a 23 +hour period. + +</p> +<p>Feedback Information provided to students on the quality of their +performance in relation to assessment criteria, which +forms the basis of improved student learning. + +</p> +<p>Formative assessment + +</p> +<p>This type of assessment normally has no or low +weighting in the final mark for a module or programme. +The goal of formative assessment is to provide an +opportunity for students to monitor their learning and +provide feedback to teachers that can be used to +review their teaching. + +</p> +<p>Graduate attributes Qualities, skills, dispositions, and understanding that +students are expected to develop. At Strathclyde these +are referred to as the 4 E’s – engaged, enquiring, +enterprising and ethically and globally aware. + +</p> +<p>Intended learning outcomes What the student is expected to be able to do or +demonstrate, in terms of particular knowledge, skills +and understanding, by the end of a module or +programme. + +</p> +<p>Ipsative feedback + +</p> +<p>This type of feedback compares a student’s +performance on a current assessment with +performance on a previous assessment. + +</p> +<p>Moderation The process of checking that assessment criteria are +consistently applied across markers in marking +students’ work. + +</p> +<p>Module The individual components of a programme, normally +worth 20 or 10 credits. + +</p> +<p>Module evaluation The process of obtaining feedback from students on all +aspects of teaching, learning, and assessment within a </p> +<p/> +</div> +<div class="page"> +<p/> +<p> Assessment and Feedback Policy +</p> +<p> 8 +</p> +<p> +</p> +<p>module. + +</p> +<p>Programme The full degree programme leading to an award. + +</p> +<p>Seen double marking Where an assessment is independently marked by two +markers, but where the second marker has access to +the marks or comments of the first marker. + +</p> +<p>Summative assessment Assessment is summative when the grading of an +assessment contributes to the final grade for a class or +course. The aim of summative assessment is to +evaluate students’ attainment of the intended learning +outcomes within a unit of study. + +</p> +<p> +Unseen double marking + +</p> +<p>Where an assessment is independently marked by two +markers who do not have access to the grades or +comments of the other marker. + +</p> +<p>Working days The University’s standard working week is normally +Monday to Friday, covering five working days. Fifteen +working days would normally equate to 21 calendar +days, but may be longer where the period includes +University closure days. +</p> +<p> + </p> +<p/> +</div> +</body> +</html> + + + + 2019-05-29T13:52:29Z + + + 1.5 + + + Acrobat PDFMaker 15 for Word + + + Assessment and Feedback Policy + + + University of Strathclyde + + + application/json + + + false + + + true + + + Version 1.2 + + + EN-GB + + + application/pdf; version=1.5 + + + Acrobat PDFMaker 15 for Word + + + true + + + false + + + Assessment and Feedback Policy + + + 2019-05-29T14:52:24Z + + + 2019-05-29T13:52:29Z + + + D:20190528113809 + + + Version 1.2 + + + Version 1.2 + + + TC017730709991 + + + true + + + 2019-05-29T14:52:29Z + + + Ashley + + + Ashley + + + 2019-05-29T13:52:24Z + + + 2019-05-29T13:52:24Z + + + true + + + 2019-05-29T13:52:24Z + + + Assessment_and_Feedback_Policy.pdf + + + Ashley + + + Adobe PDF Library 15.0 + + + Adobe PDF Library 15.0 + + + TC017730709991 + + + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + + Version 1.2 + + + true + + + Ashley + + + Version 1.2 + + + 2019-05-29T13:52:24Z + + + 2019-05-29T13:52:29Z + + + 2019-05-29T13:52:29Z + + + Assessment and Feedback Policy + + + uuid:19d0024b-2633-4ae5-a2f5-e0f1b491b856 + + + 2019-05-29T13:52:29Z + + + 2019-05-29T13:52:29Z + + + 2019-05-29T13:52:29Z + + + application/pdf + + + 101489 + + + 2019-05-29T14:52:29Z + + + org.apache.tika.parser.DefaultParser + org.apache.tika.parser.pdf.PDFParser + + + Ashley + + + EN-GB + + + Adobe PDF Library 15.0 + + + true + + + 9 + + + true + + + 407 + 4047 + 3287 + 2892 + 2798 + 3055 + 2950 + 2113 + 932 + + + true + + + University of Strathclyde + + + true + + + D:20190528113809 + + + true + + + 2019-05-29T13:52:24Z + + + +<?xml version="1.0" encoding="UTF-8"?> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta name="date" +content="2019-05-29T13:52:29Z"/> +<meta name="pdf:PDFVersion" +content="1.5"/> +<meta name="xmp:CreatorTool" + content="Acrobat PDFMaker 15 for Word"/> +<meta +name="pdf:docinfo:title" + content="Assessment and Feedback Policy"/> +<meta +name="Company" content="University of Strathclyde"/> +<meta +name="stream_content_type" content="application/json"/> +<meta +name="pdf:hasXFA" content="false"/> +<meta +name="access_permission:can_print_degraded" content="true"/> +<meta +name="subject" content="Version 1.2"/> +<meta name="language" +content="EN-GB"/> +<meta name="dc:format" + content="application/pdf; version=1.5"/> +<meta +name="pdf:docinfo:creator_tool" + content="Acrobat PDFMaker 15 for Word"/> +<meta +name="access_permission:fill_in_form" content="true"/> +<meta +name="pdf:encrypted" content="false"/> +<meta name="dc:title" + content="Assessment and Feedback Policy"/> +<meta +name="xmp:CreateDate" content="2019-05-29T14:52:24Z"/> +<meta +name="modified" content="2019-05-29T13:52:29Z"/> +<meta +name="pdf:docinfo:custom:SourceModified" +content="D:20190528113809"/> +<meta name="cp:subject" +content="Version 1.2"/> +<meta name="pdf:docinfo:subject" + content="Version 1.2"/> +<meta +name="pdf:docinfo:custom:_TemplateID" content="TC017730709991"/> +<meta +name="pdf:hasMarkedContent" content="true"/> +<meta name="xmp:ModifyDate" + content="2019-05-29T14:52:29Z"/> +<meta +name="pdf:docinfo:creator" content="Ashley"/> +<meta name="meta:author" +content="Ashley"/> +<meta name="meta:creation-date" +content="2019-05-29T13:52:24Z"/> +<meta name="created" + content="2019-05-29T13:52:24Z"/> +<meta +name="access_permission:extract_for_accessibility" +content="true"/> +<meta name="Creation-Date" +content="2019-05-29T13:52:24Z"/> +<meta name="resourceName" + content="Assessment_and_Feedback_Policy.pdf"/> +<meta +name="Author" content="Ashley"/> +<meta name="producer" + content="Adobe PDF Library 15.0"/> +<meta +name="pdf:docinfo:producer" content="Adobe PDF Library 15.0"/> +<meta +name="_TemplateID" content="TC017730709991"/> +<meta +name="dc:description" content="Version 1.2"/> +<meta +name="access_permission:modify_annotations" content="true"/> +<meta +name="dc:creator" content="Ashley"/> +<meta name="description" +content="Version 1.2"/> +<meta name="dcterms:created" +content="2019-05-29T13:52:24Z"/> +<meta name="Last-Modified" + content="2019-05-29T13:52:29Z"/> +<meta +name="dcterms:modified" content="2019-05-29T13:52:29Z"/> +<meta +name="xmpMM:DocumentID" + content="uuid:19d0024b-2633-4ae5-a2f5-e0f1b491b856"/> +<meta +name="Last-Save-Date" content="2019-05-29T13:52:29Z"/> +<meta +name="pdf:docinfo:modified" content="2019-05-29T13:52:29Z"/> +<meta +name="meta:save-date" content="2019-05-29T13:52:29Z"/> +<meta +name="Content-Type" content="application/pdf"/> +<meta name="stream_size" +content="101489"/> +<meta name="xmp:MetadataDate" +content="2019-05-29T14:52:29Z"/> +<meta name="X-Parsed-By" + content="org.apache.tika.parser.DefaultParser"/> +<meta +name="X-Parsed-By" + content="org.apache.tika.parser.pdf.PDFParser"/> +<meta +name="creator" content="Ashley"/> +<meta name="dc:language" +content="EN-GB"/> +<meta name="pdf:producer" + content="Adobe PDF Library 15.0"/> +<meta +name="access_permission:assemble_document" content="true"/> +<meta +name="xmpTPg:NPages" content="9"/> +<meta name="pdf:hasXMP" + content="true"/> +<meta +name="access_permission:extract_content" content="true"/> +<meta +name="pdf:docinfo:custom:Company" + content="University of Strathclyde"/> +<meta +name="access_permission:can_print" content="true"/> +<meta +name="SourceModified" content="D:20190528113809"/> +<meta +name="access_permission:can_modify" content="true"/> +<meta +name="pdf:docinfo:created" content="2019-05-29T13:52:24Z"/> +<title>Assessment and Feedback Policy</title> +</head> +<body> + <div class="page"> + +<p/> +<p> +</p> +<p> +</p> +<p> +</p> +<p> ASSESSMENT AND +FEEDBACK POLICY +</p> +<p> +</p> +<p>Version No. Description Author Approval Effective Date +</p> +<p>1.2 Assessment and +</p> +<p>Feedback Policy – +</p> +<p>applicable to both +</p> +<p>undergraduate and +</p> +<p>postgraduate taught +</p> +<p>courses +</p> +<p>Assessment +</p> +<p>and +</p> +<p>Feedback +</p> +<p>Working +</p> +<p>Group +</p> +<p>Senate From academic +</p> +<p>year 2019-20 +</p> +<p> Version 1.2 +</p> +<p> + +the place of useful learning +The University of Strathclyde is a charitable body, registered in +Scotland, number SC015263 +</p> +<p> +</p> +<p> + + </p> +<p/> +</div> +<div class="page"> + +<p/> +<p> Assessment and Feedback Policy +</p> +<p> 1 +</p> +<p> +</p> +<p>1. RATIONALE +</p> +<p>In higher education, “assessment” describes any process that involves the evaluation or +appraisal of a student’s knowledge, understanding, skills, attitudes or abilities. In line with the +QAA UK Quality Code for Higher Education, assessment is taken to be an integral component +of teaching and learning, and serves multiple purposes. In addition to enabling evaluation and +measurement of students’ learning, effective assessment shapes and enhances student +learning. Assessments should therefore be designed to facilitate students’ attainment of +intended learning outcomes, and permit the measurement of such attainments against explicit +criteria. A holistic approach to assessment at course level can also support the development of +distinctive Strathclyde graduate attributes: graduates that are engaged, enquiring, enterprising, +and ethically and globally aware. Recent publications on assessment and feedback in higher +education suggest a ‘transformation’ is required to ensure that assessment and feedback +practices enable students to develop such attributes and the skills needed to be lifelong +learners in the 21st century.1 +</p> +<p>This approach recognises that assessment is central to learning and teaching, and is not +designed solely to measure student learning. Also essential to enhancing learning, is the +provision of continuous feedback to students on their learning; it is recognised that feedback +takes different forms (e.g. replies to posts on a discussion forum), but in relation to assessment, +useful feedback is feedback that is specific in informing learners the extent to which they have +met published assessment criteria, and explains to them what they need to do to improve. +Ensuring feedback is clear, specific, and adopts a supportive tone is most likely to foster +student engagement with feedback. The role of feedback in effectively supporting student +learning should be recognised at the assessment design stage, where factors such as the +timing of the release of feedback and the scheduling of subsequent assessments are +considered (particularly where feedback can be used by students to improve the development +of future work), as well as the provision of opportunities for students to clarify feedback. +</p> +<p>The University expects that feedback will normally be returned to students within 15 working +days of assessment submission2. The University’s VLE, Myplace, provides a platform for the +electronic return of grades and feedback, and where permitted by the nature of an assessment, +staff are expected to implement the submission of assessments online through Myplace, in +order to promote effectiveness and efficiency in each stage of the assessment and feedback +process. In addition, Myplace should be used to manage assessments that are submitted offline +in order to implement the Policy and Procedure on Extensions to Coursework Submission and +Procedures for the Recording and Publication of Marks. Staff should also be cognisant of +specific issues relevant to equality, diversity and inclusion in designing assessments and +providing feedback, which may affect particular groups of students.3 +</p> +<p> +1 For example: Boud, D. &amp; Falachikov, N. (Eds.). (2007). Rethinking Assessment in Higher Education. Oxon: +Routledge; Higher Education Academy. (2012). A Marked Improvement. Transforming assessment in higher +education. York: The Higher Education Academy; Merry, S., Price, M., Carless, D. &amp; Taras, M. (Eds.). (2013). +Reconceptualising Feedback in Higher Education. Developing Dialogue with Students. Oxon: Routledge; +Carless, D. (2015). Excellence in university assessment: learning from award-winning practice. London: +Routledge. +2 Exceptions to this are dissertations or assessments with credit weightings of 40 credits or higher, and where +double marking is required. (See the Policy on Moderation and Double Marking). +3 Staff are advised to consult the University’s equality impact assessment information where relevant. </p> +<p/> +<div class="annotation"> + <a href="https://www.qaa.ac.uk/quality-code/advice-and-guidance/assessment">https://www.qaa.ac.uk/quality-code/advice-and-guidance/assessment</a> +</div> +<div class="annotation"> + <a href="https://www.strath.ac.uk/equalitydiversity/eia/">https://www.strath.ac.uk/equalitydiversity/eia/</a> +</div> +</div> +<div class="page"> + +<p/> +<p> Assessment and Feedback Policy +</p> +<p> 2 +</p> +<p> +</p> +<p>Previous work on assessment at Strathclyde (through the Re-Engineering Assessment +Practices4 and Peer Evaluation in Education Review5 projects, together with the work on Peer +and Community Personal Development Planning), have emphasised the importance of +providing students with opportunities to self- and peer-assess, and for students to develop the +capacity to evaluate, reflect on6 and manage their own learning through the effective use of +feedback. Published research also highlights the importance of developing students’ +understanding of the criteria and standards by which their work is assessed, and suggests that +providing students with the opportunity to analyse and discuss the qualities of carefully selected +exemplars is an effective learning activity to achieve this7. It is widely recognised that these will, +in the long-term, help develop attitudes and skills that foster engagement and independence in +learning. + +The aim of the current policy is to develop an institution-wide approach to assessment and +feedback that enhances the effectiveness of assessment in aiding students to achieve the +necessary knowledge and skills described in a class or programme’s intended learning +outcomes. The policy is underpinned by principles of assessment and feedback (outlined in +section 3) that reflect this aim and a commitment to ensuring our assessment and feedback +practices are fair, transparent, and continuously reviewed. In short, to ensure they continue to +be fit for purpose. This policy should be adhered to across the University. The implementation +of the policy will be monitored by the Quality Assurance Committee. +</p> +<p> +2. SCOPE +</p> +<p>This policy applies to all undergraduate and postgraduate taught programmes offered by the +University. This policy should be read in conjunction with other relevant University policies and +procedures. The University website contains policies and procedures on a range of +assessment-related matters, including: ensuring equality, guidance for staff on supporting +students with a disability, guides for staff marking assessments in undergraduate and +postgraduate taught courses, guidance for students and staff on avoiding plagiarism, guidance +for staff in dealing with possible cases of plagiarism, policies on classifying honours degrees +and other degree awards, and on awarding motivational merit and distinction, guidance on the +compensation scheme, the role of external examiners in taught courses, the procedure for +dealing with student discipline cases, guidance for staff on the appeals procedure and +accounting for students’ personal circumstances. +</p> +<p> +</p> +<p> +</p> +<p> + +4 REAP Project, accessed at http://www.reap.ac.uk/ +5 PEER Project, accessed from: http://www.reap.ac.uk/PEER.aspx. The site contains a PEER toolkit +containing guidance for staff on using peer review. +6 Winstone, N. &amp; Nash. R. (2016). The Developing Engagement with Feedback Toolkit (DEFT). The Higher +Education Academy. https://www.heacademy.ac.uk/knowledge-hub/developing-engagement-feedback-toolkit- +deft +7 Carless, D. &amp; Boud, D. (2018): The development of student feedback literacy: enabling uptake of feedback, +Assessment &amp; Evaluation in Higher Education, DOI: 10.1080/02602938.2018.1463354 </p> +<p/> +<div class="annotation"> + <a href="http://www.strath.ac.uk/staff/policies/academic/">http://www.strath.ac.uk/staff/policies/academic/</a> +</div> +<div class="annotation"> + <a href="http://www.reap.ac.uk/">http://www.reap.ac.uk/</a> +</div> +<div class="annotation"> + <a href="http://www.reap.ac.uk/PEER.aspx">http://www.reap.ac.uk/PEER.aspx</a> +</div> +<div class="annotation"> + <a href="https://www.heacademy.ac.uk/knowledge-hub/developing-engagement-feedback-toolkit-deft">https://www.heacademy.ac.uk/knowledge-hub/developing-engagement-feedback-toolkit-deft</a> +</div> +<div class="annotation"> + <a href="https://www.heacademy.ac.uk/knowledge-hub/developing-engagement-feedback-toolkit-deft">https://www.heacademy.ac.uk/knowledge-hub/developing-engagement-feedback-toolkit-deft</a> +</div> +</div> +<div class="page"> + +<p/> +<p> Assessment and Feedback Policy +</p> +<p> 3 +</p> +<p> +</p> +<p>3. ASSESSMENT AND FEEDBACK PRINCIPLES + +</p> +<p>These principles have been widely consulted on across the University, and must be adhered to +throughout the institution. +</p> +<p>PRINCIPLE 1. ASSESSMENT AND FEEDBACK PRACTICES PROMOTE EFFECTIVE +STUDENT LEARNING +</p> +<p>1.1 Assessment and feedback activities are designed to foster student engagement, to +support and measure students’ attainment of knowledge, understanding, and +transferable skills. +</p> +<p>1.2 A range of assessment methods are used, increasing in complexity across a +programme, and taking into consideration student and staff workloads. +</p> +<p>1.3 Assessment and feedback practices align with intended learning outcomes and +assessment criteria, and provide opportunities for students to be active participants in +the process, including opportunities for students to discuss feedback. +</p> +<p>1.4 Timely, constructive, and supportive feedback helps students understand the extent to +which they have fulfilled the assessment criteria, and supports students to use their +feedback in the development of subsequent work. +</p> +<p>1.5 The timing of assessments and return of feedback should be scheduled to enable +students to use feedback to inform their approach to subsequent assessments. The +release of feedback should consider the ability of students to gain further feedback and +support, and avoid release in close proximity to periods where the university is closed +</p> +<p>1.6 Where examinations are used, students should receive at least general feedback soon +after the examination. Students who wish to view their exam scripts following the +provision of general feedback should be facilitated to do so under supervision of staff. +</p> +<p>PRINCIPLE 2. ASSESSMENT AND FEEDBACK PRACTICES ARE APPROPRIATE, FAIR, +AND TRANSPARENT +</p> +<p>2.1 Assessment tasks are appropriate to disciplinary and/or professional contexts. +2.2 Assessment applies rigorous academic standards related to and across, the discipline(s) +</p> +<p>or professional context and is based on clearly defined assessment criteria. +2.3 Assessment grading and feedback is based solely on students’ achievement against +</p> +<p>criteria and standards. +2.4 Assessment and feedback practices are fair, inclusive and accessible to all students. +</p> +<p>PRINCIPLE 3. ASSESSMENT AND FEEDBACK PRACTICES ARE CLEARLY +COMMUNICATED TO STUDENTS AND STAFF +</p> +<p>3.1 Students must be made aware at the beginning of a class of the details of assessments, +including the purpose, weighting, and timing of assessment. +</p> +<p>3.2 Students must be made aware at the beginning of a class of the nature and timing of +feedback. Where unexpected problems cause a delay in the return of feedback within 15 +working days, an explanation will be provided to students, along with a new date for the +return of feedback. +</p> +<p>3.3 All students and staff are made aware at the start of a class of the criteria and standards +used to assess and provide feedback on students’ work. </p> +<p/> +</div> +<div class="page"> + +<p/> +<p> Assessment and Feedback Policy +</p> +<p> 4 +</p> +<p> +</p> +<p>3.4 Students must be made aware of relevant policies and procedures around academic +honesty in assessment, and of procedures associated with personal circumstances, +extensions to the deadlines of coursework, and late submission of coursework. +</p> +<p>3.5 There are opportunities for students and staff to engage in a dialogue around +assessment and feedback, including opportunities for students to clarify feedback. +</p> +<p>3.6 In each programme the processes for marking, moderation, and feedback are +appropriate and fair and are explained to students and staff. +</p> +<p>PRINCIPLE 4. ASSESSMENT AND FEEDBACK PRACTICES ARE CONTINUOUSLY +REVIEWED +</p> +<p>4.1 The timing of assessment activities at cohort level, including the timing of examinations, +are monitored and reviewed, in order to minimise bunching. +</p> +<p>4.2 Assessment and feedback activities, and the outcomes of assessment, are reviewed via +the class and course approval and/or review processes, and external examining +processes. +</p> +<p>4.3 Assessment and feedback activities are continuously reviewed to ensure effective +alignment with a programme’s intended learning outcomes and graduate attributes. +</p> +<p>4.4 Opportunities to develop effective practice and innovation in assessment and feedback +are available to all staff involved in assessment. +</p> +<p> +4. ANONYMOUS MARKING +</p> +<p>4.1 Anonymous marking (when the identity of a student is not known to a marker and/or exam +board) will be used for all assessments wherever possible and appropriate, including formal +written examinations at both undergraduate and postgraduate levels. +</p> +<p>4.2 Where anonymous marking is not possible, for example where an alternative method of +assessment has been used for a student with a disability, additional scrutiny by a second +marker may be appropriate. +</p> +<p>4.3 Other exceptions will be where the mode of assessment (e.g., presentations or oral +examinations) prevents anonymity, or where there is pedagogical justification (e.g., the use +of ipsative feedback). +</p> +<p>4.4 Double marking is effective practice for assessments that are not marked anonymously and +for dissertations, or comparable assessments. +</p> +<p> +5. RESPONSIBILITIES FOR IMPLEMENTATION +</p> +<p>5.1 The University assumes responsibility for: +- Providing staff with opportunities to develop effective practice in assessment and +</p> +<p>feedback through the Organisational Staff and Development Unit (Strathclyde Teaching +Excellence Programme) Education Enhancement (the University’s VLE, Myplace offers +various tools to support effective practice in assessment and feedback, including +handling peer review/assessment activities, online assessment submission and return of +feedback, and enabling efficient communication with different student and staff groups; +the Sharing Practice in Enhancing Learning and Teaching (SPELT) platform, and </p> +<p/> +<div class="annotation"> + <a href="https://www.strath.ac.uk/hr/learninganddevelopment/step/">https://www.strath.ac.uk/hr/learninganddevelopment/step/</a> +</div> +<div class="annotation"> + <a href="https://www.strath.ac.uk/hr/learninganddevelopment/step/">https://www.strath.ac.uk/hr/learninganddevelopment/step/</a> +</div> +<div class="annotation"> + <a href="http://spelt.strath.ac.uk/pages/viewpage.action;jsessionid=01EB624BAAB6873613854F3DC2EE7433?pageId=8979188">http://spelt.strath.ac.uk/pages/viewpage.action;jsessionid=01EB624BAAB6873613854F3DC2EE7433?pageId=8979188</a> +</div> +</div> +<div class="page"> + +<p/> +<p> Assessment and Feedback Policy +</p> +<p> 5 +</p> +<p> +</p> +<p>TESTA), the Disability and Wellbeing Service (for ensuring assessments are accessible +to all students), and the Equality and Diversity Office; +</p> +<p>- Providing adequate resources, including information technology systems, to support +effective practice (Information Services Directorate, Student Business, Education +Enhancement); and +</p> +<p>- Monitoring implementation of this policy through the Quality Assurance Committee +(Education Enhancement). +</p> +<p>5.2 Faculties are responsible for: overseeing and receiving reports from programme exam +boards; scrutinising external examiners’ reports and responding to them; ensuring +consistency in the implementation of this and other related policies across programmes, +and sharing effective practice within and between faculties. +</p> +<p>5.3 Heads of Department/School are responsible for: instructing colleagues within their +Department/School to note and adhere to the Assessment and Feedback Policy and +Procedures for Recording and Publication of Marks, Procedures on Preparing and +Conducting Exams, and other related policies such as the Policy on the Late Submission of +Coursework, Policy on Extensions to Coursework Submission, the Policy on Moderation +and Double Marking, and Guidance on Marking Assessments in Undergraduate and +Postgraduate Taught courses; and for ensuring that marking of assessments is sufficiently +resourced to enable feedback to be returned within 15 working days of assessment +submission. +</p> +<p>5.4 Programme Exam Boards are responsible for: making sure assessments across a +programme are marked fairly; ensuring university and faculty regulations are adhered to; +and responding to points made by External Examiners. +</p> +<p>5.5 Programme Leaders/Directors are responsible for ensuring a coherent and effective +approach to assessment and feedback is taken across a programme of study. This +approach will involve: +</p> +<p>- Ensuring a range of assessment methods are used throughout a programme of study, +as appropriate to the discipline; +</p> +<p>- Monitoring the intensity and equity of assessments across and between programmes, +with due consideration to joint honours degrees; +</p> +<p>- Designing and reviewing the Assessment Schedule and Exam Timetable within a +programme and requesting amendments where there is assessment/exam bunching; +</p> +<p>- Developing effective moderation and/or double marking procedures, in line with the +Policy on Moderation and Double Marking; +</p> +<p>- Ensuring procedures are in place for monitoring students’ use of the Policies and +Procedures on Extensions to Coursework Submission and Late Submission of +Coursework, as a mechanism for identifying students who may require support; and +</p> +<p>- Ensuring procedures related to assuring fairness in assessment, such as moderation +and the role of External Examiners, are communicated to students. +</p> +<p>5.6 Year Coordinators and/or Module Coordinators are responsible for: +</p> +<p>- Having an overview of summative assessment activities within a programme of study to +avoid, where possible, a concentration of assessment deadlines, including examinations; </p> +<p/> +</div> +<div class="page"> + +<p/> +<p> Assessment and Feedback Policy +</p> +<p> 6 +</p> +<p> +</p> +<p>- Making sure the details of all assessments are provided at the start of a class; +- Ensuring assessment criteria that is aligned to intended learning outcomes and the +</p> +<p>University’s Guidance on Marking Assessments in Undergraduate and Postgraduate +Taught programmes, is transparent and made available to students at the start of a +class; +</p> +<p>- Providing opportunities for students to use feedback prior to the submission of any +subsequent related assessments; +</p> +<p>- Working with students to develop effective practice in assessment and feedback, +including: providing students with opportunities to develop their understanding of the +criteria and standards used to assess their work and supporting students to engage with +and use feedback; +</p> +<p>- Obtaining feedback from students in relation to assessment and feedback for module(s) +for which they are responsible, and for communicating any actions on this feedback to +students; and +</p> +<p>- For undertaking effective moderation and/or double marking procedures. +</p> +<p>5.7 Staff involved in assessing students’ work are responsible for: +</p> +<p>- Designing assessments that effectively facilitate and measure students’ achievement of +intended learning outcomes; +</p> +<p>- Assessing students’ work according to published assessment criteria which are aligned +to intended learning outcomes and the University’s Guidance on Marking Assessments +in Undergraduate and Postgraduate Taught programmes; +</p> +<p>- Providing timely, informative and helpful feedback which enables students to further +improve their learning and performance wherever possible; +</p> +<p>- Informing students when, where and how feedback will be provided; +- Engaging in dialogue with students about assessment and feedback; and +- Continuously reviewing their approaches to assessment and feedback to reflect effective +</p> +<p>practice. +</p> +<p>5.8 Students are expected to be responsible for their own learning through: +</p> +<p>- Understanding the requirements of individual assessments, and actively engaging with +assessment tasks by devoting appropriate time and effort; +</p> +<p>- Developing an understanding of the relationship between intended learning outcomes +and assessment criteria, and standards in their programme of study; +</p> +<p>- Ensuring their academic work is authentic and honestly produced; +- Finding out where, how and when work is submitted and how and when feedback is +</p> +<p>provided; +- Actively engaging, reflecting, and using provided feedback; +- Understanding the academic policies and procedures related to assessment and +</p> +<p>feedback, including Personal Circumstances Procedure, Policy and Procedures for the +Late Submission of Coursework, Policy on Moderation and Double Marking, Policy on +Extensions to the Submission of Coursework, and Compensation and Motivational Merit; +</p> +<p>- Seeking academic support when needed, for example, if feedback needs to be clarified; +and +</p> +<p>- Participating in the development of assessment and feedback practices at class and +programme levels. </p> +<p/> +</div> +<div class="page"> + +<p/> +<p> Assessment and Feedback Policy +</p> +<p> 7 +</p> +<p> +</p> +<p>6. DEFINITIONS OF TERMS + +Assessment The process of measuring the performance of students +</p> +<p>(as in examinations, assignments and other assessable +work) that enables students to monitor their progress +and contributes to their academic results. + +</p> +<p>Assessment criteria + +</p> +<p> +</p> +<p>Specific criteria against which pieces of work are +assessed. An understanding of the criteria must be +shared by markers and students. Feedback should +relate to these criteria. +</p> +<p>Assessment bunching The perception of assessments within a programme of +study being scheduled too closely together. + +</p> +<p>Assessment schedule + +</p> +<p>A timeline of all cohort level assessment. +</p> +<p>Exam bunching Where the scheduling of two exams falls within a 23 +hour period. + +</p> +<p>Feedback Information provided to students on the quality of their +performance in relation to assessment criteria, which +forms the basis of improved student learning. + +</p> +<p>Formative assessment + +</p> +<p>This type of assessment normally has no or low +weighting in the final mark for a module or programme. +The goal of formative assessment is to provide an +opportunity for students to monitor their learning and +provide feedback to teachers that can be used to +review their teaching. + +</p> +<p>Graduate attributes Qualities, skills, dispositions, and understanding that +students are expected to develop. At Strathclyde these +are referred to as the 4 E’s – engaged, enquiring, +enterprising and ethically and globally aware. + +</p> +<p>Intended learning outcomes What the student is expected to be able to do or +demonstrate, in terms of particular knowledge, skills +and understanding, by the end of a module or +programme. + +</p> +<p>Ipsative feedback + +</p> +<p>This type of feedback compares a student’s +performance on a current assessment with +performance on a previous assessment. + +</p> +<p>Moderation The process of checking that assessment criteria are +consistently applied across markers in marking +students’ work. + +</p> +<p>Module The individual components of a programme, normally +worth 20 or 10 credits. + +</p> +<p>Module evaluation The process of obtaining feedback from students on all +aspects of teaching, learning, and assessment within a </p> +<p/> +</div> +<div class="page"> +<p/> +<p> Assessment and Feedback Policy +</p> +<p> 8 +</p> +<p> +</p> +<p>module. + +</p> +<p>Programme The full degree programme leading to an award. + +</p> +<p>Seen double marking Where an assessment is independently marked by two +markers, but where the second marker has access to +the marks or comments of the first marker. + +</p> +<p>Summative assessment Assessment is summative when the grading of an +assessment contributes to the final grade for a class or +course. The aim of summative assessment is to +evaluate students’ attainment of the intended learning +outcomes within a unit of study. + +</p> +<p> +Unseen double marking + +</p> +<p>Where an assessment is independently marked by two +markers who do not have access to the grades or +comments of the other marker. + +</p> +<p>Working days The University’s standard working week is normally +Monday to Friday, covering five working days. Fifteen +working days would normally equate to 21 calendar +days, but may be longer where the period includes +University closure days. +</p> +<p> + </p> +<p/> +</div> +</body> +</html> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Assessment and Feedback Policy + + +
+ +

+

+

+

+

+

+

+

ASSESSMENT AND +FEEDBACK POLICY +

+

+

+

Version No. Description Author Approval Effective Date +

+

1.2 Assessment and +

+

Feedback Policy – +

+

applicable to both +

+

undergraduate and +

+

postgraduate taught +

+

courses +

+

Assessment +

+

and +

+

Feedback +

+

Working +

+

Group +

+

Senate From academic +

+

year 2019-20 +

+

Version 1.2 +

+

+ +the place of useful learning +The University of Strathclyde is a charitable body, registered in +Scotland, number SC015263 +

+

+

+

+ +

+

+

+
+ +

+

Assessment and Feedback Policy +

+

1 +

+

+

+

1. RATIONALE +

+

In higher education, “assessment” describes any process that involves the evaluation or +appraisal of a student’s knowledge, understanding, skills, attitudes or abilities. In line with the +QAA UK Quality Code for Higher Education, assessment is taken to be an integral component +of teaching and learning, and serves multiple purposes. In addition to enabling evaluation and +measurement of students’ learning, effective assessment shapes and enhances student +learning. Assessments should therefore be designed to facilitate students’ attainment of +intended learning outcomes, and permit the measurement of such attainments against explicit +criteria. A holistic approach to assessment at course level can also support the development of +distinctive Strathclyde graduate attributes: graduates that are engaged, enquiring, enterprising, +and ethically and globally aware. Recent publications on assessment and feedback in higher +education suggest a ‘transformation’ is required to ensure that assessment and feedback +practices enable students to develop such attributes and the skills needed to be lifelong +learners in the 21st century.1 +

+

This approach recognises that assessment is central to learning and teaching, and is not +designed solely to measure student learning. Also essential to enhancing learning, is the +provision of continuous feedback to students on their learning; it is recognised that feedback +takes different forms (e.g. replies to posts on a discussion forum), but in relation to assessment, +useful feedback is feedback that is specific in informing learners the extent to which they have +met published assessment criteria, and explains to them what they need to do to improve. +Ensuring feedback is clear, specific, and adopts a supportive tone is most likely to foster +student engagement with feedback. The role of feedback in effectively supporting student +learning should be recognised at the assessment design stage, where factors such as the +timing of the release of feedback and the scheduling of subsequent assessments are +considered (particularly where feedback can be used by students to improve the development +of future work), as well as the provision of opportunities for students to clarify feedback. +

+

The University expects that feedback will normally be returned to students within 15 working +days of assessment submission2. The University’s VLE, Myplace, provides a platform for the +electronic return of grades and feedback, and where permitted by the nature of an assessment, +staff are expected to implement the submission of assessments online through Myplace, in +order to promote effectiveness and efficiency in each stage of the assessment and feedback +process. In addition, Myplace should be used to manage assessments that are submitted offline +in order to implement the Policy and Procedure on Extensions to Coursework Submission and +Procedures for the Recording and Publication of Marks. Staff should also be cognisant of +specific issues relevant to equality, diversity and inclusion in designing assessments and +providing feedback, which may affect particular groups of students.3 +

+

+1 For example: Boud, D. & Falachikov, N. (Eds.). (2007). Rethinking Assessment in Higher Education. Oxon: +Routledge; Higher Education Academy. (2012). A Marked Improvement. Transforming assessment in higher +education. York: The Higher Education Academy; Merry, S., Price, M., Carless, D. & Taras, M. (Eds.). (2013). +Reconceptualising Feedback in Higher Education. Developing Dialogue with Students. Oxon: Routledge; +Carless, D. (2015). Excellence in university assessment: learning from award-winning practice. London: +Routledge. +2 Exceptions to this are dissertations or assessments with credit weightings of 40 credits or higher, and where +double marking is required. (See the Policy on Moderation and Double Marking). +3 Staff are advised to consult the University’s equality impact assessment information where relevant.

+

+

+ +
+
+ +

+

Assessment and Feedback Policy +

+

2 +

+

+

+

Previous work on assessment at Strathclyde (through the Re-Engineering Assessment +Practices4 and Peer Evaluation in Education Review5 projects, together with the work on Peer +and Community Personal Development Planning), have emphasised the importance of +providing students with opportunities to self- and peer-assess, and for students to develop the +capacity to evaluate, reflect on6 and manage their own learning through the effective use of +feedback. Published research also highlights the importance of developing students’ +understanding of the criteria and standards by which their work is assessed, and suggests that +providing students with the opportunity to analyse and discuss the qualities of carefully selected +exemplars is an effective learning activity to achieve this7. It is widely recognised that these will, +in the long-term, help develop attitudes and skills that foster engagement and independence in +learning. + +The aim of the current policy is to develop an institution-wide approach to assessment and +feedback that enhances the effectiveness of assessment in aiding students to achieve the +necessary knowledge and skills described in a class or programme’s intended learning +outcomes. The policy is underpinned by principles of assessment and feedback (outlined in +section 3) that reflect this aim and a commitment to ensuring our assessment and feedback +practices are fair, transparent, and continuously reviewed. In short, to ensure they continue to +be fit for purpose. This policy should be adhered to across the University. The implementation +of the policy will be monitored by the Quality Assurance Committee. +

+

+2. SCOPE +

+

This policy applies to all undergraduate and postgraduate taught programmes offered by the +University. This policy should be read in conjunction with other relevant University policies and +procedures. The University website contains policies and procedures on a range of +assessment-related matters, including: ensuring equality, guidance for staff on supporting +students with a disability, guides for staff marking assessments in undergraduate and +postgraduate taught courses, guidance for students and staff on avoiding plagiarism, guidance +for staff in dealing with possible cases of plagiarism, policies on classifying honours degrees +and other degree awards, and on awarding motivational merit and distinction, guidance on the +compensation scheme, the role of external examiners in taught courses, the procedure for +dealing with student discipline cases, guidance for staff on the appeals procedure and +accounting for students’ personal circumstances. +

+

+

+

+

+

+ +4 REAP Project, accessed at http://www.reap.ac.uk/ +5 PEER Project, accessed from: http://www.reap.ac.uk/PEER.aspx. The site contains a PEER toolkit +containing guidance for staff on using peer review. +6 Winstone, N. & Nash. R. (2016). The Developing Engagement with Feedback Toolkit (DEFT). The Higher +Education Academy. https://www.heacademy.ac.uk/knowledge-hub/developing-engagement-feedback-toolkit- +deft +7 Carless, D. & Boud, D. (2018): The development of student feedback literacy: enabling uptake of feedback, +Assessment & Evaluation in Higher Education, DOI: 10.1080/02602938.2018.1463354

+

+

+ + + + +
+
+ +

+

Assessment and Feedback Policy +

+

3 +

+

+

+

3. ASSESSMENT AND FEEDBACK PRINCIPLES + +

+

These principles have been widely consulted on across the University, and must be adhered to +throughout the institution. +

+

PRINCIPLE 1. ASSESSMENT AND FEEDBACK PRACTICES PROMOTE EFFECTIVE +STUDENT LEARNING +

+

1.1 Assessment and feedback activities are designed to foster student engagement, to +support and measure students’ attainment of knowledge, understanding, and +transferable skills. +

+

1.2 A range of assessment methods are used, increasing in complexity across a +programme, and taking into consideration student and staff workloads. +

+

1.3 Assessment and feedback practices align with intended learning outcomes and +assessment criteria, and provide opportunities for students to be active participants in +the process, including opportunities for students to discuss feedback. +

+

1.4 Timely, constructive, and supportive feedback helps students understand the extent to +which they have fulfilled the assessment criteria, and supports students to use their +feedback in the development of subsequent work. +

+

1.5 The timing of assessments and return of feedback should be scheduled to enable +students to use feedback to inform their approach to subsequent assessments. The +release of feedback should consider the ability of students to gain further feedback and +support, and avoid release in close proximity to periods where the university is closed +

+

1.6 Where examinations are used, students should receive at least general feedback soon +after the examination. Students who wish to view their exam scripts following the +provision of general feedback should be facilitated to do so under supervision of staff. +

+

PRINCIPLE 2. ASSESSMENT AND FEEDBACK PRACTICES ARE APPROPRIATE, FAIR, +AND TRANSPARENT +

+

2.1 Assessment tasks are appropriate to disciplinary and/or professional contexts. +2.2 Assessment applies rigorous academic standards related to and across, the discipline(s) +

+

or professional context and is based on clearly defined assessment criteria. +2.3 Assessment grading and feedback is based solely on students’ achievement against +

+

criteria and standards. +2.4 Assessment and feedback practices are fair, inclusive and accessible to all students. +

+

PRINCIPLE 3. ASSESSMENT AND FEEDBACK PRACTICES ARE CLEARLY +COMMUNICATED TO STUDENTS AND STAFF +

+

3.1 Students must be made aware at the beginning of a class of the details of assessments, +including the purpose, weighting, and timing of assessment. +

+

3.2 Students must be made aware at the beginning of a class of the nature and timing of +feedback. Where unexpected problems cause a delay in the return of feedback within 15 +working days, an explanation will be provided to students, along with a new date for the +return of feedback. +

+

3.3 All students and staff are made aware at the start of a class of the criteria and standards +used to assess and provide feedback on students’ work.

+

+

+
+ +

+

Assessment and Feedback Policy +

+

4 +

+

+

+

3.4 Students must be made aware of relevant policies and procedures around academic +honesty in assessment, and of procedures associated with personal circumstances, +extensions to the deadlines of coursework, and late submission of coursework. +

+

3.5 There are opportunities for students and staff to engage in a dialogue around +assessment and feedback, including opportunities for students to clarify feedback. +

+

3.6 In each programme the processes for marking, moderation, and feedback are +appropriate and fair and are explained to students and staff. +

+

PRINCIPLE 4. ASSESSMENT AND FEEDBACK PRACTICES ARE CONTINUOUSLY +REVIEWED +

+

4.1 The timing of assessment activities at cohort level, including the timing of examinations, +are monitored and reviewed, in order to minimise bunching. +

+

4.2 Assessment and feedback activities, and the outcomes of assessment, are reviewed via +the class and course approval and/or review processes, and external examining +processes. +

+

4.3 Assessment and feedback activities are continuously reviewed to ensure effective +alignment with a programme’s intended learning outcomes and graduate attributes. +

+

4.4 Opportunities to develop effective practice and innovation in assessment and feedback +are available to all staff involved in assessment. +

+

+4. ANONYMOUS MARKING +

+

4.1 Anonymous marking (when the identity of a student is not known to a marker and/or exam +board) will be used for all assessments wherever possible and appropriate, including formal +written examinations at both undergraduate and postgraduate levels. +

+

4.2 Where anonymous marking is not possible, for example where an alternative method of +assessment has been used for a student with a disability, additional scrutiny by a second +marker may be appropriate. +

+

4.3 Other exceptions will be where the mode of assessment (e.g., presentations or oral +examinations) prevents anonymity, or where there is pedagogical justification (e.g., the use +of ipsative feedback). +

+

4.4 Double marking is effective practice for assessments that are not marked anonymously and +for dissertations, or comparable assessments. +

+

+5. RESPONSIBILITIES FOR IMPLEMENTATION +

+

5.1 The University assumes responsibility for: +- Providing staff with opportunities to develop effective practice in assessment and +

+

feedback through the Organisational Staff and Development Unit (Strathclyde Teaching +Excellence Programme) Education Enhancement (the University’s VLE, Myplace offers +various tools to support effective practice in assessment and feedback, including +handling peer review/assessment activities, online assessment submission and return of +feedback, and enabling efficient communication with different student and staff groups; +the Sharing Practice in Enhancing Learning and Teaching (SPELT) platform, and

+

+

+ + +
+
+ +

+

Assessment and Feedback Policy +

+

5 +

+

+

+

TESTA), the Disability and Wellbeing Service (for ensuring assessments are accessible +to all students), and the Equality and Diversity Office; +

+

- Providing adequate resources, including information technology systems, to support +effective practice (Information Services Directorate, Student Business, Education +Enhancement); and +

+

- Monitoring implementation of this policy through the Quality Assurance Committee +(Education Enhancement). +

+

5.2 Faculties are responsible for: overseeing and receiving reports from programme exam +boards; scrutinising external examiners’ reports and responding to them; ensuring +consistency in the implementation of this and other related policies across programmes, +and sharing effective practice within and between faculties. +

+

5.3 Heads of Department/School are responsible for: instructing colleagues within their +Department/School to note and adhere to the Assessment and Feedback Policy and +Procedures for Recording and Publication of Marks, Procedures on Preparing and +Conducting Exams, and other related policies such as the Policy on the Late Submission of +Coursework, Policy on Extensions to Coursework Submission, the Policy on Moderation +and Double Marking, and Guidance on Marking Assessments in Undergraduate and +Postgraduate Taught courses; and for ensuring that marking of assessments is sufficiently +resourced to enable feedback to be returned within 15 working days of assessment +submission. +

+

5.4 Programme Exam Boards are responsible for: making sure assessments across a +programme are marked fairly; ensuring university and faculty regulations are adhered to; +and responding to points made by External Examiners. +

+

5.5 Programme Leaders/Directors are responsible for ensuring a coherent and effective +approach to assessment and feedback is taken across a programme of study. This +approach will involve: +

+

- Ensuring a range of assessment methods are used throughout a programme of study, +as appropriate to the discipline; +

+

- Monitoring the intensity and equity of assessments across and between programmes, +with due consideration to joint honours degrees; +

+

- Designing and reviewing the Assessment Schedule and Exam Timetable within a +programme and requesting amendments where there is assessment/exam bunching; +

+

- Developing effective moderation and/or double marking procedures, in line with the +Policy on Moderation and Double Marking; +

+

- Ensuring procedures are in place for monitoring students’ use of the Policies and +Procedures on Extensions to Coursework Submission and Late Submission of +Coursework, as a mechanism for identifying students who may require support; and +

+

- Ensuring procedures related to assuring fairness in assessment, such as moderation +and the role of External Examiners, are communicated to students. +

+

5.6 Year Coordinators and/or Module Coordinators are responsible for: +

+

- Having an overview of summative assessment activities within a programme of study to +avoid, where possible, a concentration of assessment deadlines, including examinations;

+

+

+
+ +

+

Assessment and Feedback Policy +

+

6 +

+

+

+

- Making sure the details of all assessments are provided at the start of a class; +- Ensuring assessment criteria that is aligned to intended learning outcomes and the +

+

University’s Guidance on Marking Assessments in Undergraduate and Postgraduate +Taught programmes, is transparent and made available to students at the start of a +class; +

+

- Providing opportunities for students to use feedback prior to the submission of any +subsequent related assessments; +

+

- Working with students to develop effective practice in assessment and feedback, +including: providing students with opportunities to develop their understanding of the +criteria and standards used to assess their work and supporting students to engage with +and use feedback; +

+

- Obtaining feedback from students in relation to assessment and feedback for module(s) +for which they are responsible, and for communicating any actions on this feedback to +students; and +

+

- For undertaking effective moderation and/or double marking procedures. +

+

5.7 Staff involved in assessing students’ work are responsible for: +

+

- Designing assessments that effectively facilitate and measure students’ achievement of +intended learning outcomes; +

+

- Assessing students’ work according to published assessment criteria which are aligned +to intended learning outcomes and the University’s Guidance on Marking Assessments +in Undergraduate and Postgraduate Taught programmes; +

+

- Providing timely, informative and helpful feedback which enables students to further +improve their learning and performance wherever possible; +

+

- Informing students when, where and how feedback will be provided; +- Engaging in dialogue with students about assessment and feedback; and +- Continuously reviewing their approaches to assessment and feedback to reflect effective +

+

practice. +

+

5.8 Students are expected to be responsible for their own learning through: +

+

- Understanding the requirements of individual assessments, and actively engaging with +assessment tasks by devoting appropriate time and effort; +

+

- Developing an understanding of the relationship between intended learning outcomes +and assessment criteria, and standards in their programme of study; +

+

- Ensuring their academic work is authentic and honestly produced; +- Finding out where, how and when work is submitted and how and when feedback is +

+

provided; +- Actively engaging, reflecting, and using provided feedback; +- Understanding the academic policies and procedures related to assessment and +

+

feedback, including Personal Circumstances Procedure, Policy and Procedures for the +Late Submission of Coursework, Policy on Moderation and Double Marking, Policy on +Extensions to the Submission of Coursework, and Compensation and Motivational Merit; +

+

- Seeking academic support when needed, for example, if feedback needs to be clarified; +and +

+

- Participating in the development of assessment and feedback practices at class and +programme levels.

+

+

+
+ +

+

Assessment and Feedback Policy +

+

7 +

+

+

+

6. DEFINITIONS OF TERMS + +Assessment The process of measuring the performance of students +

+

(as in examinations, assignments and other assessable +work) that enables students to monitor their progress +and contributes to their academic results. + +

+

Assessment criteria + +

+

+

+

Specific criteria against which pieces of work are +assessed. An understanding of the criteria must be +shared by markers and students. Feedback should +relate to these criteria. +

+

Assessment bunching The perception of assessments within a programme of +study being scheduled too closely together. + +

+

Assessment schedule + +

+

A timeline of all cohort level assessment. +

+

Exam bunching Where the scheduling of two exams falls within a 23 +hour period. + +

+

Feedback Information provided to students on the quality of their +performance in relation to assessment criteria, which +forms the basis of improved student learning. + +

+

Formative assessment + +

+

This type of assessment normally has no or low +weighting in the final mark for a module or programme. +The goal of formative assessment is to provide an +opportunity for students to monitor their learning and +provide feedback to teachers that can be used to +review their teaching. + +

+

Graduate attributes Qualities, skills, dispositions, and understanding that +students are expected to develop. At Strathclyde these +are referred to as the 4 E’s – engaged, enquiring, +enterprising and ethically and globally aware. + +

+

Intended learning outcomes What the student is expected to be able to do or +demonstrate, in terms of particular knowledge, skills +and understanding, by the end of a module or +programme. + +

+

Ipsative feedback + +

+

This type of feedback compares a student’s +performance on a current assessment with +performance on a previous assessment. + +

+

Moderation The process of checking that assessment criteria are +consistently applied across markers in marking +students’ work. + +

+

Module The individual components of a programme, normally +worth 20 or 10 credits. + +

+

Module evaluation The process of obtaining feedback from students on all +aspects of teaching, learning, and assessment within a

+

+

+
+

+

Assessment and Feedback Policy +

+

8 +

+

+

+

module. + +

+

Programme The full degree programme leading to an award. + +

+

Seen double marking Where an assessment is independently marked by two +markers, but where the second marker has access to +the marks or comments of the first marker. + +

+

Summative assessment Assessment is summative when the grading of an +assessment contributes to the final grade for a class or +course. The aim of summative assessment is to +evaluate students’ attainment of the intended learning +outcomes within a unit of study. + +

+

+Unseen double marking + +

+

Where an assessment is independently marked by two +markers who do not have access to the grades or +comments of the other marker. + +

+

Working days The University’s standard working week is normally +Monday to Friday, covering five working days. Fifteen +working days would normally equate to 21 calendar +days, but may be longer where the period includes +University closure days. +

+

+

+

+

+ + +array(14) { + ["areaid"]=> + string(19) "mod_folder-activity" + ["id"]=> + string(33) "mod_folder-activity-1-solrfile287" + ["itemid"]=> + int(1) + ["title"]=> + string(34) "Assessment_and_Feedback_Policy.pdf" + ["contextid"]=> + int(268) + ["courseid"]=> + int(4) + ["owneruserid"]=> + int(0) + ["modified"]=> + string(20) "2024-03-04T17:17:08Z" + ["type"]=> + int(2) + ["solr_filegroupingid"]=> + string(21) "mod_folder-activity-1" + ["solr_fileid"]=> + string(3) "287" + ["solr_filecontenthash"]=> + string(40) "6de274633203c03b996b248e841f2a1f17cdb537" + ["solr_fileindexstatus"]=> + int(1) + ["solr_vector"]=> + array(5) { + [0]=> + float(0.0053587136790156364) + [1]=> + float(-0.00049990462139248848) + [2]=> + float(0.038883671164512634) + [3]=> + float(-0.0030010775662958622) + [4]=> + float(-0.0090081822127103806) + } +} diff --git a/search/cli/result2.txt b/search/cli/result2.txt new file mode 100644 index 000000000000..21e64b0f5bf0 --- /dev/null +++ b/search/cli/result2.txt @@ -0,0 +1,64 @@ +Running full reindex of site +============================ +Processing area: Text block content + No new documents to index. +Processing area: Courses + Processed 5 records containing 5 documents (1 batch), in 0.2 seconds. +Processing area: Course custom fields + No new documents to index. +Processing area: Course sections + No new documents to index. +Processing area: Messages - received + No new documents to index. +Processing area: Messages - sent + No new documents to index. +Processing area: Course Teacher + No new documents to index. +Processing area: Users + Processed 103 records containing 103 documents (2 batches), in 1 seconds. +Processing area: Assignment - activity information + Processed 22 records containing 22 documents (1 batch), in 0.3 seconds. +Processing area: Attendance - activity information + Processed 1 records containing 1 documents, in 0.1 seconds. +Processing area: BigBlueButton - activity information + No new documents to index. +Processing area: BigBlueButton - tags information + No new documents to index. +Processing area: Book - resource information + No new documents to index. +Processing area: Book - chapters + No new documents to index. +Processing area: Chat - activity information + No new documents to index. +Processing area: Choice - activity information + No new documents to index. +Processing area: Database - activity information + No new documents to index. +Processing area: Database - entries + No new documents to index. +Processing area: Feedback - activity information + No new documents to index. +Processing area: Folder +++ http://172.22.0.6:8983/solr/moodle/update/extract?wt=xml&uprefix=ignored_&captureAttr=true&fmap.content=solr_filecontent&fmap.media_white_point=ignored_mwp&fmap.media_black_point=ignored_mbp&fmap.areaid=ignored_areaid&literal.mdltmp_areaid=mod_folder-activity&fmap.mdltmp_areaid=areaid&fmap.id=ignored_id&literal.mdltmp_id=mod_folder-activity-1-solrfile287&fmap.mdltmp_id=id&fmap.itemid=ignored_itemid&literal.mdltmp_itemid=1&fmap.mdltmp_itemid=itemid&fmap.title=ignored_title&literal.mdltmp_title=Assessment_and_Feedback_Policy.pdf&fmap.mdltmp_title=title&fmap.contextid=ignored_contextid&literal.mdltmp_contextid=268&fmap.mdltmp_contextid=contextid&fmap.courseid=ignored_courseid&literal.mdltmp_courseid=4&fmap.mdltmp_courseid=courseid&fmap.owneruserid=ignored_owneruserid&literal.mdltmp_owneruserid=0&fmap.mdltmp_owneruserid=owneruserid&fmap.modified=ignored_modified&literal.mdltmp_modified=2024-03-04T17%3A17%3A08Z&fmap.mdltmp_modified=modified&fmap.type=ignored_type&literal.mdltmp_type=2&fmap.mdltmp_type=type&fmap.solr_filegroupingid=ignored_solr_filegroupingid&literal.mdltmp_solr_filegroupingid=mod_folder-activity-1&fmap.mdltmp_solr_filegroupingid=solr_filegroupingid&fmap.solr_fileid=ignored_solr_fileid&literal.mdltmp_solr_fileid=287&fmap.mdltmp_solr_fileid=solr_fileid&fmap.solr_filecontenthash=ignored_solr_filecontenthash&literal.mdltmp_solr_filecontenthash=6de274633203c03b996b248e841f2a1f17cdb537&fmap.mdltmp_solr_filecontenthash=solr_filecontenthash&fmap.solr_fileindexstatus=ignored_solr_fileindexstatus&literal.mdltmp_solr_fileindexstatus=1&fmap.mdltmp_solr_fileindexstatus=solr_fileindexstatus&fmap.solr_vector=ignored_solr_vector&literal.mdltmp_solr_vector&fmap.mdltmp_solr_vector=solr_vector&resource.name=Assessment_and_Feedback_Policy.pdf&extractOnly=true ++ +* line 139 of /search/engine/solrrag/classes/engine.php: call to debugging() +* line 970 of /search/engine/solr/classes/engine.php: call to search_solrrag\engine->add_stored_file() +* line 776 of /search/engine/solr/classes/engine.php: call to search_solr\engine->process_document_files() +* line 318 of /search/classes/engine.php: call to search_solr\engine->add_document_batch() +* line 1283 of /search/classes/manager.php: call to core_search\engine->add_documents() +* line 108 of /search/cli/indexer.php: call to core_search\manager->index() +++ Got SOLR update/extract response ++ +* line 181 of /search/engine/solrrag/classes/engine.php: call to debugging() +* line 970 of /search/engine/solr/classes/engine.php: call to search_solrrag\engine->add_stored_file() +* line 776 of /search/engine/solr/classes/engine.php: call to search_solr\engine->process_document_files() +* line 318 of /search/classes/engine.php: call to search_solr\engine->add_document_batch() +* line 1283 of /search/classes/manager.php: call to core_search\engine->add_documents() +* line 108 of /search/cli/indexer.php: call to core_search\manager->index() +++ Using vector field solr_vector_1356 ++ +* line 205 of /search/engine/solrrag/classes/engine.php: call to debugging() +* line 970 of /search/engine/solr/classes/engine.php: call to search_solrrag\engine->add_stored_file() +* line 776 of /search/engine/solr/classes/engine.php: call to search_solr\engine->process_document_files() +* line 318 of /search/classes/engine.php: call to search_solr\engine->add_document_batch() +* line 1283 of /search/classes/manager.php: call to core_search\engine->add_documents() +* line 108 of /search/cli/indexer.php: call to core_search\manager->index() +bool(false) +Goodbye \ No newline at end of file diff --git a/search/engine/solrrag/classes/ai/aiprovider.php b/search/engine/solrrag/classes/ai/aiprovider.php index 871dda8c6891..a65e7f59e06d 100644 --- a/search/engine/solrrag/classes/ai/aiprovider.php +++ b/search/engine/solrrag/classes/ai/aiprovider.php @@ -39,14 +39,19 @@ public function use_for_query():bool { } public function embed_documents(array $documents) { // Go send the documents off to a back end and then return array of each document's vectors. - print_r($documents); - return [ - [0.0053587136790156364, - -0.0004999046213924885, - 0.038883671164512634, - -0.003001077566295862, - -0.00900818221271038] - ]; + // But for the minute generate an array of fake vectors of a specific length. + $vectors = []; + foreach ($documents as $document) { + $vectors[] = $this->fake_vector(1356); + } + return $vectors; + } + private function fake_vector($length) { + $vector = []; + for ($i = 0; $i < $length; $i++) { + $vector[] = rand(0, 1); + } + return $vector; } /** @@ -54,14 +59,8 @@ public function embed_documents(array $documents) { * @return array */ public function embed_query($document): array { - print_r($document); // Send document to back end and return the vector - return [0.0053587136790156364, - -0.0004999046213924885, - 0.038883671164512634, - -0.003001077566295862, - -0.00900818221271038 - ]; + return $this->fake_vector(1356); } /** * We're overriding this whilst we don't have a real DB table. @@ -81,4 +80,4 @@ public static function get_records($filters = array(), $sort = '', $order = 'ASC array_push($records, $fake); return $records; } -} \ No newline at end of file +} diff --git a/search/engine/solrrag/classes/document.php b/search/engine/solrrag/classes/document.php index 5c3ada09a5ec..662c69ad9182 100644 --- a/search/engine/solrrag/classes/document.php +++ b/search/engine/solrrag/classes/document.php @@ -31,11 +31,15 @@ class document extends \search_solr\document { 'indexed' => true, 'mainquery' => true ), - 'solr_vector' => [ - 'type' => 'knn_vector_10', + 'solr_vector_1356' => [ + 'type' => 'knn_vector_1356', // this field def seems to be related to the size of the LLM embedding too :-( + 'stored' => true, + 'indexed' => true + ], + 'solr_vector_3072' => [ + 'type' => 'knn_vector_3072', // this field def seems to be related to the size of the LLM embedding too :-( 'stored' => true, 'indexed' => true - ] ); @@ -74,4 +78,4 @@ public function export_file_for_engine($file) { public function fetch_document_contents() { } -} \ No newline at end of file +} diff --git a/search/engine/solrrag/classes/engine.php b/search/engine/solrrag/classes/engine.php index b0cd9aaa2b65..b1201a375c03 100644 --- a/search/engine/solrrag/classes/engine.php +++ b/search/engine/solrrag/classes/engine.php @@ -74,16 +74,15 @@ public function is_server_ready() */ protected function add_stored_file($document, $storedfile) { + $embeddings = []; $filedoc = $document->export_file_for_engine($storedfile); /** - * Should we event attempt to get vectors. + * Should we even attempt to get vectors. */ if (!is_null($this->embeddingprovider)) { // garnish $filedoc with the embedding vector. It would be nice if this could be done // via the export_file_for_engine() call above, that has no awareness of the engine. $embeddings = $this->embeddingprovider->embed_documents([$filedoc]); - $filedoc['solr_vector'] = "[". implode(",", $embeddings[0]) ."]"; - print_r($filedoc); } else { // potentially warn that selected provider can't be used for // generating embeddings for RAG. @@ -131,14 +130,18 @@ protected function add_stored_file($document, $storedfile) // This sets the true filename for Tika. $url->param('resource.name', $storedfile->get_filename()); - + $url->param('extractOnly', "true"); +// $url->param("xpath", "/xhtml:html/xhtml:body/xhtml:div//node()"); // A giant block of code that is really just error checking around the curl request. try { $requesturl = $url->out(false); + + debugging($requesturl); // We have to post the file directly in binary data (not using multipart) to avoid // Solr bug SOLR-15039 which can cause incorrect data when you use multipart upload. // Note this loads the whole file into memory; see limit in file_is_indexable(). - $result = $curl->post($url->out(false), $storedfile->get_content()); + $result = $curl->post($requesturl, $storedfile->get_content()); + //$url->out(false) $code = $curl->get_errno(); $info = $curl->get_info(); @@ -175,6 +178,34 @@ protected function add_stored_file($document, $storedfile) debugging($message, DEBUG_DEVELOPER); } else { // The document was successfully indexed. + debugging("Got SOLR update/extract response"); + preg_match('/(?.*)<\/str>/imsU', $result, $streamcontent); + + if ($streamcontent[1]!== 0) { + $xmlcontent = html_entity_decode($streamcontent[1]); + $xml = simplexml_load_string($xmlcontent); + $filedoc['content'] = (string)$xml->body->asXML(); + $metadata = $xml->head->meta; + foreach($metadata as $meta) { + $name = (string)$meta['name']; + $content = (string)$meta['content']; + if ($content != null) { + $filedoc[$name] = $content; + } else { + $filedoc[$name] = ""; + + } + } + } + if (count($embeddings) > 0) { + $vector = $embeddings[0]; + $vlength = count($vector); + $vectorfield = "solr_vector_" . $vlength; + $filedoc[$vectorfield] = $vector; + debugging("Using vector field $vectorfield"); + } + $this->add_solr_document($filedoc); + exit("Goodbye"); return; } } else { @@ -188,9 +219,10 @@ protected function add_stored_file($document, $storedfile) // There was an error, but we are not tracking per-file success, so we just continue on. debugging('Unknown exception while indexing file "' . $storedfile->get_filename() . '".', DEBUG_DEVELOPER); } - + // If we get here, the document was not indexed due to an error. So we will index just the base info without the file. $filedoc['solr_fileindexstatus'] = document::INDEXED_FILE_ERROR; + $this->add_solr_document($filedoc); @@ -198,4 +230,32 @@ protected function add_stored_file($document, $storedfile) // with talking to solr. //return parent::add_stored_file($document, $storedfile); } -} \ No newline at end of file + + + protected function create_solr_document(array $doc): \SolrInputDocument { + $solrdoc = new \SolrInputDocument(); + + // Replace underlines in the content with spaces. The reason for this is that for italic + // text, content_to_text puts _italic_ underlines. Solr treats underlines as part of the + // word, which means that if you search for a word in italic then you can't find it. + if (array_key_exists('content', $doc)) { + $doc['content'] = self::replace_underlines($doc['content']); + } + + // Set all the fields. + foreach ($doc as $field => $value) { + if (is_null($value)) { + continue; + } + if (is_array($value)) { + foreach ($value as $v) { + $solrdoc->addField($field, $v); + } + continue; + } + $solrdoc->addField($field, $value); + } + + return $solrdoc; + } +} From bef2f3bf4879288c3daab46fce370df035d74f6e Mon Sep 17 00:00:00 2001 From: Michael hughes Date: Wed, 6 Mar 2024 23:57:17 +0000 Subject: [PATCH 04/21] Got basic Open AI Embeddings being generated and persisted --- search/engine/solrrag/classes/ai/aiclient.php | 92 ++++++++++++++++++ .../engine/solrrag/classes/ai/aiprovider.php | 94 +++++++++++++------ search/engine/solrrag/classes/ai/api.php | 6 +- search/engine/solrrag/classes/engine.php | 39 ++++---- 4 files changed, 178 insertions(+), 53 deletions(-) create mode 100644 search/engine/solrrag/classes/ai/aiclient.php diff --git a/search/engine/solrrag/classes/ai/aiclient.php b/search/engine/solrrag/classes/ai/aiclient.php new file mode 100644 index 000000000000..4f9da6ec3dff --- /dev/null +++ b/search/engine/solrrag/classes/ai/aiclient.php @@ -0,0 +1,92 @@ +libdir.'/filelib.php'); +/** + * Base client for AI providers that uses simple http request. + */ +class AIClient extends \curl { + private $provider; + public function __construct( + \core\ai\AIProvider $provider + ) { + $this->provider = $provider; + $settings = []; + parent::__construct($settings); + $this->setHeader('Authorization: Bearer ' . $this->provider->get('apikey')); + $this->setHeader('Content-Type: application/json'); + } + + public function get_embeddings_url(): string { + return $this->provider->get('baseurl') . $this->provider->get('embeddings'); + } + + public function get_chat_completions_url(): string { + return $this->provider->get('baseurl') . $this->provider->get('completions'); + } + + /** + * @param $document + * @return array + */ + public function embed_query($content): array { + // Send document to back end and return the vector + $usedptokens = $this->provider->get_usage('prompt_tokens'); + $totaltokens = $this->provider->get_usage('total_tokens'); + mtrace("Prompt tokens: $usedptokens. Total tokens: $totaltokens"); + $params = [ + "input" => htmlentities($content), // TODO need to do some length checking here! + "model" => $this->provider->get('embeddingmodel') + ]; + $params = json_encode($params); +// var_dump($this->get_embeddings_url()); + + $rawresult = $this->post($this->get_embeddings_url(), $params); +// var_dump($rawresult); + $result = json_decode($rawresult, true); + var_dump($result); + $usage = $result['usage']; + $this->provider->increment_prompt_usage($usage['prompt_tokens']); + $this->provider->increment_total_tokens($usage['total_tokens']); + mtrace("Used Prompt tokens: {$usage['prompt_tokens']}. Total tokens: {$usage['total_tokens']}"); + $data = $result['data']; + foreach($data as $d) { + if ($d['object'] == "embedding") { + return $d['embedding']; + } + } + $usedptokens = $this->provider->get_usage('prompt_tokens'); + $totaltokens = $this->provider->get_usage('total_tokens'); + mtrace("Total Used: Prompt tokens: $usedptokens. Total tokens: $totaltokens"); + return []; + } + public function embed_documents(array $documents) { + // Go send the documents off to a back end and then return array of each document's vectors. + // But for the minute generate an array of fake vectors of a specific length. + $embeddings = []; + foreach($documents as $doc) { + $embeddings[] = $this->embed_query($doc); + } + return $embeddings; + } + public function fake_embed(array $documents) { + $vectors = []; + foreach ($documents as $document) { + $vectors[] = $this->fake_vector(1356); + } + return $vectors; + } + public function complete($query) { + + + } + private function fake_vector($length) { + $vector = []; + for ($i = 0; $i < $length; $i++) { + $vector[] = rand(0, 1); + } + return $vector; + } + + + +} diff --git a/search/engine/solrrag/classes/ai/aiprovider.php b/search/engine/solrrag/classes/ai/aiprovider.php index a65e7f59e06d..afd607f8867b 100644 --- a/search/engine/solrrag/classes/ai/aiprovider.php +++ b/search/engine/solrrag/classes/ai/aiprovider.php @@ -6,14 +6,7 @@ class AIProvider extends persistent { // Ultimately this would extend a persistent. - public function __construct($id = 0, stdClass $record = null) { - if ($id > 0) { - $this->raw_set('id', $id); - $this->raw_set('name', "Fake AI Provider"); - $this->raw_set('allowembeddings', true); - $this->raw_set('allowquery', true); - } - } + protected static function define_properties() { @@ -21,11 +14,29 @@ protected static function define_properties() 'name' => [ 'type' => PARAM_TEXT ], + 'apikey' =>[ + 'type' => PARAM_ALPHANUMEXT + ], 'allowembeddings' => [ 'type' => PARAM_BOOL ], 'allowquery' => [ 'type' => PARAM_BOOL + ], + 'baseurl' => [ + 'type' => PARAM_URL + ], + 'embeddings' => [ + 'type' => PARAM_URL + ], + 'embeddingmodel' => [ + 'type' => PARAM_ALPHANUMEXT + ], + 'completions' => [ + 'type' => PARAM_URL + ], + 'completionmodel' => [ + 'type' => PARAM_ALPHANUMEXT ] ]; } @@ -37,31 +48,43 @@ public function use_for_embeddings(): bool { public function use_for_query():bool { return $this->get('allowquery'); } - public function embed_documents(array $documents) { - // Go send the documents off to a back end and then return array of each document's vectors. - // But for the minute generate an array of fake vectors of a specific length. - $vectors = []; - foreach ($documents as $document) { - $vectors[] = $this->fake_vector(1356); - } - return $vectors; + public function get_usage($type) { + return "-"; + $key = [ + '$type', + $this->get('id'), + $this->get('apikey'), + ]; + $current = get_config('ai', $key); + return $current; } - private function fake_vector($length) { - $vector = []; - for ($i = 0; $i < $length; $i++) { - $vector[] = rand(0, 1); - } - return $vector; + public function increment_prompt_usage($change) { + return; + $key = [ + 'prompttokens', + $this->get('id'), + $this->get('apikey'), + ]; + $key = implode("_", $key); + $current = get_config('ai', $key); + $new = $current + $change; + set_config($key, $new, 'ai'); } - - /** - * @param $document - * @return array - */ - public function embed_query($document): array { - // Send document to back end and return the vector - return $this->fake_vector(1356); + public function increment_total_tokens($change) { + return; + $key = [ + 'totaltokens', + $this->get('id'), + $this->get('apikey'), + ]; + $key = implode("_", $key); + $current = get_config('ai', $key); + $new = $current + $change; + set_config($key, $new, 'ai'); } + + //public function + // TODO token counting. /** * We're overriding this whilst we don't have a real DB table. * @param $filters @@ -75,9 +98,18 @@ public static function get_records($filters = array(), $sort = '', $order = 'ASC $records = []; $fake = new static(0, (object) [ 'id' => 1, - 'name' => "Fake AI Provider" + 'name' => "Fake Open AI Provider", + 'allowembeddings' => true, + 'allowquery' => true, + 'baseurl' => 'https://api.openai.com/v1/', + 'embeddings' => 'embeddings', + 'embeddingmodel' => 'text-embedding-3-small', + 'completions' => 'completions', + 'completionmodel' => 'gpt-4-turbo-preview', + 'apikey'=> '' ]); array_push($records, $fake); return $records; } + } diff --git a/search/engine/solrrag/classes/ai/api.php b/search/engine/solrrag/classes/ai/api.php index b3317f6980e5..a6c916d927a9 100644 --- a/search/engine/solrrag/classes/ai/api.php +++ b/search/engine/solrrag/classes/ai/api.php @@ -9,6 +9,8 @@ public static function get_all_providers() { return array_values(AIProvider::get_records()); } public static function get_provider(int $id): AIProvider { - return new AIProvider($id); + $fakes = AIProvider::get_records(); + return $fakes[0]; + } -} \ No newline at end of file +} diff --git a/search/engine/solrrag/classes/engine.php b/search/engine/solrrag/classes/engine.php index b1201a375c03..526416dacfdf 100644 --- a/search/engine/solrrag/classes/engine.php +++ b/search/engine/solrrag/classes/engine.php @@ -7,13 +7,16 @@ // Fudge autoloading! require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/api.php"); require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/aiprovider.php"); +require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/aiclient.php"); use \core\ai\AIProvider; +use \core\ai\AIClient; class engine extends \search_solr\engine { /** * @var AIProvider AI rovider object to use to generate embeddings. */ - protected ?AIProvider $embeddingprovider = null; + protected ?AIClient $aiclient = null; + protected ?AIProvider $aiprovider = null; public function __construct(bool $alternateconfiguration = false) { @@ -25,9 +28,8 @@ public function __construct(bool $alternateconfiguration = false) // So we'll fudge this for the moment and leverage an OpenAI Web Service API via a simple HTTP request. $aiproviderid = 1; $aiprovider = \core\ai\api::get_provider($aiproviderid); - if ($aiprovider->use_for_embeddings()) { - $this->embeddingprovider = $aiprovider; - } + $this->aiprovider = $aiprovider; + $this->aiclient = !is_null($aiprovider)? new AIClient($aiprovider) : null; } public function is_server_ready() @@ -75,20 +77,8 @@ public function is_server_ready() protected function add_stored_file($document, $storedfile) { $embeddings = []; - $filedoc = $document->export_file_for_engine($storedfile); - /** - * Should we even attempt to get vectors. - */ - if (!is_null($this->embeddingprovider)) { - // garnish $filedoc with the embedding vector. It would be nice if this could be done - // via the export_file_for_engine() call above, that has no awareness of the engine. - $embeddings = $this->embeddingprovider->embed_documents([$filedoc]); - } else { - // potentially warn that selected provider can't be used for - // generating embeddings for RAG. - } - + $filedoc = $document->export_file_for_engine($storedfile); // Used the underlying implementation if (!$this->file_is_indexable($storedfile)) { @@ -197,15 +187,24 @@ protected function add_stored_file($document, $storedfile) } } } - if (count($embeddings) > 0) { - $vector = $embeddings[0]; + /** + * Since solr has given us back the content, we can now send it off to the AI provider. + */ + if ($this->aiprovider->use_for_embeddings() && $this->aiclient) { + // garnish $filedoc with the embedding vector. It would be nice if this could be done + // via the export_file_for_engine() call above, that has no awareness of the engine. + // We expect $filedoc['content'] to be set. + $vector = $this->aiclient->embed_query($filedoc['content']); $vlength = count($vector); $vectorfield = "solr_vector_" . $vlength; + // TODO Check if a field of this length actually exists or not. $filedoc[$vectorfield] = $vector; debugging("Using vector field $vectorfield"); + } else { + // potentially warn that selected provider can't be used for + // generating embeddings for RAG. } $this->add_solr_document($filedoc); - exit("Goodbye"); return; } } else { From 74c7a749731bce378ed5d44a34164365e9e1d32b Mon Sep 17 00:00:00 2001 From: Michael hughes Date: Thu, 7 Mar 2024 22:45:21 +0000 Subject: [PATCH 05/21] xaichat Reference AI chat bot --- mod/xaichat/LICENSE.md | 596 ++++++++++++++++++ mod/xaichat/README.md | 21 + mod/xaichat/classes/aichatform.php | 27 + .../classes/event/course_module_viewed.php | 54 ++ mod/xaichat/db/access.php | 15 + mod/xaichat/db/install.php | 34 + mod/xaichat/db/install.xml | 24 + mod/xaichat/db/uninstall.php | 34 + mod/xaichat/db/upgrade.php | 47 ++ mod/xaichat/db/upgradelib.php | 38 ++ mod/xaichat/grade.php | 44 ++ mod/xaichat/index.php | 92 +++ mod/xaichat/lang/en/xaichat.php | 36 ++ mod/xaichat/lib.php | 297 +++++++++ mod/xaichat/locallib.php | 0 mod/xaichat/mod_form.php | 94 +++ mod/xaichat/settings.php | 33 + mod/xaichat/templates/conversation.mustache | 5 + mod/xaichat/templates/message.mustache | 4 + mod/xaichat/version.php | 34 + mod/xaichat/view.php | 141 +++++ 21 files changed, 1670 insertions(+) create mode 100644 mod/xaichat/LICENSE.md create mode 100644 mod/xaichat/README.md create mode 100644 mod/xaichat/classes/aichatform.php create mode 100644 mod/xaichat/classes/event/course_module_viewed.php create mode 100644 mod/xaichat/db/access.php create mode 100644 mod/xaichat/db/install.php create mode 100644 mod/xaichat/db/install.xml create mode 100644 mod/xaichat/db/uninstall.php create mode 100644 mod/xaichat/db/upgrade.php create mode 100644 mod/xaichat/db/upgradelib.php create mode 100644 mod/xaichat/grade.php create mode 100644 mod/xaichat/index.php create mode 100644 mod/xaichat/lang/en/xaichat.php create mode 100644 mod/xaichat/lib.php create mode 100644 mod/xaichat/locallib.php create mode 100644 mod/xaichat/mod_form.php create mode 100644 mod/xaichat/settings.php create mode 100644 mod/xaichat/templates/conversation.mustache create mode 100644 mod/xaichat/templates/message.mustache create mode 100644 mod/xaichat/version.php create mode 100644 mod/xaichat/view.php diff --git a/mod/xaichat/LICENSE.md b/mod/xaichat/LICENSE.md new file mode 100644 index 000000000000..16d89e0a34c0 --- /dev/null +++ b/mod/xaichat/LICENSE.md @@ -0,0 +1,596 @@ +GNU GENERAL PUBLIC LICENSE +========================== + +Version 3, 29 June 2007 + +Copyright © 2007 Free Software Foundation, Inc. <> + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +## Preamble + +The GNU General Public License is a free, copyleft license for software and other +kinds of works. + +The licenses for most software and other practical works are designed to take away +your freedom to share and change the works. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change all versions of a +program--to make sure it remains free software for all its users. We, the Free +Software Foundation, use the GNU General Public License for most of our software; it +applies also to any other work released this way by its authors. You can apply it to +your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General +Public Licenses are designed to make sure that you have the freedom to distribute +copies of free software (and charge for them if you wish), that you receive source +code or can get it if you want it, that you can change the software or use pieces of +it in new free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you these rights or +asking you to surrender the rights. Therefore, you have certain responsibilities if +you distribute copies of the software, or if you modify it: responsibilities to +respect the freedom of others. + +For example, if you distribute copies of such a program, whether gratis or for a fee, +you must pass on to the recipients the same freedoms that you received. You must make +sure that they, too, receive or can get the source code. And you must show them these +terms so they know their rights. + +Developers that use the GNU GPL protect your rights with two steps: (1) assert +copyright on the software, and (2) offer you this License giving you legal permission +to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains that there is +no warranty for this free software. For both users' and authors' sake, the GPL +requires that modified versions be marked as changed, so that their problems will not +be attributed erroneously to authors of previous versions. + +Some devices are designed to deny users access to install or run modified versions of +the software inside them, although the manufacturer can do so. This is fundamentally +incompatible with the aim of protecting users' freedom to change the software. The +systematic pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we have designed +this version of the GPL to prohibit the practice for those products. If such problems +arise substantially in other domains, we stand ready to extend this provision to +those domains in future versions of the GPL, as needed to protect the freedom of +users. + +Finally, every program is threatened constantly by software patents. States should +not allow patents to restrict development and use of software on general-purpose +computers, but in those that do, we wish to avoid the special danger that patents +applied to a free program could make it effectively proprietary. To prevent this, the +GPL assures that patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and modification follow. + +## TERMS AND CONDITIONS + +### 0. Definitions. + +“This License” refers to version 3 of the GNU General Public License. + +“Copyright” also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + +“The Program” refers to any copyrightable work licensed under this +License. Each licensee is addressed as “you”. “Licensees” and +“recipients” may be individuals or organizations. + +To “modify” a work means to copy from or adapt all or part of the work in +a fashion requiring copyright permission, other than the making of an exact copy. The +resulting work is called a “modified version” of the earlier work or a +work “based on” the earlier work. + +A “covered work” means either the unmodified Program or a work based on +the Program. + +To “propagate” a work means to do anything with it that, without +permission, would make you directly or secondarily liable for infringement under +applicable copyright law, except executing it on a computer or modifying a private +copy. Propagation includes copying, distribution (with or without modification), +making available to the public, and in some countries other activities as well. + +To “convey” a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through a computer +network, with no transfer of a copy, is not conveying. + +An interactive user interface displays “Appropriate Legal Notices” to the +extent that it includes a convenient and prominently visible feature that (1) +displays an appropriate copyright notice, and (2) tells the user that there is no +warranty for the work (except to the extent that warranties are provided), that +licensees may convey the work under this License, and how to view a copy of this +License. If the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +### 1. Source Code. + +The “source code” for a work means the preferred form of the work for +making modifications to it. “Object code” means any non-source form of a +work. + +A “Standard Interface” means an interface that either is an official +standard defined by a recognized standards body, or, in the case of interfaces +specified for a particular programming language, one that is widely used among +developers working in that language. + +The “System Libraries” of an executable work include anything, other than +the work as a whole, that (a) is included in the normal form of packaging a Major +Component, but which is not part of that Major Component, and (b) serves only to +enable use of the work with that Major Component, or to implement a Standard +Interface for which an implementation is available to the public in source code form. +A “Major Component”, in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system (if any) on which +the executable work runs, or a compiler used to produce the work, or an object code +interpreter used to run it. + +The “Corresponding Source” for a work in object code form means all the +source code needed to generate, install, and (for an executable work) run the object +code and to modify the work, including scripts to control those activities. However, +it does not include the work's System Libraries, or general-purpose tools or +generally available free programs which are used unmodified in performing those +activities but which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for the work, and +the source code for shared libraries and dynamically linked subprograms that the work +is specifically designed to require, such as by intimate data communication or +control flow between those subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can regenerate +automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same work. + +### 2. Basic Permissions. + +All rights granted under this License are granted for the term of copyright on the +Program, and are irrevocable provided the stated conditions are met. This License +explicitly affirms your unlimited permission to run the unmodified Program. The +output from running a covered work is covered by this License only if the output, +given its content, constitutes a covered work. This License acknowledges your rights +of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, without +conditions so long as your license otherwise remains in force. You may convey covered +works to others for the sole purpose of having them make modifications exclusively +for you, or provide you with facilities for running those works, provided that you +comply with the terms of this License in conveying all material for which you do not +control copyright. Those thus making or running the covered works for you must do so +exclusively on your behalf, under your direction and control, on terms that prohibit +them from making any copies of your copyrighted material outside their relationship +with you. + +Conveying under any other circumstances is permitted solely under the conditions +stated below. Sublicensing is not allowed; section 10 makes it unnecessary. + +### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological measure under any +applicable law fulfilling obligations under article 11 of the WIPO copyright treaty +adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention +of such measures. + +When you convey a covered work, you waive any legal power to forbid circumvention of +technological measures to the extent such circumvention is effected by exercising +rights under this License with respect to the covered work, and you disclaim any +intention to limit operation or modification of the work as a means of enforcing, +against the work's users, your or third parties' legal rights to forbid circumvention +of technological measures. + +### 4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you receive it, in any +medium, provided that you conspicuously and appropriately publish on each copy an +appropriate copyright notice; keep intact all notices stating that this License and +any non-permissive terms added in accord with section 7 apply to the code; keep +intact all notices of the absence of any warranty; and give all recipients a copy of +this License along with the Program. + +You may charge any price or no price for each copy that you convey, and you may offer +support or warranty protection for a fee. + +### 5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to produce it from +the Program, in the form of source code under the terms of section 4, provided that +you also meet all of these conditions: + +* **a)** The work must carry prominent notices stating that you modified it, and giving a +relevant date. +* **b)** The work must carry prominent notices stating that it is released under this +License and any conditions added under section 7. This requirement modifies the +requirement in section 4 to “keep intact all notices”. +* **c)** You must license the entire work, as a whole, under this License to anyone who +comes into possession of a copy. This License will therefore apply, along with any +applicable section 7 additional terms, to the whole of the work, and all its parts, +regardless of how they are packaged. This License gives no permission to license the +work in any other way, but it does not invalidate such permission if you have +separately received it. +* **d)** If the work has interactive user interfaces, each must display Appropriate Legal +Notices; however, if the Program has interactive interfaces that do not display +Appropriate Legal Notices, your work need not make them do so. + +A compilation of a covered work with other separate and independent works, which are +not by their nature extensions of the covered work, and which are not combined with +it such as to form a larger program, in or on a volume of a storage or distribution +medium, is called an “aggregate” if the compilation and its resulting +copyright are not used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work in an aggregate +does not cause this License to apply to the other parts of the aggregate. + +### 6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms of sections 4 and +5, provided that you also convey the machine-readable Corresponding Source under the +terms of this License, in one of these ways: + +* **a)** Convey the object code in, or embodied in, a physical product (including a +physical distribution medium), accompanied by the Corresponding Source fixed on a +durable physical medium customarily used for software interchange. +* **b)** Convey the object code in, or embodied in, a physical product (including a +physical distribution medium), accompanied by a written offer, valid for at least +three years and valid for as long as you offer spare parts or customer support for +that product model, to give anyone who possesses the object code either (1) a copy of +the Corresponding Source for all the software in the product that is covered by this +License, on a durable physical medium customarily used for software interchange, for +a price no more than your reasonable cost of physically performing this conveying of +source, or (2) access to copy the Corresponding Source from a network server at no +charge. +* **c)** Convey individual copies of the object code with a copy of the written offer to +provide the Corresponding Source. This alternative is allowed only occasionally and +noncommercially, and only if you received the object code with such an offer, in +accord with subsection 6b. +* **d)** Convey the object code by offering access from a designated place (gratis or for +a charge), and offer equivalent access to the Corresponding Source in the same way +through the same place at no further charge. You need not require recipients to copy +the Corresponding Source along with the object code. If the place to copy the object +code is a network server, the Corresponding Source may be on a different server +(operated by you or a third party) that supports equivalent copying facilities, +provided you maintain clear directions next to the object code saying where to find +the Corresponding Source. Regardless of what server hosts the Corresponding Source, +you remain obligated to ensure that it is available for as long as needed to satisfy +these requirements. +* **e)** Convey the object code using peer-to-peer transmission, provided you inform +other peers where the object code and Corresponding Source of the work are being +offered to the general public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded from the +Corresponding Source as a System Library, need not be included in conveying the +object code work. + +A “User Product” is either (1) a “consumer product”, which +means any tangible personal property which is normally used for personal, family, or +household purposes, or (2) anything designed or sold for incorporation into a +dwelling. In determining whether a product is a consumer product, doubtful cases +shall be resolved in favor of coverage. For a particular product received by a +particular user, “normally used” refers to a typical or common use of +that class of product, regardless of the status of the particular user or of the way +in which the particular user actually uses, or expects or is expected to use, the +product. A product is a consumer product regardless of whether the product has +substantial commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + +“Installation Information” for a User Product means any methods, +procedures, authorization keys, or other information required to install and execute +modified versions of a covered work in that User Product from a modified version of +its Corresponding Source. The information must suffice to ensure that the continued +functioning of the modified object code is in no case prevented or interfered with +solely because modification has been made. + +If you convey an object code work under this section in, or with, or specifically for +use in, a User Product, and the conveying occurs as part of a transaction in which +the right of possession and use of the User Product is transferred to the recipient +in perpetuity or for a fixed term (regardless of how the transaction is +characterized), the Corresponding Source conveyed under this section must be +accompanied by the Installation Information. But this requirement does not apply if +neither you nor any third party retains the ability to install modified object code +on the User Product (for example, the work has been installed in ROM). + +The requirement to provide Installation Information does not include a requirement to +continue to provide support service, warranty, or updates for a work that has been +modified or installed by the recipient, or for the User Product in which it has been +modified or installed. Access to a network may be denied when the modification itself +materially and adversely affects the operation of the network or violates the rules +and protocols for communication across the network. + +Corresponding Source conveyed, and Installation Information provided, in accord with +this section must be in a format that is publicly documented (and with an +implementation available to the public in source code form), and must require no +special password or key for unpacking, reading or copying. + +### 7. Additional Terms. + +“Additional permissions” are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. Additional +permissions that are applicable to the entire Program shall be treated as though they +were included in this License, to the extent that they are valid under applicable +law. If additional permissions apply only to part of the Program, that part may be +used separately under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option remove any +additional permissions from that copy, or from any part of it. (Additional +permissions may be written to require their own removal in certain cases when you +modify the work.) You may place additional permissions on material, added by you to a +covered work, for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you add to a +covered work, you may (if authorized by the copyright holders of that material) +supplement the terms of this License with terms: + +* **a)** Disclaiming warranty or limiting liability differently from the terms of +sections 15 and 16 of this License; or +* **b)** Requiring preservation of specified reasonable legal notices or author +attributions in that material or in the Appropriate Legal Notices displayed by works +containing it; or +* **c)** Prohibiting misrepresentation of the origin of that material, or requiring that +modified versions of such material be marked in reasonable ways as different from the +original version; or +* **d)** Limiting the use for publicity purposes of names of licensors or authors of the +material; or +* **e)** Declining to grant rights under trademark law for use of some trade names, +trademarks, or service marks; or +* **f)** Requiring indemnification of licensors and authors of that material by anyone +who conveys the material (or modified versions of it) with contractual assumptions of +liability to the recipient, for any liability that these contractual assumptions +directly impose on those licensors and authors. + +All other non-permissive additional terms are considered “further +restrictions” within the meaning of section 10. If the Program as you received +it, or any part of it, contains a notice stating that it is governed by this License +along with a term that is a further restriction, you may remove that term. If a +license document contains a further restriction but permits relicensing or conveying +under this License, you may add to a covered work material governed by the terms of +that license document, provided that the further restriction does not survive such +relicensing or conveying. + +If you add terms to a covered work in accord with this section, you must place, in +the relevant source files, a statement of the additional terms that apply to those +files, or a notice indicating where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the form of a +separately written license, or stated as exceptions; the above requirements apply +either way. + +### 8. Termination. + +You may not propagate or modify a covered work except as expressly provided under +this License. Any attempt otherwise to propagate or modify it is void, and will +automatically terminate your rights under this License (including any patent licenses +granted under the third paragraph of section 11). + +However, if you cease all violation of this License, then your license from a +particular copyright holder is reinstated (a) provisionally, unless and until the +copyright holder explicitly and finally terminates your license, and (b) permanently, +if the copyright holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is reinstated permanently +if the copyright holder notifies you of the violation by some reasonable means, this +is the first time you have received notice of violation of this License (for any +work) from that copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the licenses of +parties who have received copies or rights from you under this License. If your +rights have been terminated and not permanently reinstated, you do not qualify to +receive new licenses for the same material under section 10. + +### 9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or run a copy of the +Program. Ancillary propagation of a covered work occurring solely as a consequence of +using peer-to-peer transmission to receive a copy likewise does not require +acceptance. However, nothing other than this License grants you permission to +propagate or modify any covered work. These actions infringe copyright if you do not +accept this License. Therefore, by modifying or propagating a covered work, you +indicate your acceptance of this License to do so. + +### 10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically receives a license +from the original licensors, to run, modify and propagate that work, subject to this +License. You are not responsible for enforcing compliance by third parties with this +License. + +An “entity transaction” is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an organization, or +merging organizations. If propagation of a covered work results from an entity +transaction, each party to that transaction who receives a copy of the work also +receives whatever licenses to the work the party's predecessor in interest had or +could give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if the predecessor +has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the rights granted or +affirmed under this License. For example, you may not impose a license fee, royalty, +or other charge for exercise of rights granted under this License, and you may not +initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging +that any patent claim is infringed by making, using, selling, offering for sale, or +importing the Program or any portion of it. + +### 11. Patents. + +A “contributor” is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The work thus +licensed is called the contributor's “contributor version”. + +A contributor's “essential patent claims” are all patent claims owned or +controlled by the contributor, whether already acquired or hereafter acquired, that +would be infringed by some manner, permitted by this License, of making, using, or +selling its contributor version, but do not include claims that would be infringed +only as a consequence of further modification of the contributor version. For +purposes of this definition, “control” includes the right to grant patent +sublicenses in a manner consistent with the requirements of this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free patent license +under the contributor's essential patent claims, to make, use, sell, offer for sale, +import and otherwise run, modify and propagate the contents of its contributor +version. + +In the following three paragraphs, a “patent license” is any express +agreement or commitment, however denominated, not to enforce a patent (such as an +express permission to practice a patent or covenant not to sue for patent +infringement). To “grant” such a patent license to a party means to make +such an agreement or commitment not to enforce a patent against the party. + +If you convey a covered work, knowingly relying on a patent license, and the +Corresponding Source of the work is not available for anyone to copy, free of charge +and under the terms of this License, through a publicly available network server or +other readily accessible means, then you must either (1) cause the Corresponding +Source to be so available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner consistent with +the requirements of this License, to extend the patent license to downstream +recipients. “Knowingly relying” means you have actual knowledge that, but +for the patent license, your conveying the covered work in a country, or your +recipient's use of the covered work in a country, would infringe one or more +identifiable patents in that country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or arrangement, you +convey, or propagate by procuring conveyance of, a covered work, and grant a patent +license to some of the parties receiving the covered work authorizing them to use, +propagate, modify or convey a specific copy of the covered work, then the patent +license you grant is automatically extended to all recipients of the covered work and +works based on it. + +A patent license is “discriminatory” if it does not include within the +scope of its coverage, prohibits the exercise of, or is conditioned on the +non-exercise of one or more of the rights that are specifically granted under this +License. You may not convey a covered work if you are a party to an arrangement with +a third party that is in the business of distributing software, under which you make +payment to the third party based on the extent of your activity of conveying the +work, and under which the third party grants, to any of the parties who would receive +the covered work from you, a discriminatory patent license (a) in connection with +copies of the covered work conveyed by you (or copies made from those copies), or (b) +primarily for and in connection with specific products or compilations that contain +the covered work, unless you entered into that arrangement, or that patent license +was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting any implied +license or other defenses to infringement that may otherwise be available to you +under applicable patent law. + +### 12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or otherwise) +that contradict the conditions of this License, they do not excuse you from the +conditions of this License. If you cannot convey a covered work so as to satisfy +simultaneously your obligations under this License and any other pertinent +obligations, then as a consequence you may not convey it at all. For example, if you +agree to terms that obligate you to collect a royalty for further conveying from +those to whom you convey the Program, the only way you could satisfy both those terms +and this License would be to refrain entirely from conveying the Program. + +### 13. Use with the GNU Affero General Public License. + +Notwithstanding any other provision of this License, you have permission to link or +combine any covered work with a work licensed under version 3 of the GNU Affero +General Public License into a single combined work, and to convey the resulting work. +The terms of this License will continue to apply to the part which is the covered +work, but the special requirements of the GNU Affero General Public License, section +13, concerning interaction through a network will apply to the combination as such. + +### 14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions of the GNU +General Public License from time to time. Such new versions will be similar in spirit +to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies that +a certain numbered version of the GNU General Public License “or any later +version” applies to it, you have the option of following the terms and +conditions either of that numbered version or of any later version published by the +Free Software Foundation. If the Program does not specify a version number of the GNU +General Public License, you may choose any version ever published by the Free +Software Foundation. + +If the Program specifies that a proxy can decide which future versions of the GNU +General Public License can be used, that proxy's public statement of acceptance of a +version permanently authorizes you to choose that version for the Program. + +Later license versions may give you additional or different permissions. However, no +additional obligations are imposed on any author or copyright holder as a result of +your choosing to follow a later version. + +### 15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER +EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE +QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +### 16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY +COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS +PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, +INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE +OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE +WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + +### 17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided above cannot be +given local legal effect according to their terms, reviewing courts shall apply local +law that most closely approximates an absolute waiver of all civil liability in +connection with the Program, unless a warranty or assumption of liability accompanies +a copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS + +## How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible use to +the public, the best way to achieve this is to make it free software which everyone +can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach them +to the start of each source file to most effectively state the exclusion of warranty; +and each file should have at least the “copyright” line and a pointer to +where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If the program does terminal interaction, make it output a short notice like this +when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type 'show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type 'show c' for details. + +The hypothetical commands 'show w' and 'show c' should show the appropriate parts of +the General Public License. Of course, your program's commands might be different; +for a GUI interface, you would use an “about box”. + +You should also get your employer (if you work as a programmer) or school, if any, to +sign a “copyright disclaimer” for the program, if necessary. For more +information on this, and how to apply and follow the GNU GPL, see +<>. + +The GNU General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may consider it +more useful to permit linking proprietary applications with the library. If this is +what you want to do, use the GNU Lesser General Public License instead of this +License. But first, please read +<>. \ No newline at end of file diff --git a/mod/xaichat/README.md b/mod/xaichat/README.md new file mode 100644 index 000000000000..831ae7eafb02 --- /dev/null +++ b/mod/xaichat/README.md @@ -0,0 +1,21 @@ +# AI Chat (Reference) # + +This is a **reference** implementation of an AI chat plugin that uses +the "faked" AI Provider API to offer chat based against Moodle data. + + +## License ## + +2024 Michael Hughes + +This program is free software: you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see . diff --git a/mod/xaichat/classes/aichatform.php b/mod/xaichat/classes/aichatform.php new file mode 100644 index 000000000000..eed5bea26187 --- /dev/null +++ b/mod/xaichat/classes/aichatform.php @@ -0,0 +1,27 @@ +libdir.'/formslib.php'); + +class aichatform extends \moodleform { + public function definition() { + $mform = $this->_form; + + $mform->addElement('hidden','id', 'id'); +// $mform->setConstant('id', ); + $mform->setType('id', PARAM_INT); + $mform->addElement('textarea', 'userprompt', 'User Prompt'); + + $this->add_action_buttons(true,'Send'); +// $buttons = [ +// $mform->createElement('submit','submitbutton','Send'), +// $mform->createElement('cancel','submitbutton','Cancel'), +// $mform->createElement('submit','restartbutton','Restart') +// ]; +// $mform->addGroup($buttons, 'actionbuttons', ' ', array(' '), false); + + } +} diff --git a/mod/xaichat/classes/event/course_module_viewed.php b/mod/xaichat/classes/event/course_module_viewed.php new file mode 100644 index 000000000000..d810d288a6c5 --- /dev/null +++ b/mod/xaichat/classes/event/course_module_viewed.php @@ -0,0 +1,54 @@ +. + +/** + * The mod_workshop course module viewed event. + * + * @package mod_xaichat + * @copyright 2024 Michael Hughes + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_xaichat\event; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once("$CFG->dirroot/mod/xaichat/locallib.php"); + +/** + * The mod_workshop course module viewed event class. + * + * @package mod_workshop + * @since Moodle 2.6 + * @copyright 2013 Adrian Greeve + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class course_module_viewed extends \core\event\course_module_viewed { + + /** + * Init method. + */ + protected function init() { + $this->data['crud'] = 'r'; + $this->data['edulevel'] = self::LEVEL_PARTICIPATING; + $this->data['objecttable'] = 'xaichat'; + } + + public static function get_objectid_mapping() { + return array('db' => 'xaichat', 'restore' => 'xaichat'); + } +} diff --git a/mod/xaichat/db/access.php b/mod/xaichat/db/access.php new file mode 100644 index 000000000000..16578ec8e343 --- /dev/null +++ b/mod/xaichat/db/access.php @@ -0,0 +1,15 @@ + [ + 'riskbitmask' => RISK_XSS, + + 'captype' => 'write', + 'contextlevel' => CONTEXT_COURSE, + 'archetypes' => [ + 'editingteacher' => CAP_ALLOW, + 'manager' => CAP_ALLOW + ], + 'clonepermissionsfrom' => 'moodle/course:manageactivities' + ] +]; diff --git a/mod/xaichat/db/install.php b/mod/xaichat/db/install.php new file mode 100644 index 000000000000..3163a6138315 --- /dev/null +++ b/mod/xaichat/db/install.php @@ -0,0 +1,34 @@ +. + +/** + * Code to be executed after the plugin's database scheme has been installed is defined here. + * + * @package mod_xaichat + * @category upgrade + * @copyright 2024 Michael Hughes + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +/** + * Custom code to be run on installing the plugin. + */ +function xmldb_xaichat_install() { + + return true; +} diff --git a/mod/xaichat/db/install.xml b/mod/xaichat/db/install.xml new file mode 100644 index 000000000000..28199a2d01b7 --- /dev/null +++ b/mod/xaichat/db/install.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + +
+
+
diff --git a/mod/xaichat/db/uninstall.php b/mod/xaichat/db/uninstall.php new file mode 100644 index 000000000000..73c6fa7449e0 --- /dev/null +++ b/mod/xaichat/db/uninstall.php @@ -0,0 +1,34 @@ +. + +/** + * Code that is executed before the tables and data are dropped during the plugin uninstallation. + * + * @package mod_xaichat + * @category upgrade + * @copyright 2024 Michael Hughes + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +/** + * Custom uninstallation procedure. + */ +function xmldb_xaichat_uninstall() { + + return true; +} diff --git a/mod/xaichat/db/upgrade.php b/mod/xaichat/db/upgrade.php new file mode 100644 index 000000000000..e7136da2f129 --- /dev/null +++ b/mod/xaichat/db/upgrade.php @@ -0,0 +1,47 @@ +. + +/** + * Plugin upgrade steps are defined here. + * + * @package mod_xaichat + * @category upgrade + * @copyright 2024 Michael Hughes + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +require_once(__DIR__.'/upgradelib.php'); + +/** + * Execute mod_xaichat upgrade from the given old version. + * + * @param int $oldversion + * @return bool + */ +function xmldb_xaichat_upgrade($oldversion) { + global $DB; + + $dbman = $DB->get_manager(); + + // For further information please read {@link https://docs.moodle.org/dev/Upgrade_API}. + // + // You will also have to create the db/install.xml file by using the XMLDB Editor. + // Documentation for the XMLDB Editor can be found at {@link https://docs.moodle.org/dev/XMLDB_editor}. + + return true; +} diff --git a/mod/xaichat/db/upgradelib.php b/mod/xaichat/db/upgradelib.php new file mode 100644 index 000000000000..46da4252f1ca --- /dev/null +++ b/mod/xaichat/db/upgradelib.php @@ -0,0 +1,38 @@ +. + +/** + * Plugin upgrade helper functions are defined here. + * + * @package mod_xaichat + * @category upgrade + * @copyright 2024 Michael Hughes + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +/** + * Helper function used by the upgrade.php file. + */ +function mod_xaichat_helper_function() { + global $DB; + + // Please note: you can only use raw low level database access here. + // Avoid Moodle API calls in upgrade steps. + // + // For more information please read {@link https://docs.moodle.org/dev/Upgrade_API}. +} diff --git a/mod/xaichat/grade.php b/mod/xaichat/grade.php new file mode 100644 index 000000000000..49fb10622e58 --- /dev/null +++ b/mod/xaichat/grade.php @@ -0,0 +1,44 @@ +. + +/** + * Redirect the user to the appropiate submission related page. + * + * @package mod_xaichat + * @category grade + * @copyright 2024 Michael Hughes + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require(__DIR__.'/../../config.php'); + +// Course module ID. +$id = required_param('id', PARAM_INT); + +$cm = get_coursemodule_from_id('xaichat', $id, 0, false, MUST_EXIST); +$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST); +$moduleinstance = $DB->get_record('xaichat', array('id' => $cm->instance), '*', MUST_EXIST); + +require_login($course, true, $cm); + +// Item number may be != 0 for activities that allow more than one grade per user. +$itemnumber = optional_param('itemnumber', 0, PARAM_INT); + +// Graded user ID (optional). +$userid = optional_param('userid', 0, PARAM_INT); + +// In the simplest case just redirect to the view page. +redirect('view.php?id='.$id); diff --git a/mod/xaichat/index.php b/mod/xaichat/index.php new file mode 100644 index 000000000000..447fd9cab5a0 --- /dev/null +++ b/mod/xaichat/index.php @@ -0,0 +1,92 @@ +. + +/** + * Display information about all the mod_xaichat modules in the requested course. + * + * @package mod_xaichat + * @copyright 2024 Michael Hughes + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require(__DIR__.'/../../config.php'); + +require_once(__DIR__.'/lib.php'); + +$id = required_param('id', PARAM_INT); + +$course = $DB->get_record('course', array('id' => $id), '*', MUST_EXIST); +require_course_login($course); + +$coursecontext = context_course::instance($course->id); + +$event = \mod_xaichat\event\course_module_instance_list_viewed::create(array( + 'context' => $modulecontext +)); +$event->add_record_snapshot('course', $course); +$event->trigger(); + +$PAGE->set_url('/mod/xaichat/index.php', array('id' => $id)); +$PAGE->set_title(format_string($course->fullname)); +$PAGE->set_heading(format_string($course->fullname)); +$PAGE->set_context($coursecontext); + +echo $OUTPUT->header(); + +$modulenameplural = get_string('modulenameplural', 'mod_xaichat'); +echo $OUTPUT->heading($modulenameplural); + +$xaichats = get_all_instances_in_course('xaichat', $course); + +if (empty($xaichats)) { + notice(get_string('no$xaichatinstances', 'mod_xaichat'), new moodle_url('/course/view.php', array('id' => $course->id))); +} + +$table = new html_table(); +$table->attributes['class'] = 'generaltable mod_index'; + +if ($course->format == 'weeks') { + $table->head = array(get_string('week'), get_string('name')); + $table->align = array('center', 'left'); +} else if ($course->format == 'topics') { + $table->head = array(get_string('topic'), get_string('name')); + $table->align = array('center', 'left', 'left', 'left'); +} else { + $table->head = array(get_string('name')); + $table->align = array('left', 'left', 'left'); +} + +foreach ($xaichats as $xaichat) { + if (!$xaichat->visible) { + $link = html_writer::link( + new moodle_url('/mod/xaichat/view.php', array('id' => $xaichat->coursemodule)), + format_string($xaichat->name, true), + array('class' => 'dimmed')); + } else { + $link = html_writer::link( + new moodle_url('/mod/xaichat/view.php', array('id' => $xaichat->coursemodule)), + format_string($xaichat->name, true)); + } + + if ($course->format == 'weeks' or $course->format == 'topics') { + $table->data[] = array($xaichat->section, $link); + } else { + $table->data[] = array($link); + } +} + +echo html_writer::table($table); +echo $OUTPUT->footer(); diff --git a/mod/xaichat/lang/en/xaichat.php b/mod/xaichat/lang/en/xaichat.php new file mode 100644 index 000000000000..4e1849130e97 --- /dev/null +++ b/mod/xaichat/lang/en/xaichat.php @@ -0,0 +1,36 @@ +. + +/** + * Plugin strings are defined here. + * + * @package mod_xaichat + * @category string + * @copyright 2024 Michael Hughes + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$string['pluginname'] = 'AI Chat (Reference)'; +$string['modulename'] = 'AI Chat (Reference)'; +$string['modulenameplural'] = 'AI Chats (Reference)'; + +$string['xaichatname'] = 'Chat Name'; +$string['xaichatname_help'] = 'The name of the chat.'; +$string['xaichatsettings'] = 'Settings'; +$string['xaichatfieldset'] = ''; +$string['pluginadministration'] = 'Plugin Administration'; diff --git a/mod/xaichat/lib.php b/mod/xaichat/lib.php new file mode 100644 index 000000000000..6c7e078f64d5 --- /dev/null +++ b/mod/xaichat/lib.php @@ -0,0 +1,297 @@ +. + +/** + * Library of interface functions and constants. + * + * @package mod_xaichat + * @copyright 2024 Michael Hughes + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/api.php"); +require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/aiprovider.php"); +require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/aiclient.php"); + +/** + * Return if the plugin supports $feature. + * + * @param string $feature Constant representing the feature. + * @return true | null True if the feature is supported, null otherwise. + */ +function xaichat_supports($feature) { + switch ($feature) { + case FEATURE_GRADE_HAS_GRADE: + return true; + case FEATURE_MOD_INTRO: + return true; + default: + return null; + } +} + +/** + * Saves a new instance of the mod_xaichat into the database. + * + * Given an object containing all the necessary data, (defined by the form + * in mod_form.php) this function will create a new instance and return the id + * number of the instance. + * + * @param object $moduleinstance An object from the form. + * @param mod_xaichat_mod_form $mform The form. + * @return int The id of the newly inserted record. + */ +function xaichat_add_instance($moduleinstance, $mform = null) { + global $DB; + + $moduleinstance->timecreated = time(); + + $id = $DB->insert_record('xaichat', $moduleinstance); + + return $id; +} + +/** + * Updates an instance of the mod_xaichat in the database. + * + * Given an object containing all the necessary data (defined in mod_form.php), + * this function will update an existing instance with new data. + * + * @param object $moduleinstance An object from the form in mod_form.php. + * @param mod_xaichat_mod_form $mform The form. + * @return bool True if successful, false otherwise. + */ +function xaichat_update_instance($moduleinstance, $mform = null) { + global $DB; + + $moduleinstance->timemodified = time(); + $moduleinstance->id = $moduleinstance->instance; + + return $DB->update_record('xaichat', $moduleinstance); +} + +/** + * Removes an instance of the mod_xaichat from the database. + * + * @param int $id Id of the module instance. + * @return bool True if successful, false on failure. + */ +function xaichat_delete_instance($id) { + global $DB; + + $exists = $DB->get_record('xaichat', array('id' => $id)); + if (!$exists) { + return false; + } + + $DB->delete_records('xaichat', array('id' => $id)); + + return true; +} + +/** + * Is a given scale used by the instance of mod_xaichat? + * + * This function returns if a scale is being used by one mod_xaichat + * if it has support for grading and scales. + * + * @param int $moduleinstanceid ID of an instance of this module. + * @param int $scaleid ID of the scale. + * @return bool True if the scale is used by the given mod_xaichat instance. + */ +function xaichat_scale_used($moduleinstanceid, $scaleid) { + global $DB; + + if ($scaleid && $DB->record_exists('xaichat', array('id' => $moduleinstanceid, 'grade' => -$scaleid))) { + return true; + } else { + return false; + } +} + +/** + * Checks if scale is being used by any instance of mod_xaichat. + * + * This is used to find out if scale used anywhere. + * + * @param int $scaleid ID of the scale. + * @return bool True if the scale is used by any mod_xaichat instance. + */ +function xaichat_scale_used_anywhere($scaleid) { + global $DB; + + if ($scaleid and $DB->record_exists('xaichat', array('grade' => -$scaleid))) { + return true; + } else { + return false; + } +} + +/** + * Creates or updates grade item for the given mod_xaichat instance. + * + * Needed by {@see grade_update_mod_grades()}. + * + * @param stdClass $moduleinstance Instance object with extra cmidnumber and modname property. + * @param bool $reset Reset grades in the gradebook. + * @return void. + */ +function xaichat_grade_item_update($moduleinstance, $reset=false) { + global $CFG; + require_once($CFG->libdir.'/gradelib.php'); + + $item = array(); + $item['itemname'] = clean_param($moduleinstance->name, PARAM_NOTAGS); + $item['gradetype'] = GRADE_TYPE_VALUE; + + if ($moduleinstance->grade > 0) { + $item['gradetype'] = GRADE_TYPE_VALUE; + $item['grademax'] = $moduleinstance->grade; + $item['grademin'] = 0; + } else if ($moduleinstance->grade < 0) { + $item['gradetype'] = GRADE_TYPE_SCALE; + $item['scaleid'] = -$moduleinstance->grade; + } else { + $item['gradetype'] = GRADE_TYPE_NONE; + } + if ($reset) { + $item['reset'] = true; + } + + grade_update('/mod/xaichat', $moduleinstance->course, 'mod', 'mod_xaichat', $moduleinstance->id, 0, null, $item); +} + +/** + * Delete grade item for given mod_xaichat instance. + * + * @param stdClass $moduleinstance Instance object. + * @return grade_item. + */ +function xaichat_grade_item_delete($moduleinstance) { + global $CFG; + require_once($CFG->libdir.'/gradelib.php'); + + return grade_update('/mod/xaichat', $moduleinstance->course, 'mod', 'xaichat', + $moduleinstance->id, 0, null, array('deleted' => 1)); +} + +/** + * Update mod_xaichat grades in the gradebook. + * + * Needed by {@see grade_update_mod_grades()}. + * + * @param stdClass $moduleinstance Instance object with extra cmidnumber and modname property. + * @param int $userid Update grade of specific user only, 0 means all participants. + */ +function xaichat_update_grades($moduleinstance, $userid = 0) { + global $CFG, $DB; + require_once($CFG->libdir.'/gradelib.php'); + + // Populate array of grade objects indexed by userid. + $grades = array(); + grade_update('/mod/xaichat', $moduleinstance->course, 'mod', 'mod_xaichat', $moduleinstance->id, 0, $grades); +} + +/** + * Returns the lists of all browsable file areas within the given module context. + * + * The file area 'intro' for the activity introduction field is added automatically + * by {@see file_browser::get_file_info_context_module()}. + * + * @package mod_xaichat + * @category files + * + * @param stdClass $course + * @param stdClass $cm + * @param stdClass $context + * @return string[]. + */ +function xaichat_get_file_areas($course, $cm, $context) { + return array(); +} + +/** + * File browsing support for mod_xaichat file areas. + * + * @package mod_xaichat + * @category files + * + * @param file_browser $browser + * @param array $areas + * @param stdClass $course + * @param stdClass $cm + * @param stdClass $context + * @param string $filearea + * @param int $itemid + * @param string $filepath + * @param string $filename + * @return file_info Instance or null if not found. + */ +function xaichat_get_file_info($browser, $areas, $course, $cm, $context, $filearea, $itemid, $filepath, $filename) { + return null; +} + +/** + * Serves the files from the mod_xaichat file areas. + * + * @package mod_xaichat + * @category files + * + * @param stdClass $course The course object. + * @param stdClass $cm The course module object. + * @param stdClass $context The mod_xaichat's context. + * @param string $filearea The name of the file area. + * @param array $args Extra arguments (itemid, path). + * @param bool $forcedownload Whether or not force download. + * @param array $options Additional options affecting the file serving. + */ +function xaichat_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, $options = array()) { + global $DB, $CFG; + + if ($context->contextlevel != CONTEXT_MODULE) { + send_file_not_found(); + } + + require_login($course, true, $cm); + send_file_not_found(); +} + +/** + * Extends the global navigation tree by adding mod_xaichat nodes if there is a relevant content. + * + * This can be called by an AJAX request so do not rely on $PAGE as it might not be set up properly. + * + * @param navigation_node $xaichatnode An object representing the navigation tree node. + * @param stdClass $course + * @param stdClass $module + * @param cm_info $cm + */ +function xaichat_extend_navigation($xaichatnode, $course, $module, $cm) { +} + +/** + * Extends the settings navigation with the mod_xaichat settings. + * + * This function is called when the context for the page is a mod_xaichat module. + * This is not called by AJAX so it is safe to rely on the $PAGE. + * + * @param settings_navigation $settingsnav {@see settings_navigation} + * @param navigation_node $xaichatnode {@see navigation_node} + */ +function xaichat_extend_settings_navigation($settingsnav, $xaichatnode = null) { +} diff --git a/mod/xaichat/locallib.php b/mod/xaichat/locallib.php new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/mod/xaichat/mod_form.php b/mod/xaichat/mod_form.php new file mode 100644 index 000000000000..1b88e0a319ea --- /dev/null +++ b/mod/xaichat/mod_form.php @@ -0,0 +1,94 @@ +. + +/** + * The main mod_xaichat configuration form. + * + * @package mod_xaichat + * @copyright 2024 Michael Hughes + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot.'/course/moodleform_mod.php'); +require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/api.php"); +require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/aiprovider.php"); +require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/aiclient.php"); + +/** + * Module instance settings form. + * + * @package mod_xaichat + * @copyright 2024 Michael Hughes + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class mod_xaichat_mod_form extends moodleform_mod { + + /** + * Defines forms elements + */ + public function definition() { + global $CFG; + + $mform = $this->_form; + + // Adding the "general" fieldset, where all the common settings are shown. + $mform->addElement('header', 'general', get_string('general', 'form')); + + // Adding the standard "name" field. + $mform->addElement('text', 'name', get_string('xaichatname', 'mod_xaichat'), array('size' => '64')); + + if (!empty($CFG->formatstringstriptags)) { + $mform->setType('name', PARAM_TEXT); + } else { + $mform->setType('name', PARAM_CLEANHTML); + } + + $mform->addRule('name', null, 'required', null, 'client'); + $mform->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'client'); + $mform->addHelpButton('name', 'xaichatname', 'mod_xaichat'); + + // Adding the standard "intro" and "introformat" fields. + if ($CFG->branch >= 29) { + $this->standard_intro_elements(); + } else { + $this->add_intro_editor(); + } + + // Adding the rest of mod_xaichat settings, spreading all them into this fieldset + // ... or adding more fieldsets ('header' elements) if needed for better logic. + $mform->addElement('header', 'xaichatfieldset', get_string('xaichatfieldset', 'mod_xaichat')); + + $providers = \core\ai\api::get_all_providers(); + $optproviders = []; + foreach($providers as $provider) { + $optproviders[$provider->get('id')] = $provider->get('name'); + } + $mform->addElement('select', 'aiproviderid', + 'Choose Provider', + $optproviders + ); + // Add standard grading elements. + $this->standard_grading_coursemodule_elements(); + + // Add standard elements. + $this->standard_coursemodule_elements(); + + // Add standard buttons. + $this->add_action_buttons(); + } +} diff --git a/mod/xaichat/settings.php b/mod/xaichat/settings.php new file mode 100644 index 000000000000..6e2bd56f2d1b --- /dev/null +++ b/mod/xaichat/settings.php @@ -0,0 +1,33 @@ +. + +/** + * Plugin administration pages are defined here. + * + * @package mod_xaichat + * @category admin + * @copyright 2024 Michael Hughes + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +if ($hassiteconfig) { + // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedIf + if ($ADMIN->fulltree) { + // TODO: Define the plugin settings page - {@link https://docs.moodle.org/dev/Admin_settings}. + } +} diff --git a/mod/xaichat/templates/conversation.mustache b/mod/xaichat/templates/conversation.mustache new file mode 100644 index 000000000000..7dacee0a4d14 --- /dev/null +++ b/mod/xaichat/templates/conversation.mustache @@ -0,0 +1,5 @@ +

Responses

+{{#messages}} + {{> mod_xaichat/message}} +{{/messages}} +
diff --git a/mod/xaichat/templates/message.mustache b/mod/xaichat/templates/message.mustache new file mode 100644 index 000000000000..e24e53a096ba --- /dev/null +++ b/mod/xaichat/templates/message.mustache @@ -0,0 +1,4 @@ +
+
{{{role}}}
+
{{{content}}}
+
diff --git a/mod/xaichat/version.php b/mod/xaichat/version.php new file mode 100644 index 000000000000..72182cdb2ba8 --- /dev/null +++ b/mod/xaichat/version.php @@ -0,0 +1,34 @@ +. + +/** + * Plugin version and other meta-data are defined here. + * + * @package mod_xaichat + * @copyright 2024 Michael Hughes + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$plugin->component = 'mod_xaichat'; +$plugin->release = '0.0.1'; +$plugin->version = 2024030701; +$plugin->requires = 2021051700; +$plugin->maturity = MATURITY_ALPHA; +$plugin->dependencies = [ + 'search_solrrag' => 2024030400 +]; diff --git a/mod/xaichat/view.php b/mod/xaichat/view.php new file mode 100644 index 000000000000..6ecb80a35a93 --- /dev/null +++ b/mod/xaichat/view.php @@ -0,0 +1,141 @@ +. + +/** + * Prints an instance of mod_xaichat. + * + * @package mod_xaichat + * @copyright 2024 Michael Hughes + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require(__DIR__.'/../../config.php'); +require_once(__DIR__.'/lib.php'); +use mod_xaichat\aichatform; + +// Course module id. +$id = optional_param('id', 0, PARAM_INT); + +// Activity instance id. +$x = optional_param('x', 0, PARAM_INT); + +if ($id) { + $cm = get_coursemodule_from_id('xaichat', $id, 0, false, MUST_EXIST); + $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST); + $moduleinstance = $DB->get_record('xaichat', array('id' => $cm->instance), '*', MUST_EXIST); +} else { + $moduleinstance = $DB->get_record('xaichat', array('id' => $x), '*', MUST_EXIST); + $course = $DB->get_record('course', array('id' => $moduleinstance->course), '*', MUST_EXIST); + $cm = get_coursemodule_from_instance('xaichat', $moduleinstance->id, $course->id, false, MUST_EXIST); +} + +require_login($course, true, $cm); + +$PAGE->set_url('/mod/xaichat/view.php', ['id' => $cm->id]); + +$modulecontext = context_module::instance($cm->id); + +$aicontextkey = "mod_xaichat:context:{$cm->id}:{$USER->id}"; +if (!isset($_SESSION[$aicontextkey])) { + $_SESSION[$aicontextkey] = [ + 'messages'=>[] + ]; +} +//$aicontext = $_SESSION[$aicontextkey]; + +if (!($aiprovider = \core\ai\api::get_provider($moduleinstance->aiproviderid))){ + throw new moodle_exception("noaiproviderfound", 'xaichat'); +} + +$event = \mod_xaichat\event\course_module_viewed::create(array( + 'objectid' => $moduleinstance->id, + 'context' => $modulecontext +)); +$event->add_record_snapshot('course', $course); +$event->add_record_snapshot('xaichat', $moduleinstance); +$event->trigger(); + +$PAGE->set_title(format_string($moduleinstance->name)); +$PAGE->set_heading(format_string($course->fullname)); +$PAGE->set_context($modulecontext); +echo $OUTPUT->header(); + +$chatform = new aichatform(); +if ($data = $chatform->get_data()) { + if (isset($data->restartbutton)) { + redirect(new \moodle_url('/mod/xaichat/view.php', array('id' => $cm->id))); + } + $totalsteps = 3; + $aiclient = new \core\ai\AIClient($aiprovider); + + $progress = new \progress_bar(); + $progress->create(); + $progress->update(1, $totalsteps,'Processing User Prompt'); +// $vector = $aiclient->embed_query($data->userprompt); + $search = \core_search\manager::instance(true, true); + $search->similarity_search($settings); + + $_SESSION[$aicontextkey]['messages'][] = (object)[ + "role" => "user", + "content" => $data->userprompt + ]; + $progress->update(2, $totalsteps, 'Waiting for response'); + $airesults = $aiclient->chat($_SESSION[$aicontextkey]['messages']); + $_SESSION[$aicontextkey]['messages'] = array_merge($_SESSION[$aicontextkey]['messages'],$airesults); + $progress->update(3, $totalsteps, 'Got Response'); + redirect(new \moodle_url('/mod/xaichat/view.php', ['id' => $cm->id])); +} else if ($chatform->is_cancelled()) { + $_SESSION[$aicontextkey] = [ + 'messages'=>[] + ]; +} else { + // Clear session on first view of form. + // TODO prefix with system and context prompts. +// $_SESSION[$aicontextkey] = [ +// 'messages'=>[] +// ]; + $toform = [ + 'id' => $id, + 'aiproviderid' => $moduleinstance->aiproviderid, + 'aicontext' => $_SESSION[$aicontextkey], + ]; + // Initialise; + $chatform->set_data($toform); +} +$userpic = $OUTPUT->render(new \user_picture($USER)). fullname($USER); +$aipic = $aiprovider->get('name'); + +$displaymessages = []; +foreach ($_SESSION[$aicontextkey]['messages'] as $message) { + $displaymessages[] = [ + "role" => $message->role == "user" ? $userpic : \html_writer::tag("strong", $aipic), + "content" => format_text($message->content, FORMAT_MARKDOWN) + ]; +} +$tcontext = [ + "userpic" => new user_picture($USER), + "messages" => $displaymessages +]; + +echo $OUTPUT->render_from_template("mod_xaichat/conversation", $tcontext); + +$chatform->display(); + +//echo \html_writer::tag('pre', print_r($displaymessages,1)); +//echo \html_writer::tag('pre', print_r($_SESSION[$aicontextkey]['messages'],1)); + +echo $OUTPUT->footer(); From fddf4e14e3c98c3e09790610d9e04243efc685b1 Mon Sep 17 00:00:00 2001 From: Michael hughes Date: Thu, 7 Mar 2024 22:46:07 +0000 Subject: [PATCH 06/21] solrrag/xaichat reference implementations of a moodle plugin --- search/engine/solrrag/classes/ai/aiclient.php | 41 ++++++++++ .../engine/solrrag/classes/ai/aiprovider.php | 17 +++- search/engine/solrrag/classes/ai/api.php | 7 +- search/engine/solrrag/classes/engine.php | 79 ++++++++++++------- 4 files changed, 114 insertions(+), 30 deletions(-) diff --git a/search/engine/solrrag/classes/ai/aiclient.php b/search/engine/solrrag/classes/ai/aiclient.php index 4f9da6ec3dff..4b9c9b8bad8e 100644 --- a/search/engine/solrrag/classes/ai/aiclient.php +++ b/search/engine/solrrag/classes/ai/aiclient.php @@ -5,6 +5,9 @@ * Base client for AI providers that uses simple http request. */ class AIClient extends \curl { + /** + * @var AIProvider + */ private $provider; public function __construct( \core\ai\AIProvider $provider @@ -24,6 +27,44 @@ public function get_chat_completions_url(): string { return $this->provider->get('baseurl') . $this->provider->get('completions'); } + /** + * @param $messages + * @return array String array of each line of the AI's Response. + * @throws \coding_exception + */ + public function chat($messages) { + $params = [ + "model" => $this->provider->get('completionmodel'), + "messages" => $messages + ]; + $params = json_encode($params); + $rawresult = $this->post($this->get_chat_completions_url(), $params); + $jsonresult = json_decode($rawresult); + if (!isset($jsonresult->choices)) { + exit(); + return []; + } + $result = $this->convert_chat_completion($jsonresult->choices); + if (isset($jsonresult->usage)) { + $this->provider->increment_prompt_usage($jsonresult->usage->prompt_tokens); + $this->provider->increment_completion_tokens($jsonresult->usage->completion_tokens); + $this->provider->increment_total_tokens($jsonresult->usage->total_tokens); + } + return $result; + } + + /** + * Converts an OpenAI Type of response to an array of sentences + * @param $completion + * @return array + */ + protected function convert_chat_completion($choices) { + $responses = []; + foreach($choices as $choice) { + array_push($responses, $choice->message); + } + return $responses; + } /** * @param $document * @return array diff --git a/search/engine/solrrag/classes/ai/aiprovider.php b/search/engine/solrrag/classes/ai/aiprovider.php index afd607f8867b..8ccd0fd1331b 100644 --- a/search/engine/solrrag/classes/ai/aiprovider.php +++ b/search/engine/solrrag/classes/ai/aiprovider.php @@ -70,6 +70,18 @@ public function increment_prompt_usage($change) { $new = $current + $change; set_config($key, $new, 'ai'); } + public function increment_completion_tokens($change) { + return; + $key = [ + 'completiontokens', + $this->get('id'), + $this->get('apikey'), + ]; + $key = implode("_", $key); + $current = get_config('ai', $key); + $new = $current + $change; + set_config($key, $new, 'ai'); + } public function increment_total_tokens($change) { return; $key = [ @@ -95,6 +107,7 @@ public function increment_total_tokens($change) { * @return array */ public static function get_records($filters = array(), $sort = '', $order = 'ASC', $skip = 0, $limit = 0) { + global $_ENV; $records = []; $fake = new static(0, (object) [ 'id' => 1, @@ -104,9 +117,9 @@ public static function get_records($filters = array(), $sort = '', $order = 'ASC 'baseurl' => 'https://api.openai.com/v1/', 'embeddings' => 'embeddings', 'embeddingmodel' => 'text-embedding-3-small', - 'completions' => 'completions', + 'completions' => 'chat/completions', 'completionmodel' => 'gpt-4-turbo-preview', - 'apikey'=> '' + 'apikey'=> $_ENV['OPENAIKEY'] ]); array_push($records, $fake); return $records; diff --git a/search/engine/solrrag/classes/ai/api.php b/search/engine/solrrag/classes/ai/api.php index a6c916d927a9..ad5939815746 100644 --- a/search/engine/solrrag/classes/ai/api.php +++ b/search/engine/solrrag/classes/ai/api.php @@ -5,7 +5,12 @@ class api { - public static function get_all_providers() { + /** + * Return a list of AIProviders that are available for specified context. + * @param $context + * @return array + */ + public static function get_all_providers($context = null) { return array_values(AIProvider::get_records()); } public static function get_provider(int $id): AIProvider { diff --git a/search/engine/solrrag/classes/engine.php b/search/engine/solrrag/classes/engine.php index 526416dacfdf..a5d562b87b1c 100644 --- a/search/engine/solrrag/classes/engine.php +++ b/search/engine/solrrag/classes/engine.php @@ -63,6 +63,28 @@ public function is_server_ready() return true; } + /** + * Adds a document to the engine, optionally (if available) generating embeddings for it. + * @param $document + * @param $fileindexing + * @return bool + * @throws \coding_exception + */ + public function add_document($document, $fileindexing = false) { + $docdata = $document->export_for_engine(); + + if (!$this->add_solr_document($docdata)) { + return false; + } + + if ($fileindexing) { + // This will take care of updating all attached files in the index. + $this->process_document_files($document); + } + + return true; + } + /** * Adds a file to the search engine. * @@ -120,8 +142,12 @@ protected function add_stored_file($document, $storedfile) // This sets the true filename for Tika. $url->param('resource.name', $storedfile->get_filename()); - $url->param('extractOnly', "true"); -// $url->param("xpath", "/xhtml:html/xhtml:body/xhtml:div//node()"); + // If we're not doing embeddings, then we can just use the "original" implementation which will + // extract and index the file without passing the content back. + if (!$this->aiprovider->use_for_embeddings()) { + $url->param('extractOnly', "true"); + } + // A giant block of code that is really just error checking around the curl request. try { $requesturl = $url->out(false); @@ -168,41 +194,40 @@ protected function add_stored_file($document, $storedfile) debugging($message, DEBUG_DEVELOPER); } else { // The document was successfully indexed. - debugging("Got SOLR update/extract response"); - preg_match('/(?.*)<\/str>/imsU', $result, $streamcontent); - - if ($streamcontent[1]!== 0) { - $xmlcontent = html_entity_decode($streamcontent[1]); - $xml = simplexml_load_string($xmlcontent); - $filedoc['content'] = (string)$xml->body->asXML(); - $metadata = $xml->head->meta; - foreach($metadata as $meta) { - $name = (string)$meta['name']; - $content = (string)$meta['content']; - if ($content != null) { - $filedoc[$name] = $content; - } else { - $filedoc[$name] = ""; - + if ($this->aiprovider->use_for_embeddings() && $this->aiclient) { + preg_match('/(?.*)<\/str>/imsU', $result, $streamcontent); + debugging("Got SOLR update/extract response"); + if ($streamcontent[1]!== 0) { + $xmlcontent = html_entity_decode($streamcontent[1]); + $xml = simplexml_load_string($xmlcontent); + $filedoc['content'] = (string)$xml->body->asXML(); + $metadata = $xml->head->meta; + foreach($metadata as $meta) { + $name = (string)$meta['name']; + $content = (string)$meta['content']; + if ($content != null) { + $filedoc[$name] = $content; + } else { + $filedoc[$name] = ""; + + } } } - } - /** - * Since solr has given us back the content, we can now send it off to the AI provider. - */ - if ($this->aiprovider->use_for_embeddings() && $this->aiclient) { + /** + * Since solr has given us back the content, we can now send it off to the AI provider. + */ + // garnish $filedoc with the embedding vector. It would be nice if this could be done // via the export_file_for_engine() call above, that has no awareness of the engine. // We expect $filedoc['content'] to be set. $vector = $this->aiclient->embed_query($filedoc['content']); $vlength = count($vector); $vectorfield = "solr_vector_" . $vlength; - // TODO Check if a field of this length actually exists or not. $filedoc[$vectorfield] = $vector; - debugging("Using vector field $vectorfield"); } else { - // potentially warn that selected provider can't be used for - // generating embeddings for RAG. + // As before if embeddings is not in use, then we can bail + // as the document is already indexed. + return; } $this->add_solr_document($filedoc); return; From 44471633cf10461ff8724dfa8a4d91b24100a2c4 Mon Sep 17 00:00:00 2001 From: Michael hughes Date: Thu, 7 Mar 2024 22:57:20 +0000 Subject: [PATCH 07/21] Tweaked progress bar --- mod/xaichat/view.php | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/mod/xaichat/view.php b/mod/xaichat/view.php index 6ecb80a35a93..9e8d2fa6f106 100644 --- a/mod/xaichat/view.php +++ b/mod/xaichat/view.php @@ -79,24 +79,41 @@ if (isset($data->restartbutton)) { redirect(new \moodle_url('/mod/xaichat/view.php', array('id' => $cm->id))); } - $totalsteps = 3; + $stepnow = 0; + $totalsteps = 4; $aiclient = new \core\ai\AIClient($aiprovider); $progress = new \progress_bar(); $progress->create(); - $progress->update(1, $totalsteps,'Processing User Prompt'); + if (empty($_SESSION[$aicontextkey]['messages'])) { + // If the user has not made any prompts yet, we need to prime the interaction with + // a bunch of system and context specific prompts to constrain behaviour. + $totalsteps++; + $progress->update(1, $totalsteps,'Processing System Prompts'); + } + $progress->update(1, $totalsteps,'Looking for relevant context'); // $vector = $aiclient->embed_query($data->userprompt); $search = \core_search\manager::instance(true, true); - $search->similarity_search($settings); +// $search->similarity_search($settings); + // Perform "R" from RAG, finding documents from within the context that are similar to the user's prompt. + // Add the retrieved documents to the context for this chat by generating some system messages with the content + // returned + // Add the user's new prompt to the messages. + $progress->update(2, $totalsteps,'Attaching user prompt'); $_SESSION[$aicontextkey]['messages'][] = (object)[ "role" => "user", "content" => $data->userprompt ]; - $progress->update(2, $totalsteps, 'Waiting for response'); + // Pass the whole context over the AI to summarise. + $progress->update(3, $totalsteps, 'Waiting for response'); $airesults = $aiclient->chat($_SESSION[$aicontextkey]['messages']); $_SESSION[$aicontextkey]['messages'] = array_merge($_SESSION[$aicontextkey]['messages'],$airesults); - $progress->update(3, $totalsteps, 'Got Response'); + $progress->update(4, $totalsteps, 'Got Response'); + + // We stash the data in the session temporarily (should go into an activity-user store in database) but this + // is fast and dirty, and then we do a redirect so that we don't double up the request if the user hit's + // refresh. redirect(new \moodle_url('/mod/xaichat/view.php', ['id' => $cm->id])); } else if ($chatform->is_cancelled()) { $_SESSION[$aicontextkey] = [ @@ -104,10 +121,7 @@ ]; } else { // Clear session on first view of form. - // TODO prefix with system and context prompts. -// $_SESSION[$aicontextkey] = [ -// 'messages'=>[] -// ]; + $toform = [ 'id' => $id, 'aiproviderid' => $moduleinstance->aiproviderid, From c1f8f9c51727301cef5aa0cfbc3b7246a239c674 Mon Sep 17 00:00:00 2001 From: Michael hughes Date: Fri, 8 Mar 2024 17:14:00 +0000 Subject: [PATCH 08/21] attempting retrieval using vector --- mod/xaichat/view.php | 10 +- search/cli/result.txt | 2617 ---------------------- search/cli/result2.txt | 64 - search/engine/solrrag/classes/engine.php | 98 + search/engine/solrrag/tests/testdoc.txt | 0 5 files changed, 107 insertions(+), 2682 deletions(-) delete mode 100644 search/cli/result.txt delete mode 100644 search/cli/result2.txt create mode 100644 search/engine/solrrag/tests/testdoc.txt diff --git a/mod/xaichat/view.php b/mod/xaichat/view.php index 9e8d2fa6f106..cf44d40d0a9c 100644 --- a/mod/xaichat/view.php +++ b/mod/xaichat/view.php @@ -93,8 +93,16 @@ } $progress->update(1, $totalsteps,'Looking for relevant context'); // $vector = $aiclient->embed_query($data->userprompt); + $vector = [-0.027786661000000001,0.0090511510000000003,-0.044274903999999997,-0.014116816000000001,-0.027637671999999999,-0.026917553,-0.020126086000000001,0.072856180000000006,-0.018462362,-0.028258463000000001,-0.0012904718999999999,0.022807906999999999,0.0097464390000000008,-0.0071825663999999997,-0.013272539,-0.043306466000000002,-6.3679705e-5,0.00088850889999999997,-0.046609079999999997,0.053785439999999997,-0.0091194380000000005,-0.0021650129999999999,-0.013061468999999999,-0.041270266999999999,0.010646587000000001,0.042685675999999999,-0.013620182,0.016376499999999999,0.039755534000000002,0.041295100000000001,0.0019896391000000001,-0.063320810000000005,0.0015030932999999999,-0.061185284999999999,0.013483607999999999,-0.041568250000000001,0.0011445857000000001,-0.061880574000000001,0.012353766,0.022311273999999999,-0.023714265000000002,-0.0024816170000000002,0.047105714999999999,0.023143134999999999,-0.016500656999999998,-0.027513513,-0.0022922754,-0.012788320000000001,-0.0023962580000000001,-0.0092001410000000002,-0.017034540000000001,0.020970362999999999,0.0086290120000000001,0.018400282,-0.0040817090000000002,0.01021824,0.0033367584000000001,0.009131854,-0.0040910210000000002,0.0036564662999999998,0.001761498,0.0088152500000000002,-0.027215533,-0.089940383999999998,-0.038762268000000002,0.022820323999999999,-0.084676064999999995,0.0099450920000000009,-0.029947017999999999,0.027836324999999999,0.017940896000000001,0.0044790159999999997,0.016339251999999999,0.032008050000000003,0.023230046000000001,-0.0093987949999999997,0.016997293,-0.017940896000000001,0.014514123,0.039234072000000002,-0.010187201,-0.0056398977000000003,-0.0148990145,-0.046435259999999999,-0.0056709372000000001,0.0055716102999999999,0.013669845999999999,-0.028183969,-0.037446189999999997,-0.026768561,0.026867888999999999,-0.040425990000000002,-0.023602521000000001,0.024359887,0.016897965000000001,-0.054629717000000001,0.0031970800000000001,0.021255927000000001,0.048322469999999999,0.013433944,-0.0063879521999999998,-0.058155819999999997,-0.023453531999999999,0.019778442,0.012540002999999999,0.041841400000000001,0.031536248000000003,0.028010146999999999,-0.054331740000000003,0.035558979999999997,-0.048272807000000001,-0.0078468140000000006,-0.0033957337,-0.010255488,-0.022869987000000001,0.045963459999999998,0.0138933305,-0.0084303590000000001,0.02984769,0.017369768000000001,0.039383059999999998,-0.005764056,0.016438580000000001,-0.046956724999999998,0.033075809999999997,0.0016652752,0.028928919000000001,0.0010925943,-0.026048442000000002,-0.031039612000000001,0.0114287855,0.013483607999999999,-0.025216580999999998,0.021889135000000001,-0.037595179999999999,-0.019654283000000002,-0.014501707000000001,-0.013421529,-0.032504680000000001,0.017071787000000001,-0.064810709999999994,0.0017537382,-0.058304809999999999,-0.0014231662000000001,-0.027910819999999999,0.0062265460000000003,-0.00046249022000000001,0.016724143,-0.018251291999999999,-0.0136946775,-0.025551809000000002,-0.035608639999999997,-0.048819102000000003,0.021802223999999999,-0.078021170000000001,0.0022814115999999999,0.023950164999999999,-0.021255927000000001,-0.013942994,0.06351946,-0.048918429999999999,0.010801786000000001,-0.024993095999999999,0.0083248239999999998,0.0013168555,0.012502756,0.031958385999999998,0.0066921404000000002,-0.027861156000000002,-0.036055613,0.004379689,0.010776954,-0.023950164999999999,-0.022447848999999999,-0.048843934999999998,0.031412086999999998,-0.019592202999999999,0.0013766068999999999,0.035211336000000003,0.011006647,0.012285478000000001,0.0045938626999999996,0.027414186,-0.038091812000000003,0.051053955999999998,-0.010702458,0.013011806000000001,-0.067095230000000006,-0.041692406000000001,-0.035236169999999997,0.014787272000000001,0.035112009999999999,-0.013645014,-0.024732362000000001,-0.028680601999999999,0.025775295,-0.00060760044,0.019294222999999999,0.030319492999999999,0.0049942737000000003,-0.0044107290000000004,0.017096618000000001,-0.00083962149999999998,0.018598936,-0.031511415000000001,-0.02674373,-0.025067592,0.012155111999999999,-0.0054443479999999999,-0.028481949999999999,0.057659183000000003,0.0069280415999999996,0.011012855,-0.025166918,-0.011484656500000001,0.022062957000000001,-0.033795930000000002,0.068982440000000006,-0.016364085,0.048595619999999999,-0.028233632000000002,0.081398280000000003,0.012751073,-0.040972290000000001,-0.0039575505000000004,0.02291965,0.0042306990000000001,-0.041220605,-0.027439017,0.01542048,0.0006921834,0.016910382000000002,-0.024732362000000001,-0.033100642,-0.016463410000000001,-0.0070149525999999999,-0.040525316999999998,-0.017071787000000001,0.018139550000000001,-0.017742243000000001,-0.022770660000000002,-0.038191139999999998,-0.013868498999999999,0.012825568000000001,0.0084427740000000001,-0.010497596999999999,0.0013890227,-0.0070646160000000001,0.031362425999999999,-0.031337592999999997,0.035236169999999997,-0.0023947059999999999,0.021106936,0.019915016000000001,0.022969313000000002,0.025874620000000001,0.026917553,-0.040699140000000002,0.040972290000000001,-0.019964678,-0.032703336,-0.033795930000000002,0.017692579,-0.054331740000000003,-0.040574983000000002,-0.0091132299999999999,-0.016860718,-0.011503279999999999,-0.012428260999999999,0.064314074999999998,-0.032703336,0.021442164,-0.0040475655999999997,0.035658307,-0.057559859999999997,0.074594400000000005,-0.016041270999999999,0.022112619999999999,-0.012726240999999999,-0.04248702,-0.0079647650000000004,0.023403868000000001,-0.017332520000000001,-0.012527587999999999,-0.015308736999999999,0.011497073,0.070770316,0.0032995108000000001,0.0061892989999999997,-0.00035889552000000001,0.028630938000000002,-0.048943263000000001,0.023465946000000001,0.030220167999999999,-0.034367059999999998,-0.026247097,0.0021060377000000001,0.017034540000000001,-0.022447848999999999,-0.030145670999999999,0.055126349999999998,0.036105279999999997,0.010752121999999999,0.030319492999999999,-0.0055405706999999997,0.0056647295,-0.032281197999999997,0.0025530079999999998,0.021578738,-0.02124351,-0.054530393000000003,0.038265634,-0.035012685000000002,0.0023605625999999998,0.086811589999999994,-0.0062731057,-0.0061365315000000004,-0.0084179430000000006,0.019877767000000001,-0.035856961999999999,0.016513073999999999,-0.041915894000000002,-0.010007171,0.044771537,-0.024210898000000002,-0.045243338000000001,0.021678065999999999,-0.011801260500000001,-0.069379739999999995,-0.010789369,0.024111569999999999,-0.06287384,-0.0070832394999999996,0.046162113999999997,0.0070335763000000003,-0.033572446999999998,-0.0025499042,-0.051202945,-0.00016218198,-0.0058602790000000004,-0.012887647,0.006400368,0.019964678,-0.001118202,-0.061483264000000003,0.027612839,0.059546391999999997,-0.039556883000000001,-0.044101080000000001,0.048620447999999997,0.019095569999999999,0.009318092,0.014303054000000001,-0.021988460000000001,0.0292269,-0.028953751999999999,0.027339690999999999,0.049315735999999999,-0.026942383,0.027662503000000001,-0.071167624999999998,0.031387255000000003,-0.038091812000000003,0.031461753000000002,-0.025253828999999998,-0.018412698000000002,-0.0069466652000000004,-0.0095167450000000001,0.0036005949999999998,-0.012260647,0.023453531999999999,0.057410865999999998,-0.013794004,0.055374670000000001,0.01556947,-0.00093739629999999995,-0.0081261700000000003,-0.023192799,-0.03866294,0.027588008000000001,-0.023639767999999999,-0.029475215999999999,0.0095415770000000007,-0.041468921999999998,-0.030046346000000002,-0.0028649562000000002,-0.032330860000000003,-0.0049601300000000001,-0.052593517999999999,-0.023379036999999998,-0.00059091660000000002,-0.034093909999999998,0.014315469000000001,-0.0088649130000000003,0.0031815602,-0.029003412999999999,-0.082093570000000005,0.055672649999999997,-0.0099575080000000003,0.019455627999999999,0.044721875000000001,0.0017521861,-0.022720996,0.033199970000000002,-0.05408342,0.010013378999999999,-0.055076689999999998,0.04966338,-0.0080206359999999994,0.028134304999999998,0.0063103530000000003,-0.012242022999999999,0.026545078,0.003845808,0.0039823823999999997,-0.0095229530000000007,-0.0056430018000000002,-0.015855035,-0.0057609519999999997,0.043231970000000002,0.025725630999999999,-0.041742069999999999,0.024210898000000002,0.04055015,-0.0022286442000000002,0.0026942384,0.045739975000000002,0.036328763,0.030096008,0.033994585000000001,0.015110083999999999,-0.025949117000000001,-0.029475215999999999,0.030716800999999998,-0.011751598,-0.029798029,-0.010243072000000001,-0.0026880304999999998,-0.031983215000000002,0.021442164,-0.017866401000000001,0.053835105000000001,0.020858620000000001,-0.04966338,-0.088102840000000002,0.026470581,0.033448286000000001,0.010491389,0.040128009999999999,-0.020933113999999999,-0.024471631000000001,-0.022820323999999999,-0.0037092335999999999,-0.012130281,0.053040490000000003,0.0094050030000000003,0.030989948999999999,0.053338469999999999,-0.0049849619999999999,-0.042313200000000002,-0.056864570000000003,-0.0141044,-0.011155636999999999,-0.015743291,0.020746877,-0.034689869999999998,0.018474778000000001,0.024782025999999999,-0.070968970000000006,-0.039979022000000003,-0.0028261568000000001,0.00065571179999999996,-0.0036626741999999999,0.025502145,-0.029673868999999999,0.060887302999999997,-0.0094360420000000004,-0.020088836999999998,0.036403257000000001,-0.0090201115000000005,-0.017655331999999999,0.025266245,0.015693627000000002,-0.015345984,-0.019952264000000001,-0.029971850000000001,0.0090821909999999999,-0.016438580000000001,-0.015668795999999999,-0.00081866980000000001,0.016537904999999999,0.0099326759999999997,0.01601644,-0.012688993500000001,-0.012912478999999999,-0.0056833530000000004,0.029450384999999999,-0.061234946999999998,0.011906795,-0.010317567,0.016773807000000002,0.015544638,-0.012862815499999999,0.037768999999999997,-0.047503023999999998,0.0041065407000000003,-0.011074934,0.0097836859999999998,-0.0054195164999999998,0.06595297,0.00099481960000000007,0.042611179999999999,-0.045293002999999998,-0.00044774642000000001,0.0090635669999999998,-0.037520683999999999,0.0063444966999999996,-0.018785172999999999,-0.00020447344,-0.035782464,0.021044858,0.0014805895,0.032728170000000001,0.016848301999999999,-0.010447933,0.020846203000000001,0.026843058,0.014588617999999999,-0.011211508,-0.0072073980000000003,0.045566152999999998,0.044746703999999998,0.0075798732999999997,-0.02011367,-0.029723532,0.017556004,0.0050253132000000001,0.065754320000000005,0.00530467,-0.019815689000000001,-0.03203288,-0.0012268406999999999,-0.022323688000000001,-0.0048608035000000001,-0.032827492999999999,0.040103180000000002,-0.027165870000000002,-0.0057392245999999996,0.057013559999999998,0.0094546660000000005,0.020895866999999999,0.011739181,0.043927260000000003,-0.024335057,-0.024868937000000001,-0.053338469999999999,-0.034044247,-0.0079771800000000004,-0.00023337909,0.0064810709999999997,0.0060868681999999997,0.0056740413,-0.032827492999999999,0.0043610656000000001,-0.011137012999999999,0.040773634000000003,0.028829591000000002,-0.0048359715999999999,-0.0034888523999999999,-0.024993095999999999,0.015631549000000002,0.031312760000000002,0.013272539,-0.062824174999999996,0.046956724999999998,0.0018794485000000001,0.025253828999999998,-0.0089890714999999993,-0.023043807999999999,0.065903305999999995,0.011981291,0.016972460000000002,-0.027364521999999999,-0.032504680000000001,-0.0097153989999999996,0.0049352985999999996,-0.019244560000000001,-0.0045907585999999997,-0.040922627000000003,0.041915894000000002,-0.024744779000000001,0.033522780000000002,-0.0058602790000000004,-0.0064189920000000001,-0.034242901999999999,0.016239925999999998,0.024881354000000001,0.025502145,0.0094360420000000004,-0.022584422,0.03143692,0.0081075469999999997,-0.0030775775999999999,-0.00019118461999999999,-0.01903349,-0.042611179999999999,0.0077102400000000001,-0.069330080000000002,0.035683140000000002,-0.026718898000000001,0.0069839129999999996,-0.0022022604999999999,-0.034143575000000002,0.0041034365999999996,-0.02542765,0.0042648427000000003,-0.028556444,0.0013494472,-0.024918599999999999,-0.0038240803000000002,-0.0059565017000000001,-0.015743291,-0.049290907000000002,0.021988460000000001,-0.013632597999999999,0.033994585000000001,-0.035434822999999997,0.059149085999999997,-0.0028416766000000002,0.018946579000000002,-0.011776429,-0.0068535465,-0.036552247000000003,-0.010267904,0.018176798000000001,-0.022671332999999998,-0.0051215360000000003,0.018462362,-0.011484656500000001,-0.0084055269999999994,0.0044666002999999999,0.057311542,-0.011174261,0.0035757634000000002,0.021752560000000001,-0.020523392000000001,0.015494974,0.0041375802999999997,0.009299468,0.0055964420000000001,-0.028159136000000001,-0.011621230999999999,0.0020749980000000002,-0.041617910000000001,6.7996150000000001e-5,0.025291075999999999,-0.020262658999999999,0.015271489500000001,0.0075612496999999997,0.02982286,-0.0021588050000000002,0.037992484999999999,0.023341787999999999,0.01873551,0.008945616,0.041841400000000001,0.027712166,0.0093677550000000002,-0.0031365528999999998,-0.035037513999999999,0.012924895,0.030393988,0.0041406843999999996,-0.031958385999999998,-0.00031932,-0.0055126350000000001,-0.0024241938000000001,-0.016662065,0.0067418039999999997,0.034044247,-0.0124034295,-0.039879695,-0.0053605409999999999,-0.021255927000000001,0.014663113,-0.018139550000000001,0.005847863,-0.044672209999999997,0.0041096450000000003,0.0013277193999999999,-0.034218070000000003,0.035782464,0.033771097999999999,0.033175137,-0.010826617,0.0020346467,0.015445311999999999,-0.0035354117999999999,-0.011074934,-0.036279100000000002,0.015134915000000001,-0.021926383000000001,0.023130719000000001,-0.010336191,0.033175137,-0.032057714000000001,0.019517709000000001,-0.0085669330000000005,0.020200579999999999,-0.0010111154000000001,0.010193408500000001,-0.011447409,0.0048235557,-0.023316956999999999,0.0010491389999999999,0.033969751999999999,0.017742243000000001,0.015681212999999999,0.022336104999999998,0.012316518,-0.0022814115999999999,0.012167528,-0.032877160000000002,0.0075364179999999996,0.010174785,-0.0044479766000000004,-0.0292269,-0.024049493000000002,-0.016587568,-0.0073253483999999999,0.040748804999999999,-0.00400411,0.0037309613999999998,0.013818835999999999,0.014886597999999999,0.0060868681999999997,-0.022820323999999999,0.017046954,-0.012496548,-0.0065493584000000001,-0.010547261,0.015544638,-0.0030418820000000001,-0.0011647615,0.013545687000000001,0.02540282,0.023403868000000001,0.0014107504,-0.0014565337999999999,-0.028084641,-0.036204603000000002,0.0086165960000000007,0.0036130110000000002,-0.0051743034,-0.021094522000000001,-0.010516220999999999,-0.015395648,-0.054778710000000001,-0.013433944,0.011975082999999999,-0.0083062009999999992,0.0028168450000000001,-0.021144183,-0.0035012683000000002,0.029351057,0.0060527246,-0.00080470194000000004,-0.011832301,-0.013371865,-0.015879865999999999,0.034069080000000002,-0.018487192999999999,0.017704995000000001,0.03116377,0.027439017,0.036229434999999997,0.027935651999999998,0.019182479999999998,0.0022705477000000002,0.024161234,0.020697213999999999,-0.025005512000000001,0.012502756,-0.027215533,-0.018648600000000001,0.049812370000000002,-0.0078716454999999998,-0.023863254,-0.034789197000000001,0.02704171,-0.0069466652000000004,-0.022646501999999999,0.0011391537,-0.059298075999999998,0.024347473000000001,0.039234072000000002,-0.014700361,-0.042909160000000002,-0.0035074762999999999,0.0075302099999999999,-0.0067542195000000003,0.0087097159999999993,0.031660404000000003,0.040997119999999998,0.046559419999999997,0.0082565369999999996,0.025191749999999999,-0.061830907999999997,0.010143746,-0.024335057,-0.036105279999999997,0.030691968,0.030120839999999999,-0.03143692,-0.014315469000000001,0.011776429,0.0022208841999999999,-0.0045473029999999999,0.019666698,-0.017233193000000001,-0.01072729,0.0078964770000000007,0.035757635000000003,-0.039258899999999999,0.044920526000000002,0.052295540000000001,-0.018437530000000001,0.031511415000000001,0.048918429999999999,-0.011192884,0.0092932599999999994,-0.026768561,-0.0061210114999999997,-0.0030279143000000001,0.0025607680000000002,-0.0055747143999999998,0.01822646,0.016984875999999999,-0.043430626,0.03866294,-0.0077412794000000004,0.022907235000000001,-0.018164381,-0.023602521000000001,0.0094546660000000005,-0.0041034365999999996,0.033572446999999998,0.020734460999999999,-0.028283294,0.0014542057999999999,0.013185628,0.018276122999999998,0.00029254835999999998,0.024794442999999999,0.0078840620000000007,0.0099140530000000008,-0.016239925999999998,-0.029897355,0.004181036,0.004742853,0.0072322297000000004,0.0090014880000000002,0.0022301962999999999,-0.029947017999999999,0.032604010000000003,0.027736996999999999,-0.00025219685999999997,-0.023503195000000001,-0.037048879999999999,-0.046733240000000002,0.023999829,-0.0027407978,-0.038836761999999997,-0.0076481607,-0.017481509999999999,0.0051029124000000004,-0.026818225000000001,-0.021094522000000001,0.014278222,0.012682785,-0.019654283000000002,-0.039457555999999998,-0.0060278926999999998,0.015755707000000001,-0.011379122,-0.0027609735000000001,0.0029503150999999998,-0.00075891849999999998,-0.027339690999999999,0.044796370000000002,-0.038687773000000002,-0.029996682,0.019753608999999998,-0.041518588000000002,-0.026594739999999999,0.0014673977000000001,0.0040630850000000001,0.042909160000000002,0.014116816000000001,-0.024335057,-0.036676407000000001,-0.04303332,-0.023279709999999999,0.020225410999999999,-0.017270440000000001,0.0029518672,-0.00062079227000000001,0.0030930974000000001,0.0095353690000000001,0.016984875999999999,-0.0038178724000000001,0.013421529,-0.0020765502,-0.022596838000000001,0.0026771666000000001,-0.0011507936,0.019381134000000001,0.00080082199999999997,0.028606106999999999,-0.022236780000000001,-0.015035588000000001,-0.014464459000000001,-0.022211946999999999,-0.016997293,0.0095353690000000001,0.034888524999999997,-0.010832825000000001,0.016302003999999998,-0.010180992999999999,0.0025607680000000002,0.036800563000000001,0.016935213000000001,-0.025924283999999999,0.0087904184999999992,0.021206263,0.030890622999999999,-0.020635133999999999,-0.044796370000000002,0.019145234000000001,0.034516048000000001,0.00067317159999999996,-0.022795490000000002,0.033895257999999998,0.035385158,-0.025055175999999998,-0.015470143,-0.0094608739999999993,-0.032157036999999999,-0.012713825,-0.0013432392,0.017680162999999999,0.010640379,0.036552247000000003,-0.024769612,0.017655331999999999,0.014563786,-0.0080765079999999996,0.00035617955,0.011434994,-0.010776954,-0.027339690999999999,0.0063134569999999997,0.023205215000000001,0.025998779,-0.020461312999999998,-0.043207143000000003,0.0037713130000000001,0.0099078445000000005,-0.02181464,0.0059937489999999996,-0.034590546,0.0054971150000000003,-0.009131854,-0.017357351,0.0088835370000000004,0.010615546999999999,0.017258024,0.040574983000000002,0.0040289415,-0.026445750000000001,0.033249630000000002,0.011453616999999999,-0.0092063490000000008,-0.019617036000000001,-0.026470581,0.014389964999999999,0.051004290000000001,-0.0090759824999999995,-0.0091442699999999995,0.040351495000000001,0.043604450000000003,-0.015333569,-0.014166478999999999,-0.0078033586,-0.0041344759999999998,-0.0018002974999999999,-0.049787539999999998,0.017034540000000001,0.036030779999999998,0.025452481999999998,0.0065555659999999997,0.032082542999999998,0.032877160000000002,0.018164381,-0.021578738,-0.013396697000000001,-0.024570957000000001,-0.002633711,-0.045640646999999999,0.0066114375000000001,0.025725630999999999,0.02512967,0.022373351999999999,-0.0075860815000000003,0.014601034000000001,-0.017183529999999999,0.035484485000000003,0.034118740000000002,-0.052841840000000001,-0.012136488000000001,0.034863690000000003,0.0075426260000000002,0.035310662999999999,-0.023292126,0.025824957999999999,-0.001839097,-0.0016311316000000001,-0.002408674,-0.0099885470000000004,0.053139816999999999,0.01476244,0.0076854080000000003,-0.014253389999999999,0.0046745655000000001,0.032157036999999999,0.039830033000000001,-0.001766154,-0.0096471109999999999,-0.0051339519999999998,0.011838507999999999,0.038315295999999999,0.026818225000000001,-0.013806419,0.044945359999999997,-0.00072787893999999998,-0.013545687000000001,-0.04998619,0.044995019999999997,0.0072322297000000004,-0.020275075,0.013570518,-0.029475215999999999,-0.042064882999999997,-0.002006711,-0.013843668,0.013297369999999999,-0.014414796000000001,0.0038116643999999999,-0.012297895,0.020473728,-0.016488243,0.009665735,-0.0072632692999999998,0.014340301,0.022236780000000001,-0.015730876000000001,-0.019915016000000001,-0.015010756,-0.0048328675,0.035136840000000003,0.0051991353000000002,-0.015842617999999999,-0.00064484790000000002,0.029698702,-0.017978142999999999,-0.0021029337999999998,0.029152404999999999,-0.00044076249999999999,0.019778442,-0.036055613,-0.0016776911000000001,0.014774855,-0.017816736999999999,0.0078778540000000001,-0.031635574999999999,0.018363034,-0.015805370999999999,0.021007609999999999,-0.042735339999999997,-0.0079399329999999997,0.010125122,0.016537904999999999,0.010298943,-0.014191311,-0.030145670999999999,-0.0027345898999999998,-8.9287390000000001e-5,0.016860718,-0.031635574999999999,-0.020275075,-0.030567810000000001,0.0095912399999999991,-0.0048328675,-0.0094546660000000005,0.024558541999999999,-0.0058230315,-0.072309880000000007,-0.010261696000000001,0.070422670000000007,-0.0094050030000000003,0.0068907940000000004,0.0073253483999999999,0.010832825000000001,0.0036099068999999998,0.011037686,-0.0107397055,-0.0041965559999999997,-0.022410599999999999,-0.0051215360000000003,0.00020932338999999999,-0.0036005949999999998,-0.0088524980000000003,0.019617036000000001,0.0030791296000000001,0.018810006000000001,-0.018363034,0.0076109130000000004,-0.069230749999999994,-0.031238265000000001,0.00063864,-0.00027858053,-0.016798638000000001,-0.009131854,0.0053419173000000004,0.047329202000000001,0.014961093999999999,0.0059751253999999997,0.014799687000000001,-0.015470143,0.019542540000000001,0.022596838000000001,-0.0091380620000000006,-0.0073998435000000003,0.0077723189999999998,0.014228558000000001,-0.014067152499999999,-0.013322202,0.07732588,-0.016264757000000001,-0.0079585565,-0.00093506834999999996,-0.015259073999999999,-0.0026942384,0.0095788239999999997,-0.0090076949999999996,-0.0022177803,-0.021727730000000001,-0.015507391000000001,-0.069777049999999993,0.020461312999999998,0.040053516999999997,0.010932150999999999,-0.014563786,0.0033833177000000001,0.012030954,-0.013297369999999999,-0.013446359999999999,0.0039575505000000004,0.0053295013,0.0087035075000000007,-0.0063258730000000001,-0.012304102000000001,-0.021827055000000001,-0.0041841400000000003,-0.0083744869999999999,0.024359887,0.0030496417999999999,0.024595789999999999,-0.0055933379999999998,-0.016066104000000001,0.027488680000000001,0.0092746359999999993,0.021851888,-0.04934057,-0.01682347,0.0048483876000000002,0.0049290903999999998,0.01075833,-0.016922796,-0.021640818999999999,-0.0036130110000000002,-0.012105449000000001,0.022435432000000002,-0.0030030825,-0.044721875000000001,-0.0083124080000000003,-0.010398271000000001,0.0071949824000000001,0.010174785,0.021963630000000001,0.032157036999999999,-0.0024288497000000001,0.0068038832000000002,0.010677626000000001,-0.031337592999999997,0.065505999999999995,-0.0039730705000000002,0.021678065999999999,-0.0045690310000000003,-0.0011026822000000001,0.0068349229999999997,-0.014961093999999999,0.0099637149999999997,0.016364085,-6.7511159999999996e-5,0.0021945005,0.010236864,-0.0102244485,-0.015159746999999999,0.011795053,-0.014402379999999999,0.016190263,-0.018896916999999999,-0.0025157606999999999,-0.021876718999999999,-0.013011806000000001,0.0019104880999999999,-0.022261610000000001,0.034814030000000003,0.057162549999999999,-0.019554955999999998,-0.014961093999999999,0.034342225999999997,-0.018884499999999999,0.0074991705000000001,0.01822646,0.051451261999999998,0.028034977999999999,-0.0048080359999999999,0.012273063000000001,0.00272683,-0.0092870520000000005,-0.005379165,0.015581885,-0.0060372044999999997,-0.0060930760000000002,-0.015917113,-0.0030279143000000001,-0.00052612139999999997,-0.01933147,-0.044001753999999997,0.0084862300000000009,-0.024037076000000001,-0.015470143,-0.01189438,0.043256804000000003,0.033249630000000002,0.018300956,-0.023416283999999999,-0.0083558629999999998,0.023167968000000001,-0.0064376154999999996,-0.010801786000000001,0.013905747,0.024012245000000002,-0.033249630000000002,-0.034367059999999998,0.022075371999999999,0.027836324999999999,0.00032630393999999999,-0.017419430999999999,0.00094050022999999999,0.017704995000000001,0.021082105,0.013781588000000001,0.012751073,0.0046838773,0.046609079999999997,0.014923845,0.012837984,-0.023230046000000001,0.02925173,0.0094670819999999999,0.012446885,0.023267293000000001,-0.026420920000000001,0.028407452999999999,0.020548224,-0.0088214580000000008,0.014154063999999999,-0.056715580000000002,0.031461753000000002,-0.037669674,0.022100205000000001,0.045590980000000003,-0.013880915000000001,0.019194896999999999,-0.045491660000000003,0.0051401596999999999,-0.020622720000000001,0.023652184999999999,-0.0029425554,-0.00054164125999999998,-0.018934164,-0.0076543684000000001,-0.0060372044999999997,-0.019170064000000001,-0.014365133,-0.018437530000000001,0.00094282825000000002,-0.035981119999999998,0.0148990145,0.0067418039999999997,0.024868937000000001,0.0067976750000000004,-0.027513513,0.0031520726999999999,0.018425113999999999,-0.0084179430000000006,-0.055076689999999998,0.0037092335999999999,-0.0029518672,-0.0102244485,-0.024198482,0.028730266000000001,-0.015408063,0.037247534999999998,9.1178860000000006e-5,-0.013545687000000001,-0.026967215999999999,0.010149953,0.02121868,-0.016612400999999999,-0.0014456699,0.0034205653000000001,0.0046528378000000002,-0.0098830129999999995,0.023366620000000001,-0.0074184669999999998,0.0091628934999999998,0.030170504000000001,0.00042912264999999999,0.025949117000000001,-0.021119352000000001,-0.039904527000000002,0.0085048539999999992,-0.039681040000000001,0.017407015000000001,0.0012423604,0.031859060000000002,0.016165430000000001,-0.017704995000000001,0.022149868,-0.017990559999999999,0.012813152,-0.014427212,0.0025048967999999998,0.0016311316000000001,-0.011875755999999999,0.020386817000000002,0.019828104999999999,-0.033721436,-0.013098716999999999,0.0054971150000000003,0.0067914673000000004,-0.0031365528999999998,0.023391452,-0.033175137,-0.029996682,0.030393988,-0.0050842889999999996,0.005286046,-0.027885988,-0.034590546,0.0061458433000000003,-3.8526704999999997e-6,-0.017034540000000001,0.039705873000000003,0.029996682,0.012018538,-0.0081882499999999993,-0.030642306000000001,0.00064019200000000004,0.0036968180000000002,-0.0063755362999999999,-0.011348083,0.022733413000000001,-0.014427212,0.017779489999999998,-0.010994230000000001,-0.0082813690000000002,0.0074681310000000002,0.015656380000000001,0.038712605999999997,-0.028804759999999999,0.026147770000000001,-0.0040289415,0.0018437530000000001,-0.025874620000000001,-0.013682260999999999,-0.0046745655000000001,0.040152844,0.017071787000000001,0.0044852240000000003,0.0011779532999999999,-0.0037961446000000002,0.0028059809999999999,-0.00084738140000000005,-0.020858620000000001,0.011844716,0.022472679999999998,-0.0052208630000000001,-0.0095850320000000003,-0.0063041453000000003,-0.012086825000000001,0.024099154000000001,0.019977095,-0.0052084469999999997,0.024918599999999999,0.025626303999999999,-0.00034027173999999998,-0.025328323,0.011962667,0.010994230000000001,-0.017258024,0.015308736999999999,-0.037421357000000002,0.0037216494999999998,-0.014377548,-0.013520855,0.022720996,-0.018797589999999999,0.0083869029999999994,-0.032231532,0.0050656646000000003,-0.021082105,0.016450995999999999,0.00023202109999999999,-0.0082006660000000006,-0.020064005999999999,-0.020212995000000001,0.0058602790000000004,-0.017431846000000001,-0.0059844369999999996,-0.020647550000000001,-0.030716800999999998,0.016066104000000001,-0.015768124000000001,0.052593517999999999,0.0048576994000000002,-0.0070646160000000001,-0.010752121999999999,0.022460263000000001,-0.025179334000000001,0.051401599999999999,-0.02432264,-0.0041375802999999997,0.017680162999999999,-0.0025731840000000001,-0.036403257000000001,0.00044541843999999997,-0.0022255399999999998,0.00075659057000000002,0.041170944000000001,-0.019157647999999999,-0.00062932812999999998,0.024111569999999999,0.019095569999999999,-0.016115766,0.0096719430000000006,0.017407015000000001,-0.018474778000000001,0.00049663379999999996,-0.021615986,-0.0077164475999999997,-0.0051215360000000003,-0.023130719000000001,0.0086290120000000001,-0.0028990997999999999,0.015507391000000001,-0.011782637,0.0044821202999999997,0.026247097,-0.017469093000000002,0.028779929999999999,0.032554346999999997,0.013918162,-0.018611351000000002,-0.010013378999999999,0.0016063,0.04303332,-0.036800563000000001,0.0086352200000000007,-0.0033181347000000002,-0.012962141999999999,-0.0087966260000000001,0.00067161960000000002,-0.011490864999999999,-0.023006559999999999,0.0034143572999999999,-0.024782025999999999,0.025315909000000001,-0.0031241369999999999,-0.023304539999999999,0.0038923675,0.022845154999999999,0.030220167999999999,0.019617036000000001,-0.036825396000000003,-0.0062420660000000001,-0.0088835370000000004,-0.0070459920000000001,0.018971412,0.0065245264999999998,-0.00065105589999999997,0.017320103999999999,0.0023559065999999998,-0.031089275999999999,0.010901111999999999,-0.025216580999999998,0.021082105,-0.030195335,-0.026445750000000001,-0.0082565369999999996,-0.05025934,0.01933147,-0.015619133,0.0064127840000000004,-0.0092622199999999998,-0.014948677,-0.028829591000000002,0.0039482387000000004,-0.024409551000000002,-0.012695201999999999,0.038240799999999998,0.0064251999999999998]; + $search = \core_search\manager::instance(true, true); -// $search->similarity_search($settings); + $settings = [ + 'similarity' => true, + 'vector' => $vector, + ]; + $limit = 0; + $docs = $search->search((object)$settings); + exit(); // Perform "R" from RAG, finding documents from within the context that are similar to the user's prompt. // Add the retrieved documents to the context for this chat by generating some system messages with the content // returned diff --git a/search/cli/result.txt b/search/cli/result.txt deleted file mode 100644 index 0fb10804a7f5..000000000000 --- a/search/cli/result.txt +++ /dev/null @@ -1,2617 +0,0 @@ -Running full reindex of site -============================ -Processing area: Text block content - No new documents to index. -Processing area: Courses - Processed 5 records containing 5 documents (1 batch), in 0.4 seconds. -Processing area: Course custom fields - No new documents to index. -Processing area: Course sections - No new documents to index. -Processing area: Messages - received - No new documents to index. -Processing area: Messages - sent - No new documents to index. -Processing area: Course Teacher - No new documents to index. -Processing area: Users - Processed 103 records containing 103 documents (2 batches), in 0.5 seconds. -Processing area: Assignment - activity information - Processed 22 records containing 22 documents (1 batch), in 0.2 seconds. -Processing area: Attendance - activity information - Processed 1 records containing 1 documents, in 0.1 seconds. -Processing area: BigBlueButton - activity information - No new documents to index. -Processing area: BigBlueButton - tags information - No new documents to index. -Processing area: Book - resource information - No new documents to index. -Processing area: Book - chapters - No new documents to index. -Processing area: Chat - activity information - No new documents to index. -Processing area: Choice - activity information - No new documents to index. -Processing area: Database - activity information - No new documents to index. -Processing area: Database - entries - No new documents to index. -Processing area: Feedback - activity information - No new documents to index. -Processing area: Folder -Array -( - [0] => Array - ( - [areaid] => mod_folder-activity - [id] => mod_folder-activity-1-solrfile287 - [itemid] => 1 - [title] => Assessment_and_Feedback_Policy.pdf - [contextid] => 268 - [courseid] => 4 - [owneruserid] => 0 - [modified] => 2024-03-04T17:17:08Z - [type] => 2 - [solr_filegroupingid] => mod_folder-activity-1 - [solr_fileid] => 287 - [solr_filecontenthash] => 6de274633203c03b996b248e841f2a1f17cdb537 - [solr_fileindexstatus] => 1 - [solr_vector] => - ) - -) -++ http://172.22.0.6:8983/solr/moodle/update/extract?wt=xml&uprefix=ignored_&captureAttr=true&fmap.content=solr_filecontent&fmap.media_white_point=ignored_mwp&fmap.media_black_point=ignored_mbp&fmap.areaid=ignored_areaid&literal.mdltmp_areaid=mod_folder-activity&fmap.mdltmp_areaid=areaid&fmap.id=ignored_id&literal.mdltmp_id=mod_folder-activity-1-solrfile287&fmap.mdltmp_id=id&fmap.itemid=ignored_itemid&literal.mdltmp_itemid=1&fmap.mdltmp_itemid=itemid&fmap.title=ignored_title&literal.mdltmp_title=Assessment_and_Feedback_Policy.pdf&fmap.mdltmp_title=title&fmap.contextid=ignored_contextid&literal.mdltmp_contextid=268&fmap.mdltmp_contextid=contextid&fmap.courseid=ignored_courseid&literal.mdltmp_courseid=4&fmap.mdltmp_courseid=courseid&fmap.owneruserid=ignored_owneruserid&literal.mdltmp_owneruserid=0&fmap.mdltmp_owneruserid=owneruserid&fmap.modified=ignored_modified&literal.mdltmp_modified=2024-03-04T17%3A17%3A08Z&fmap.mdltmp_modified=modified&fmap.type=ignored_type&literal.mdltmp_type=2&fmap.mdltmp_type=type&fmap.solr_filegroupingid=ignored_solr_filegroupingid&literal.mdltmp_solr_filegroupingid=mod_folder-activity-1&fmap.mdltmp_solr_filegroupingid=solr_filegroupingid&fmap.solr_fileid=ignored_solr_fileid&literal.mdltmp_solr_fileid=287&fmap.mdltmp_solr_fileid=solr_fileid&fmap.solr_filecontenthash=ignored_solr_filecontenthash&literal.mdltmp_solr_filecontenthash=6de274633203c03b996b248e841f2a1f17cdb537&fmap.mdltmp_solr_filecontenthash=solr_filecontenthash&fmap.solr_fileindexstatus=ignored_solr_fileindexstatus&literal.mdltmp_solr_fileindexstatus=1&fmap.mdltmp_solr_fileindexstatus=solr_fileindexstatus&fmap.solr_vector=ignored_solr_vector&literal.mdltmp_solr_vector&fmap.mdltmp_solr_vector=solr_vector&resource.name=Assessment_and_Feedback_Policy.pdf&extractOnly=true ++ -* line 142 of /search/engine/solrrag/classes/engine.php: call to debugging() -* line 970 of /search/engine/solr/classes/engine.php: call to search_solrrag\engine->add_stored_file() -* line 776 of /search/engine/solr/classes/engine.php: call to search_solr\engine->process_document_files() -* line 318 of /search/classes/engine.php: call to search_solr\engine->add_document_batch() -* line 1283 of /search/classes/manager.php: call to core_search\engine->add_documents() -* line 108 of /search/cli/indexer.php: call to core_search\manager->index() -++ Got SOLR update/extract response ++ -* line 184 of /search/engine/solrrag/classes/engine.php: call to debugging() -* line 970 of /search/engine/solr/classes/engine.php: call to search_solrrag\engine->add_stored_file() -* line 776 of /search/engine/solr/classes/engine.php: call to search_solr\engine->process_document_files() -* line 318 of /search/classes/engine.php: call to search_solr\engine->add_document_batch() -* line 1283 of /search/classes/manager.php: call to core_search\engine->add_documents() -* line 108 of /search/cli/indexer.php: call to core_search\manager->index() - - - - - 0 - 64 - -<?xml version="1.0" encoding="UTF-8"?> -<html xmlns="http://www.w3.org/1999/xhtml"> -<head> -<meta name="date" -content="2019-05-29T13:52:29Z"/> -<meta name="pdf:PDFVersion" -content="1.5"/> -<meta name="xmp:CreatorTool" - content="Acrobat PDFMaker 15 for Word"/> -<meta -name="pdf:docinfo:title" - content="Assessment and Feedback Policy"/> -<meta -name="Company" content="University of Strathclyde"/> -<meta -name="stream_content_type" content="application/json"/> -<meta -name="pdf:hasXFA" content="false"/> -<meta -name="access_permission:can_print_degraded" content="true"/> -<meta -name="subject" content="Version 1.2"/> -<meta name="language" -content="EN-GB"/> -<meta name="dc:format" - content="application/pdf; version=1.5"/> -<meta -name="pdf:docinfo:creator_tool" - content="Acrobat PDFMaker 15 for Word"/> -<meta -name="access_permission:fill_in_form" content="true"/> -<meta -name="pdf:encrypted" content="false"/> -<meta name="dc:title" - content="Assessment and Feedback Policy"/> -<meta -name="xmp:CreateDate" content="2019-05-29T14:52:24Z"/> -<meta -name="modified" content="2019-05-29T13:52:29Z"/> -<meta -name="pdf:docinfo:custom:SourceModified" -content="D:20190528113809"/> -<meta name="cp:subject" -content="Version 1.2"/> -<meta name="pdf:docinfo:subject" - content="Version 1.2"/> -<meta -name="pdf:docinfo:custom:_TemplateID" content="TC017730709991"/> -<meta -name="pdf:hasMarkedContent" content="true"/> -<meta name="xmp:ModifyDate" - content="2019-05-29T14:52:29Z"/> -<meta -name="pdf:docinfo:creator" content="Ashley"/> -<meta name="meta:author" -content="Ashley"/> -<meta name="meta:creation-date" -content="2019-05-29T13:52:24Z"/> -<meta name="created" - content="2019-05-29T13:52:24Z"/> -<meta -name="access_permission:extract_for_accessibility" -content="true"/> -<meta name="Creation-Date" -content="2019-05-29T13:52:24Z"/> -<meta name="resourceName" - content="Assessment_and_Feedback_Policy.pdf"/> -<meta -name="Author" content="Ashley"/> -<meta name="producer" - content="Adobe PDF Library 15.0"/> -<meta -name="pdf:docinfo:producer" content="Adobe PDF Library 15.0"/> -<meta -name="_TemplateID" content="TC017730709991"/> -<meta -name="dc:description" content="Version 1.2"/> -<meta -name="access_permission:modify_annotations" content="true"/> -<meta -name="dc:creator" content="Ashley"/> -<meta name="description" -content="Version 1.2"/> -<meta name="dcterms:created" -content="2019-05-29T13:52:24Z"/> -<meta name="Last-Modified" - content="2019-05-29T13:52:29Z"/> -<meta -name="dcterms:modified" content="2019-05-29T13:52:29Z"/> -<meta -name="xmpMM:DocumentID" - content="uuid:19d0024b-2633-4ae5-a2f5-e0f1b491b856"/> -<meta -name="Last-Save-Date" content="2019-05-29T13:52:29Z"/> -<meta -name="pdf:docinfo:modified" content="2019-05-29T13:52:29Z"/> -<meta -name="meta:save-date" content="2019-05-29T13:52:29Z"/> -<meta -name="Content-Type" content="application/pdf"/> -<meta name="stream_size" -content="101489"/> -<meta name="xmp:MetadataDate" -content="2019-05-29T14:52:29Z"/> -<meta name="X-Parsed-By" - content="org.apache.tika.parser.DefaultParser"/> -<meta -name="X-Parsed-By" - content="org.apache.tika.parser.pdf.PDFParser"/> -<meta -name="creator" content="Ashley"/> -<meta name="dc:language" -content="EN-GB"/> -<meta name="pdf:producer" - content="Adobe PDF Library 15.0"/> -<meta -name="access_permission:assemble_document" content="true"/> -<meta -name="xmpTPg:NPages" content="9"/> -<meta name="pdf:hasXMP" - content="true"/> -<meta -name="access_permission:extract_content" content="true"/> -<meta -name="pdf:docinfo:custom:Company" - content="University of Strathclyde"/> -<meta -name="access_permission:can_print" content="true"/> -<meta -name="SourceModified" content="D:20190528113809"/> -<meta -name="access_permission:can_modify" content="true"/> -<meta -name="pdf:docinfo:created" content="2019-05-29T13:52:24Z"/> -<title>Assessment and Feedback Policy</title> -</head> -<body> - <div class="page"> - -<p/> -<p> -</p> -<p> -</p> -<p> -</p> -<p> ASSESSMENT AND -FEEDBACK POLICY -</p> -<p> -</p> -<p>Version No. Description Author Approval Effective Date -</p> -<p>1.2 Assessment and -</p> -<p>Feedback Policy – -</p> -<p>applicable to both -</p> -<p>undergraduate and -</p> -<p>postgraduate taught -</p> -<p>courses -</p> -<p>Assessment -</p> -<p>and -</p> -<p>Feedback -</p> -<p>Working -</p> -<p>Group -</p> -<p>Senate From academic -</p> -<p>year 2019-20 -</p> -<p> Version 1.2 -</p> -<p> - -the place of useful learning -The University of Strathclyde is a charitable body, registered in -Scotland, number SC015263 -</p> -<p> -</p> -<p> - - </p> -<p/> -</div> -<div class="page"> - -<p/> -<p> Assessment and Feedback Policy -</p> -<p> 1 -</p> -<p> -</p> -<p>1. RATIONALE -</p> -<p>In higher education, “assessment” describes any process that involves the evaluation or -appraisal of a student’s knowledge, understanding, skills, attitudes or abilities. In line with the -QAA UK Quality Code for Higher Education, assessment is taken to be an integral component -of teaching and learning, and serves multiple purposes. In addition to enabling evaluation and -measurement of students’ learning, effective assessment shapes and enhances student -learning. Assessments should therefore be designed to facilitate students’ attainment of -intended learning outcomes, and permit the measurement of such attainments against explicit -criteria. A holistic approach to assessment at course level can also support the development of -distinctive Strathclyde graduate attributes: graduates that are engaged, enquiring, enterprising, -and ethically and globally aware. Recent publications on assessment and feedback in higher -education suggest a ‘transformation’ is required to ensure that assessment and feedback -practices enable students to develop such attributes and the skills needed to be lifelong -learners in the 21st century.1 -</p> -<p>This approach recognises that assessment is central to learning and teaching, and is not -designed solely to measure student learning. Also essential to enhancing learning, is the -provision of continuous feedback to students on their learning; it is recognised that feedback -takes different forms (e.g. replies to posts on a discussion forum), but in relation to assessment, -useful feedback is feedback that is specific in informing learners the extent to which they have -met published assessment criteria, and explains to them what they need to do to improve. -Ensuring feedback is clear, specific, and adopts a supportive tone is most likely to foster -student engagement with feedback. The role of feedback in effectively supporting student -learning should be recognised at the assessment design stage, where factors such as the -timing of the release of feedback and the scheduling of subsequent assessments are -considered (particularly where feedback can be used by students to improve the development -of future work), as well as the provision of opportunities for students to clarify feedback. -</p> -<p>The University expects that feedback will normally be returned to students within 15 working -days of assessment submission2. The University’s VLE, Myplace, provides a platform for the -electronic return of grades and feedback, and where permitted by the nature of an assessment, -staff are expected to implement the submission of assessments online through Myplace, in -order to promote effectiveness and efficiency in each stage of the assessment and feedback -process. In addition, Myplace should be used to manage assessments that are submitted offline -in order to implement the Policy and Procedure on Extensions to Coursework Submission and -Procedures for the Recording and Publication of Marks. Staff should also be cognisant of -specific issues relevant to equality, diversity and inclusion in designing assessments and -providing feedback, which may affect particular groups of students.3 -</p> -<p> -1 For example: Boud, D. &amp; Falachikov, N. (Eds.). (2007). Rethinking Assessment in Higher Education. Oxon: -Routledge; Higher Education Academy. (2012). A Marked Improvement. Transforming assessment in higher -education. York: The Higher Education Academy; Merry, S., Price, M., Carless, D. &amp; Taras, M. (Eds.). (2013). -Reconceptualising Feedback in Higher Education. Developing Dialogue with Students. Oxon: Routledge; -Carless, D. (2015). Excellence in university assessment: learning from award-winning practice. London: -Routledge. -2 Exceptions to this are dissertations or assessments with credit weightings of 40 credits or higher, and where -double marking is required. (See the Policy on Moderation and Double Marking). -3 Staff are advised to consult the University’s equality impact assessment information where relevant. </p> -<p/> -<div class="annotation"> - <a href="https://www.qaa.ac.uk/quality-code/advice-and-guidance/assessment">https://www.qaa.ac.uk/quality-code/advice-and-guidance/assessment</a> -</div> -<div class="annotation"> - <a href="https://www.strath.ac.uk/equalitydiversity/eia/">https://www.strath.ac.uk/equalitydiversity/eia/</a> -</div> -</div> -<div class="page"> - -<p/> -<p> Assessment and Feedback Policy -</p> -<p> 2 -</p> -<p> -</p> -<p>Previous work on assessment at Strathclyde (through the Re-Engineering Assessment -Practices4 and Peer Evaluation in Education Review5 projects, together with the work on Peer -and Community Personal Development Planning), have emphasised the importance of -providing students with opportunities to self- and peer-assess, and for students to develop the -capacity to evaluate, reflect on6 and manage their own learning through the effective use of -feedback. Published research also highlights the importance of developing students’ -understanding of the criteria and standards by which their work is assessed, and suggests that -providing students with the opportunity to analyse and discuss the qualities of carefully selected -exemplars is an effective learning activity to achieve this7. It is widely recognised that these will, -in the long-term, help develop attitudes and skills that foster engagement and independence in -learning. - -The aim of the current policy is to develop an institution-wide approach to assessment and -feedback that enhances the effectiveness of assessment in aiding students to achieve the -necessary knowledge and skills described in a class or programme’s intended learning -outcomes. The policy is underpinned by principles of assessment and feedback (outlined in -section 3) that reflect this aim and a commitment to ensuring our assessment and feedback -practices are fair, transparent, and continuously reviewed. In short, to ensure they continue to -be fit for purpose. This policy should be adhered to across the University. The implementation -of the policy will be monitored by the Quality Assurance Committee. -</p> -<p> -2. SCOPE -</p> -<p>This policy applies to all undergraduate and postgraduate taught programmes offered by the -University. This policy should be read in conjunction with other relevant University policies and -procedures. The University website contains policies and procedures on a range of -assessment-related matters, including: ensuring equality, guidance for staff on supporting -students with a disability, guides for staff marking assessments in undergraduate and -postgraduate taught courses, guidance for students and staff on avoiding plagiarism, guidance -for staff in dealing with possible cases of plagiarism, policies on classifying honours degrees -and other degree awards, and on awarding motivational merit and distinction, guidance on the -compensation scheme, the role of external examiners in taught courses, the procedure for -dealing with student discipline cases, guidance for staff on the appeals procedure and -accounting for students’ personal circumstances. -</p> -<p> -</p> -<p> -</p> -<p> - -4 REAP Project, accessed at http://www.reap.ac.uk/ -5 PEER Project, accessed from: http://www.reap.ac.uk/PEER.aspx. The site contains a PEER toolkit -containing guidance for staff on using peer review. -6 Winstone, N. &amp; Nash. R. (2016). The Developing Engagement with Feedback Toolkit (DEFT). The Higher -Education Academy. https://www.heacademy.ac.uk/knowledge-hub/developing-engagement-feedback-toolkit- -deft -7 Carless, D. &amp; Boud, D. (2018): The development of student feedback literacy: enabling uptake of feedback, -Assessment &amp; Evaluation in Higher Education, DOI: 10.1080/02602938.2018.1463354 </p> -<p/> -<div class="annotation"> - <a href="http://www.strath.ac.uk/staff/policies/academic/">http://www.strath.ac.uk/staff/policies/academic/</a> -</div> -<div class="annotation"> - <a href="http://www.reap.ac.uk/">http://www.reap.ac.uk/</a> -</div> -<div class="annotation"> - <a href="http://www.reap.ac.uk/PEER.aspx">http://www.reap.ac.uk/PEER.aspx</a> -</div> -<div class="annotation"> - <a href="https://www.heacademy.ac.uk/knowledge-hub/developing-engagement-feedback-toolkit-deft">https://www.heacademy.ac.uk/knowledge-hub/developing-engagement-feedback-toolkit-deft</a> -</div> -<div class="annotation"> - <a href="https://www.heacademy.ac.uk/knowledge-hub/developing-engagement-feedback-toolkit-deft">https://www.heacademy.ac.uk/knowledge-hub/developing-engagement-feedback-toolkit-deft</a> -</div> -</div> -<div class="page"> - -<p/> -<p> Assessment and Feedback Policy -</p> -<p> 3 -</p> -<p> -</p> -<p>3. ASSESSMENT AND FEEDBACK PRINCIPLES - -</p> -<p>These principles have been widely consulted on across the University, and must be adhered to -throughout the institution. -</p> -<p>PRINCIPLE 1. ASSESSMENT AND FEEDBACK PRACTICES PROMOTE EFFECTIVE -STUDENT LEARNING -</p> -<p>1.1 Assessment and feedback activities are designed to foster student engagement, to -support and measure students’ attainment of knowledge, understanding, and -transferable skills. -</p> -<p>1.2 A range of assessment methods are used, increasing in complexity across a -programme, and taking into consideration student and staff workloads. -</p> -<p>1.3 Assessment and feedback practices align with intended learning outcomes and -assessment criteria, and provide opportunities for students to be active participants in -the process, including opportunities for students to discuss feedback. -</p> -<p>1.4 Timely, constructive, and supportive feedback helps students understand the extent to -which they have fulfilled the assessment criteria, and supports students to use their -feedback in the development of subsequent work. -</p> -<p>1.5 The timing of assessments and return of feedback should be scheduled to enable -students to use feedback to inform their approach to subsequent assessments. The -release of feedback should consider the ability of students to gain further feedback and -support, and avoid release in close proximity to periods where the university is closed -</p> -<p>1.6 Where examinations are used, students should receive at least general feedback soon -after the examination. Students who wish to view their exam scripts following the -provision of general feedback should be facilitated to do so under supervision of staff. -</p> -<p>PRINCIPLE 2. ASSESSMENT AND FEEDBACK PRACTICES ARE APPROPRIATE, FAIR, -AND TRANSPARENT -</p> -<p>2.1 Assessment tasks are appropriate to disciplinary and/or professional contexts. -2.2 Assessment applies rigorous academic standards related to and across, the discipline(s) -</p> -<p>or professional context and is based on clearly defined assessment criteria. -2.3 Assessment grading and feedback is based solely on students’ achievement against -</p> -<p>criteria and standards. -2.4 Assessment and feedback practices are fair, inclusive and accessible to all students. -</p> -<p>PRINCIPLE 3. ASSESSMENT AND FEEDBACK PRACTICES ARE CLEARLY -COMMUNICATED TO STUDENTS AND STAFF -</p> -<p>3.1 Students must be made aware at the beginning of a class of the details of assessments, -including the purpose, weighting, and timing of assessment. -</p> -<p>3.2 Students must be made aware at the beginning of a class of the nature and timing of -feedback. Where unexpected problems cause a delay in the return of feedback within 15 -working days, an explanation will be provided to students, along with a new date for the -return of feedback. -</p> -<p>3.3 All students and staff are made aware at the start of a class of the criteria and standards -used to assess and provide feedback on students’ work. </p> -<p/> -</div> -<div class="page"> - -<p/> -<p> Assessment and Feedback Policy -</p> -<p> 4 -</p> -<p> -</p> -<p>3.4 Students must be made aware of relevant policies and procedures around academic -honesty in assessment, and of procedures associated with personal circumstances, -extensions to the deadlines of coursework, and late submission of coursework. -</p> -<p>3.5 There are opportunities for students and staff to engage in a dialogue around -assessment and feedback, including opportunities for students to clarify feedback. -</p> -<p>3.6 In each programme the processes for marking, moderation, and feedback are -appropriate and fair and are explained to students and staff. -</p> -<p>PRINCIPLE 4. ASSESSMENT AND FEEDBACK PRACTICES ARE CONTINUOUSLY -REVIEWED -</p> -<p>4.1 The timing of assessment activities at cohort level, including the timing of examinations, -are monitored and reviewed, in order to minimise bunching. -</p> -<p>4.2 Assessment and feedback activities, and the outcomes of assessment, are reviewed via -the class and course approval and/or review processes, and external examining -processes. -</p> -<p>4.3 Assessment and feedback activities are continuously reviewed to ensure effective -alignment with a programme’s intended learning outcomes and graduate attributes. -</p> -<p>4.4 Opportunities to develop effective practice and innovation in assessment and feedback -are available to all staff involved in assessment. -</p> -<p> -4. ANONYMOUS MARKING -</p> -<p>4.1 Anonymous marking (when the identity of a student is not known to a marker and/or exam -board) will be used for all assessments wherever possible and appropriate, including formal -written examinations at both undergraduate and postgraduate levels. -</p> -<p>4.2 Where anonymous marking is not possible, for example where an alternative method of -assessment has been used for a student with a disability, additional scrutiny by a second -marker may be appropriate. -</p> -<p>4.3 Other exceptions will be where the mode of assessment (e.g., presentations or oral -examinations) prevents anonymity, or where there is pedagogical justification (e.g., the use -of ipsative feedback). -</p> -<p>4.4 Double marking is effective practice for assessments that are not marked anonymously and -for dissertations, or comparable assessments. -</p> -<p> -5. RESPONSIBILITIES FOR IMPLEMENTATION -</p> -<p>5.1 The University assumes responsibility for: -- Providing staff with opportunities to develop effective practice in assessment and -</p> -<p>feedback through the Organisational Staff and Development Unit (Strathclyde Teaching -Excellence Programme) Education Enhancement (the University’s VLE, Myplace offers -various tools to support effective practice in assessment and feedback, including -handling peer review/assessment activities, online assessment submission and return of -feedback, and enabling efficient communication with different student and staff groups; -the Sharing Practice in Enhancing Learning and Teaching (SPELT) platform, and </p> -<p/> -<div class="annotation"> - <a href="https://www.strath.ac.uk/hr/learninganddevelopment/step/">https://www.strath.ac.uk/hr/learninganddevelopment/step/</a> -</div> -<div class="annotation"> - <a href="https://www.strath.ac.uk/hr/learninganddevelopment/step/">https://www.strath.ac.uk/hr/learninganddevelopment/step/</a> -</div> -<div class="annotation"> - <a href="http://spelt.strath.ac.uk/pages/viewpage.action;jsessionid=01EB624BAAB6873613854F3DC2EE7433?pageId=8979188">http://spelt.strath.ac.uk/pages/viewpage.action;jsessionid=01EB624BAAB6873613854F3DC2EE7433?pageId=8979188</a> -</div> -</div> -<div class="page"> - -<p/> -<p> Assessment and Feedback Policy -</p> -<p> 5 -</p> -<p> -</p> -<p>TESTA), the Disability and Wellbeing Service (for ensuring assessments are accessible -to all students), and the Equality and Diversity Office; -</p> -<p>- Providing adequate resources, including information technology systems, to support -effective practice (Information Services Directorate, Student Business, Education -Enhancement); and -</p> -<p>- Monitoring implementation of this policy through the Quality Assurance Committee -(Education Enhancement). -</p> -<p>5.2 Faculties are responsible for: overseeing and receiving reports from programme exam -boards; scrutinising external examiners’ reports and responding to them; ensuring -consistency in the implementation of this and other related policies across programmes, -and sharing effective practice within and between faculties. -</p> -<p>5.3 Heads of Department/School are responsible for: instructing colleagues within their -Department/School to note and adhere to the Assessment and Feedback Policy and -Procedures for Recording and Publication of Marks, Procedures on Preparing and -Conducting Exams, and other related policies such as the Policy on the Late Submission of -Coursework, Policy on Extensions to Coursework Submission, the Policy on Moderation -and Double Marking, and Guidance on Marking Assessments in Undergraduate and -Postgraduate Taught courses; and for ensuring that marking of assessments is sufficiently -resourced to enable feedback to be returned within 15 working days of assessment -submission. -</p> -<p>5.4 Programme Exam Boards are responsible for: making sure assessments across a -programme are marked fairly; ensuring university and faculty regulations are adhered to; -and responding to points made by External Examiners. -</p> -<p>5.5 Programme Leaders/Directors are responsible for ensuring a coherent and effective -approach to assessment and feedback is taken across a programme of study. This -approach will involve: -</p> -<p>- Ensuring a range of assessment methods are used throughout a programme of study, -as appropriate to the discipline; -</p> -<p>- Monitoring the intensity and equity of assessments across and between programmes, -with due consideration to joint honours degrees; -</p> -<p>- Designing and reviewing the Assessment Schedule and Exam Timetable within a -programme and requesting amendments where there is assessment/exam bunching; -</p> -<p>- Developing effective moderation and/or double marking procedures, in line with the -Policy on Moderation and Double Marking; -</p> -<p>- Ensuring procedures are in place for monitoring students’ use of the Policies and -Procedures on Extensions to Coursework Submission and Late Submission of -Coursework, as a mechanism for identifying students who may require support; and -</p> -<p>- Ensuring procedures related to assuring fairness in assessment, such as moderation -and the role of External Examiners, are communicated to students. -</p> -<p>5.6 Year Coordinators and/or Module Coordinators are responsible for: -</p> -<p>- Having an overview of summative assessment activities within a programme of study to -avoid, where possible, a concentration of assessment deadlines, including examinations; </p> -<p/> -</div> -<div class="page"> - -<p/> -<p> Assessment and Feedback Policy -</p> -<p> 6 -</p> -<p> -</p> -<p>- Making sure the details of all assessments are provided at the start of a class; -- Ensuring assessment criteria that is aligned to intended learning outcomes and the -</p> -<p>University’s Guidance on Marking Assessments in Undergraduate and Postgraduate -Taught programmes, is transparent and made available to students at the start of a -class; -</p> -<p>- Providing opportunities for students to use feedback prior to the submission of any -subsequent related assessments; -</p> -<p>- Working with students to develop effective practice in assessment and feedback, -including: providing students with opportunities to develop their understanding of the -criteria and standards used to assess their work and supporting students to engage with -and use feedback; -</p> -<p>- Obtaining feedback from students in relation to assessment and feedback for module(s) -for which they are responsible, and for communicating any actions on this feedback to -students; and -</p> -<p>- For undertaking effective moderation and/or double marking procedures. -</p> -<p>5.7 Staff involved in assessing students’ work are responsible for: -</p> -<p>- Designing assessments that effectively facilitate and measure students’ achievement of -intended learning outcomes; -</p> -<p>- Assessing students’ work according to published assessment criteria which are aligned -to intended learning outcomes and the University’s Guidance on Marking Assessments -in Undergraduate and Postgraduate Taught programmes; -</p> -<p>- Providing timely, informative and helpful feedback which enables students to further -improve their learning and performance wherever possible; -</p> -<p>- Informing students when, where and how feedback will be provided; -- Engaging in dialogue with students about assessment and feedback; and -- Continuously reviewing their approaches to assessment and feedback to reflect effective -</p> -<p>practice. -</p> -<p>5.8 Students are expected to be responsible for their own learning through: -</p> -<p>- Understanding the requirements of individual assessments, and actively engaging with -assessment tasks by devoting appropriate time and effort; -</p> -<p>- Developing an understanding of the relationship between intended learning outcomes -and assessment criteria, and standards in their programme of study; -</p> -<p>- Ensuring their academic work is authentic and honestly produced; -- Finding out where, how and when work is submitted and how and when feedback is -</p> -<p>provided; -- Actively engaging, reflecting, and using provided feedback; -- Understanding the academic policies and procedures related to assessment and -</p> -<p>feedback, including Personal Circumstances Procedure, Policy and Procedures for the -Late Submission of Coursework, Policy on Moderation and Double Marking, Policy on -Extensions to the Submission of Coursework, and Compensation and Motivational Merit; -</p> -<p>- Seeking academic support when needed, for example, if feedback needs to be clarified; -and -</p> -<p>- Participating in the development of assessment and feedback practices at class and -programme levels. </p> -<p/> -</div> -<div class="page"> - -<p/> -<p> Assessment and Feedback Policy -</p> -<p> 7 -</p> -<p> -</p> -<p>6. DEFINITIONS OF TERMS - -Assessment The process of measuring the performance of students -</p> -<p>(as in examinations, assignments and other assessable -work) that enables students to monitor their progress -and contributes to their academic results. - -</p> -<p>Assessment criteria - -</p> -<p> -</p> -<p>Specific criteria against which pieces of work are -assessed. An understanding of the criteria must be -shared by markers and students. Feedback should -relate to these criteria. -</p> -<p>Assessment bunching The perception of assessments within a programme of -study being scheduled too closely together. - -</p> -<p>Assessment schedule - -</p> -<p>A timeline of all cohort level assessment. -</p> -<p>Exam bunching Where the scheduling of two exams falls within a 23 -hour period. - -</p> -<p>Feedback Information provided to students on the quality of their -performance in relation to assessment criteria, which -forms the basis of improved student learning. - -</p> -<p>Formative assessment - -</p> -<p>This type of assessment normally has no or low -weighting in the final mark for a module or programme. -The goal of formative assessment is to provide an -opportunity for students to monitor their learning and -provide feedback to teachers that can be used to -review their teaching. - -</p> -<p>Graduate attributes Qualities, skills, dispositions, and understanding that -students are expected to develop. At Strathclyde these -are referred to as the 4 E’s – engaged, enquiring, -enterprising and ethically and globally aware. - -</p> -<p>Intended learning outcomes What the student is expected to be able to do or -demonstrate, in terms of particular knowledge, skills -and understanding, by the end of a module or -programme. - -</p> -<p>Ipsative feedback - -</p> -<p>This type of feedback compares a student’s -performance on a current assessment with -performance on a previous assessment. - -</p> -<p>Moderation The process of checking that assessment criteria are -consistently applied across markers in marking -students’ work. - -</p> -<p>Module The individual components of a programme, normally -worth 20 or 10 credits. - -</p> -<p>Module evaluation The process of obtaining feedback from students on all -aspects of teaching, learning, and assessment within a </p> -<p/> -</div> -<div class="page"> -<p/> -<p> Assessment and Feedback Policy -</p> -<p> 8 -</p> -<p> -</p> -<p>module. - -</p> -<p>Programme The full degree programme leading to an award. - -</p> -<p>Seen double marking Where an assessment is independently marked by two -markers, but where the second marker has access to -the marks or comments of the first marker. - -</p> -<p>Summative assessment Assessment is summative when the grading of an -assessment contributes to the final grade for a class or -course. The aim of summative assessment is to -evaluate students’ attainment of the intended learning -outcomes within a unit of study. - -</p> -<p> -Unseen double marking - -</p> -<p>Where an assessment is independently marked by two -markers who do not have access to the grades or -comments of the other marker. - -</p> -<p>Working days The University’s standard working week is normally -Monday to Friday, covering five working days. Fifteen -working days would normally equate to 21 calendar -days, but may be longer where the period includes -University closure days. -</p> -<p> - </p> -<p/> -</div> -</body> -</html> - - - - 2019-05-29T13:52:29Z - - - 1.5 - - - Acrobat PDFMaker 15 for Word - - - Assessment and Feedback Policy - - - University of Strathclyde - - - application/json - - - false - - - true - - - Version 1.2 - - - EN-GB - - - application/pdf; version=1.5 - - - Acrobat PDFMaker 15 for Word - - - true - - - false - - - Assessment and Feedback Policy - - - 2019-05-29T14:52:24Z - - - 2019-05-29T13:52:29Z - - - D:20190528113809 - - - Version 1.2 - - - Version 1.2 - - - TC017730709991 - - - true - - - 2019-05-29T14:52:29Z - - - Ashley - - - Ashley - - - 2019-05-29T13:52:24Z - - - 2019-05-29T13:52:24Z - - - true - - - 2019-05-29T13:52:24Z - - - Assessment_and_Feedback_Policy.pdf - - - Ashley - - - Adobe PDF Library 15.0 - - - Adobe PDF Library 15.0 - - - TC017730709991 - - - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - - - Version 1.2 - - - true - - - Ashley - - - Version 1.2 - - - 2019-05-29T13:52:24Z - - - 2019-05-29T13:52:29Z - - - 2019-05-29T13:52:29Z - - - Assessment and Feedback Policy - - - uuid:19d0024b-2633-4ae5-a2f5-e0f1b491b856 - - - 2019-05-29T13:52:29Z - - - 2019-05-29T13:52:29Z - - - 2019-05-29T13:52:29Z - - - application/pdf - - - 101489 - - - 2019-05-29T14:52:29Z - - - org.apache.tika.parser.DefaultParser - org.apache.tika.parser.pdf.PDFParser - - - Ashley - - - EN-GB - - - Adobe PDF Library 15.0 - - - true - - - 9 - - - true - - - 407 - 4047 - 3287 - 2892 - 2798 - 3055 - 2950 - 2113 - 932 - - - true - - - University of Strathclyde - - - true - - - D:20190528113809 - - - true - - - 2019-05-29T13:52:24Z - - - -<?xml version="1.0" encoding="UTF-8"?> -<html xmlns="http://www.w3.org/1999/xhtml"> -<head> -<meta name="date" -content="2019-05-29T13:52:29Z"/> -<meta name="pdf:PDFVersion" -content="1.5"/> -<meta name="xmp:CreatorTool" - content="Acrobat PDFMaker 15 for Word"/> -<meta -name="pdf:docinfo:title" - content="Assessment and Feedback Policy"/> -<meta -name="Company" content="University of Strathclyde"/> -<meta -name="stream_content_type" content="application/json"/> -<meta -name="pdf:hasXFA" content="false"/> -<meta -name="access_permission:can_print_degraded" content="true"/> -<meta -name="subject" content="Version 1.2"/> -<meta name="language" -content="EN-GB"/> -<meta name="dc:format" - content="application/pdf; version=1.5"/> -<meta -name="pdf:docinfo:creator_tool" - content="Acrobat PDFMaker 15 for Word"/> -<meta -name="access_permission:fill_in_form" content="true"/> -<meta -name="pdf:encrypted" content="false"/> -<meta name="dc:title" - content="Assessment and Feedback Policy"/> -<meta -name="xmp:CreateDate" content="2019-05-29T14:52:24Z"/> -<meta -name="modified" content="2019-05-29T13:52:29Z"/> -<meta -name="pdf:docinfo:custom:SourceModified" -content="D:20190528113809"/> -<meta name="cp:subject" -content="Version 1.2"/> -<meta name="pdf:docinfo:subject" - content="Version 1.2"/> -<meta -name="pdf:docinfo:custom:_TemplateID" content="TC017730709991"/> -<meta -name="pdf:hasMarkedContent" content="true"/> -<meta name="xmp:ModifyDate" - content="2019-05-29T14:52:29Z"/> -<meta -name="pdf:docinfo:creator" content="Ashley"/> -<meta name="meta:author" -content="Ashley"/> -<meta name="meta:creation-date" -content="2019-05-29T13:52:24Z"/> -<meta name="created" - content="2019-05-29T13:52:24Z"/> -<meta -name="access_permission:extract_for_accessibility" -content="true"/> -<meta name="Creation-Date" -content="2019-05-29T13:52:24Z"/> -<meta name="resourceName" - content="Assessment_and_Feedback_Policy.pdf"/> -<meta -name="Author" content="Ashley"/> -<meta name="producer" - content="Adobe PDF Library 15.0"/> -<meta -name="pdf:docinfo:producer" content="Adobe PDF Library 15.0"/> -<meta -name="_TemplateID" content="TC017730709991"/> -<meta -name="dc:description" content="Version 1.2"/> -<meta -name="access_permission:modify_annotations" content="true"/> -<meta -name="dc:creator" content="Ashley"/> -<meta name="description" -content="Version 1.2"/> -<meta name="dcterms:created" -content="2019-05-29T13:52:24Z"/> -<meta name="Last-Modified" - content="2019-05-29T13:52:29Z"/> -<meta -name="dcterms:modified" content="2019-05-29T13:52:29Z"/> -<meta -name="xmpMM:DocumentID" - content="uuid:19d0024b-2633-4ae5-a2f5-e0f1b491b856"/> -<meta -name="Last-Save-Date" content="2019-05-29T13:52:29Z"/> -<meta -name="pdf:docinfo:modified" content="2019-05-29T13:52:29Z"/> -<meta -name="meta:save-date" content="2019-05-29T13:52:29Z"/> -<meta -name="Content-Type" content="application/pdf"/> -<meta name="stream_size" -content="101489"/> -<meta name="xmp:MetadataDate" -content="2019-05-29T14:52:29Z"/> -<meta name="X-Parsed-By" - content="org.apache.tika.parser.DefaultParser"/> -<meta -name="X-Parsed-By" - content="org.apache.tika.parser.pdf.PDFParser"/> -<meta -name="creator" content="Ashley"/> -<meta name="dc:language" -content="EN-GB"/> -<meta name="pdf:producer" - content="Adobe PDF Library 15.0"/> -<meta -name="access_permission:assemble_document" content="true"/> -<meta -name="xmpTPg:NPages" content="9"/> -<meta name="pdf:hasXMP" - content="true"/> -<meta -name="access_permission:extract_content" content="true"/> -<meta -name="pdf:docinfo:custom:Company" - content="University of Strathclyde"/> -<meta -name="access_permission:can_print" content="true"/> -<meta -name="SourceModified" content="D:20190528113809"/> -<meta -name="access_permission:can_modify" content="true"/> -<meta -name="pdf:docinfo:created" content="2019-05-29T13:52:24Z"/> -<title>Assessment and Feedback Policy</title> -</head> -<body> - <div class="page"> - -<p/> -<p> -</p> -<p> -</p> -<p> -</p> -<p> ASSESSMENT AND -FEEDBACK POLICY -</p> -<p> -</p> -<p>Version No. Description Author Approval Effective Date -</p> -<p>1.2 Assessment and -</p> -<p>Feedback Policy – -</p> -<p>applicable to both -</p> -<p>undergraduate and -</p> -<p>postgraduate taught -</p> -<p>courses -</p> -<p>Assessment -</p> -<p>and -</p> -<p>Feedback -</p> -<p>Working -</p> -<p>Group -</p> -<p>Senate From academic -</p> -<p>year 2019-20 -</p> -<p> Version 1.2 -</p> -<p> - -the place of useful learning -The University of Strathclyde is a charitable body, registered in -Scotland, number SC015263 -</p> -<p> -</p> -<p> - - </p> -<p/> -</div> -<div class="page"> - -<p/> -<p> Assessment and Feedback Policy -</p> -<p> 1 -</p> -<p> -</p> -<p>1. RATIONALE -</p> -<p>In higher education, “assessment” describes any process that involves the evaluation or -appraisal of a student’s knowledge, understanding, skills, attitudes or abilities. In line with the -QAA UK Quality Code for Higher Education, assessment is taken to be an integral component -of teaching and learning, and serves multiple purposes. In addition to enabling evaluation and -measurement of students’ learning, effective assessment shapes and enhances student -learning. Assessments should therefore be designed to facilitate students’ attainment of -intended learning outcomes, and permit the measurement of such attainments against explicit -criteria. A holistic approach to assessment at course level can also support the development of -distinctive Strathclyde graduate attributes: graduates that are engaged, enquiring, enterprising, -and ethically and globally aware. Recent publications on assessment and feedback in higher -education suggest a ‘transformation’ is required to ensure that assessment and feedback -practices enable students to develop such attributes and the skills needed to be lifelong -learners in the 21st century.1 -</p> -<p>This approach recognises that assessment is central to learning and teaching, and is not -designed solely to measure student learning. Also essential to enhancing learning, is the -provision of continuous feedback to students on their learning; it is recognised that feedback -takes different forms (e.g. replies to posts on a discussion forum), but in relation to assessment, -useful feedback is feedback that is specific in informing learners the extent to which they have -met published assessment criteria, and explains to them what they need to do to improve. -Ensuring feedback is clear, specific, and adopts a supportive tone is most likely to foster -student engagement with feedback. The role of feedback in effectively supporting student -learning should be recognised at the assessment design stage, where factors such as the -timing of the release of feedback and the scheduling of subsequent assessments are -considered (particularly where feedback can be used by students to improve the development -of future work), as well as the provision of opportunities for students to clarify feedback. -</p> -<p>The University expects that feedback will normally be returned to students within 15 working -days of assessment submission2. The University’s VLE, Myplace, provides a platform for the -electronic return of grades and feedback, and where permitted by the nature of an assessment, -staff are expected to implement the submission of assessments online through Myplace, in -order to promote effectiveness and efficiency in each stage of the assessment and feedback -process. In addition, Myplace should be used to manage assessments that are submitted offline -in order to implement the Policy and Procedure on Extensions to Coursework Submission and -Procedures for the Recording and Publication of Marks. Staff should also be cognisant of -specific issues relevant to equality, diversity and inclusion in designing assessments and -providing feedback, which may affect particular groups of students.3 -</p> -<p> -1 For example: Boud, D. &amp; Falachikov, N. (Eds.). (2007). Rethinking Assessment in Higher Education. Oxon: -Routledge; Higher Education Academy. (2012). A Marked Improvement. Transforming assessment in higher -education. York: The Higher Education Academy; Merry, S., Price, M., Carless, D. &amp; Taras, M. (Eds.). (2013). -Reconceptualising Feedback in Higher Education. Developing Dialogue with Students. Oxon: Routledge; -Carless, D. (2015). Excellence in university assessment: learning from award-winning practice. London: -Routledge. -2 Exceptions to this are dissertations or assessments with credit weightings of 40 credits or higher, and where -double marking is required. (See the Policy on Moderation and Double Marking). -3 Staff are advised to consult the University’s equality impact assessment information where relevant. </p> -<p/> -<div class="annotation"> - <a href="https://www.qaa.ac.uk/quality-code/advice-and-guidance/assessment">https://www.qaa.ac.uk/quality-code/advice-and-guidance/assessment</a> -</div> -<div class="annotation"> - <a href="https://www.strath.ac.uk/equalitydiversity/eia/">https://www.strath.ac.uk/equalitydiversity/eia/</a> -</div> -</div> -<div class="page"> - -<p/> -<p> Assessment and Feedback Policy -</p> -<p> 2 -</p> -<p> -</p> -<p>Previous work on assessment at Strathclyde (through the Re-Engineering Assessment -Practices4 and Peer Evaluation in Education Review5 projects, together with the work on Peer -and Community Personal Development Planning), have emphasised the importance of -providing students with opportunities to self- and peer-assess, and for students to develop the -capacity to evaluate, reflect on6 and manage their own learning through the effective use of -feedback. Published research also highlights the importance of developing students’ -understanding of the criteria and standards by which their work is assessed, and suggests that -providing students with the opportunity to analyse and discuss the qualities of carefully selected -exemplars is an effective learning activity to achieve this7. It is widely recognised that these will, -in the long-term, help develop attitudes and skills that foster engagement and independence in -learning. - -The aim of the current policy is to develop an institution-wide approach to assessment and -feedback that enhances the effectiveness of assessment in aiding students to achieve the -necessary knowledge and skills described in a class or programme’s intended learning -outcomes. The policy is underpinned by principles of assessment and feedback (outlined in -section 3) that reflect this aim and a commitment to ensuring our assessment and feedback -practices are fair, transparent, and continuously reviewed. In short, to ensure they continue to -be fit for purpose. This policy should be adhered to across the University. The implementation -of the policy will be monitored by the Quality Assurance Committee. -</p> -<p> -2. SCOPE -</p> -<p>This policy applies to all undergraduate and postgraduate taught programmes offered by the -University. This policy should be read in conjunction with other relevant University policies and -procedures. The University website contains policies and procedures on a range of -assessment-related matters, including: ensuring equality, guidance for staff on supporting -students with a disability, guides for staff marking assessments in undergraduate and -postgraduate taught courses, guidance for students and staff on avoiding plagiarism, guidance -for staff in dealing with possible cases of plagiarism, policies on classifying honours degrees -and other degree awards, and on awarding motivational merit and distinction, guidance on the -compensation scheme, the role of external examiners in taught courses, the procedure for -dealing with student discipline cases, guidance for staff on the appeals procedure and -accounting for students’ personal circumstances. -</p> -<p> -</p> -<p> -</p> -<p> - -4 REAP Project, accessed at http://www.reap.ac.uk/ -5 PEER Project, accessed from: http://www.reap.ac.uk/PEER.aspx. The site contains a PEER toolkit -containing guidance for staff on using peer review. -6 Winstone, N. &amp; Nash. R. (2016). The Developing Engagement with Feedback Toolkit (DEFT). The Higher -Education Academy. https://www.heacademy.ac.uk/knowledge-hub/developing-engagement-feedback-toolkit- -deft -7 Carless, D. &amp; Boud, D. (2018): The development of student feedback literacy: enabling uptake of feedback, -Assessment &amp; Evaluation in Higher Education, DOI: 10.1080/02602938.2018.1463354 </p> -<p/> -<div class="annotation"> - <a href="http://www.strath.ac.uk/staff/policies/academic/">http://www.strath.ac.uk/staff/policies/academic/</a> -</div> -<div class="annotation"> - <a href="http://www.reap.ac.uk/">http://www.reap.ac.uk/</a> -</div> -<div class="annotation"> - <a href="http://www.reap.ac.uk/PEER.aspx">http://www.reap.ac.uk/PEER.aspx</a> -</div> -<div class="annotation"> - <a href="https://www.heacademy.ac.uk/knowledge-hub/developing-engagement-feedback-toolkit-deft">https://www.heacademy.ac.uk/knowledge-hub/developing-engagement-feedback-toolkit-deft</a> -</div> -<div class="annotation"> - <a href="https://www.heacademy.ac.uk/knowledge-hub/developing-engagement-feedback-toolkit-deft">https://www.heacademy.ac.uk/knowledge-hub/developing-engagement-feedback-toolkit-deft</a> -</div> -</div> -<div class="page"> - -<p/> -<p> Assessment and Feedback Policy -</p> -<p> 3 -</p> -<p> -</p> -<p>3. ASSESSMENT AND FEEDBACK PRINCIPLES - -</p> -<p>These principles have been widely consulted on across the University, and must be adhered to -throughout the institution. -</p> -<p>PRINCIPLE 1. ASSESSMENT AND FEEDBACK PRACTICES PROMOTE EFFECTIVE -STUDENT LEARNING -</p> -<p>1.1 Assessment and feedback activities are designed to foster student engagement, to -support and measure students’ attainment of knowledge, understanding, and -transferable skills. -</p> -<p>1.2 A range of assessment methods are used, increasing in complexity across a -programme, and taking into consideration student and staff workloads. -</p> -<p>1.3 Assessment and feedback practices align with intended learning outcomes and -assessment criteria, and provide opportunities for students to be active participants in -the process, including opportunities for students to discuss feedback. -</p> -<p>1.4 Timely, constructive, and supportive feedback helps students understand the extent to -which they have fulfilled the assessment criteria, and supports students to use their -feedback in the development of subsequent work. -</p> -<p>1.5 The timing of assessments and return of feedback should be scheduled to enable -students to use feedback to inform their approach to subsequent assessments. The -release of feedback should consider the ability of students to gain further feedback and -support, and avoid release in close proximity to periods where the university is closed -</p> -<p>1.6 Where examinations are used, students should receive at least general feedback soon -after the examination. Students who wish to view their exam scripts following the -provision of general feedback should be facilitated to do so under supervision of staff. -</p> -<p>PRINCIPLE 2. ASSESSMENT AND FEEDBACK PRACTICES ARE APPROPRIATE, FAIR, -AND TRANSPARENT -</p> -<p>2.1 Assessment tasks are appropriate to disciplinary and/or professional contexts. -2.2 Assessment applies rigorous academic standards related to and across, the discipline(s) -</p> -<p>or professional context and is based on clearly defined assessment criteria. -2.3 Assessment grading and feedback is based solely on students’ achievement against -</p> -<p>criteria and standards. -2.4 Assessment and feedback practices are fair, inclusive and accessible to all students. -</p> -<p>PRINCIPLE 3. ASSESSMENT AND FEEDBACK PRACTICES ARE CLEARLY -COMMUNICATED TO STUDENTS AND STAFF -</p> -<p>3.1 Students must be made aware at the beginning of a class of the details of assessments, -including the purpose, weighting, and timing of assessment. -</p> -<p>3.2 Students must be made aware at the beginning of a class of the nature and timing of -feedback. Where unexpected problems cause a delay in the return of feedback within 15 -working days, an explanation will be provided to students, along with a new date for the -return of feedback. -</p> -<p>3.3 All students and staff are made aware at the start of a class of the criteria and standards -used to assess and provide feedback on students’ work. </p> -<p/> -</div> -<div class="page"> - -<p/> -<p> Assessment and Feedback Policy -</p> -<p> 4 -</p> -<p> -</p> -<p>3.4 Students must be made aware of relevant policies and procedures around academic -honesty in assessment, and of procedures associated with personal circumstances, -extensions to the deadlines of coursework, and late submission of coursework. -</p> -<p>3.5 There are opportunities for students and staff to engage in a dialogue around -assessment and feedback, including opportunities for students to clarify feedback. -</p> -<p>3.6 In each programme the processes for marking, moderation, and feedback are -appropriate and fair and are explained to students and staff. -</p> -<p>PRINCIPLE 4. ASSESSMENT AND FEEDBACK PRACTICES ARE CONTINUOUSLY -REVIEWED -</p> -<p>4.1 The timing of assessment activities at cohort level, including the timing of examinations, -are monitored and reviewed, in order to minimise bunching. -</p> -<p>4.2 Assessment and feedback activities, and the outcomes of assessment, are reviewed via -the class and course approval and/or review processes, and external examining -processes. -</p> -<p>4.3 Assessment and feedback activities are continuously reviewed to ensure effective -alignment with a programme’s intended learning outcomes and graduate attributes. -</p> -<p>4.4 Opportunities to develop effective practice and innovation in assessment and feedback -are available to all staff involved in assessment. -</p> -<p> -4. ANONYMOUS MARKING -</p> -<p>4.1 Anonymous marking (when the identity of a student is not known to a marker and/or exam -board) will be used for all assessments wherever possible and appropriate, including formal -written examinations at both undergraduate and postgraduate levels. -</p> -<p>4.2 Where anonymous marking is not possible, for example where an alternative method of -assessment has been used for a student with a disability, additional scrutiny by a second -marker may be appropriate. -</p> -<p>4.3 Other exceptions will be where the mode of assessment (e.g., presentations or oral -examinations) prevents anonymity, or where there is pedagogical justification (e.g., the use -of ipsative feedback). -</p> -<p>4.4 Double marking is effective practice for assessments that are not marked anonymously and -for dissertations, or comparable assessments. -</p> -<p> -5. RESPONSIBILITIES FOR IMPLEMENTATION -</p> -<p>5.1 The University assumes responsibility for: -- Providing staff with opportunities to develop effective practice in assessment and -</p> -<p>feedback through the Organisational Staff and Development Unit (Strathclyde Teaching -Excellence Programme) Education Enhancement (the University’s VLE, Myplace offers -various tools to support effective practice in assessment and feedback, including -handling peer review/assessment activities, online assessment submission and return of -feedback, and enabling efficient communication with different student and staff groups; -the Sharing Practice in Enhancing Learning and Teaching (SPELT) platform, and </p> -<p/> -<div class="annotation"> - <a href="https://www.strath.ac.uk/hr/learninganddevelopment/step/">https://www.strath.ac.uk/hr/learninganddevelopment/step/</a> -</div> -<div class="annotation"> - <a href="https://www.strath.ac.uk/hr/learninganddevelopment/step/">https://www.strath.ac.uk/hr/learninganddevelopment/step/</a> -</div> -<div class="annotation"> - <a href="http://spelt.strath.ac.uk/pages/viewpage.action;jsessionid=01EB624BAAB6873613854F3DC2EE7433?pageId=8979188">http://spelt.strath.ac.uk/pages/viewpage.action;jsessionid=01EB624BAAB6873613854F3DC2EE7433?pageId=8979188</a> -</div> -</div> -<div class="page"> - -<p/> -<p> Assessment and Feedback Policy -</p> -<p> 5 -</p> -<p> -</p> -<p>TESTA), the Disability and Wellbeing Service (for ensuring assessments are accessible -to all students), and the Equality and Diversity Office; -</p> -<p>- Providing adequate resources, including information technology systems, to support -effective practice (Information Services Directorate, Student Business, Education -Enhancement); and -</p> -<p>- Monitoring implementation of this policy through the Quality Assurance Committee -(Education Enhancement). -</p> -<p>5.2 Faculties are responsible for: overseeing and receiving reports from programme exam -boards; scrutinising external examiners’ reports and responding to them; ensuring -consistency in the implementation of this and other related policies across programmes, -and sharing effective practice within and between faculties. -</p> -<p>5.3 Heads of Department/School are responsible for: instructing colleagues within their -Department/School to note and adhere to the Assessment and Feedback Policy and -Procedures for Recording and Publication of Marks, Procedures on Preparing and -Conducting Exams, and other related policies such as the Policy on the Late Submission of -Coursework, Policy on Extensions to Coursework Submission, the Policy on Moderation -and Double Marking, and Guidance on Marking Assessments in Undergraduate and -Postgraduate Taught courses; and for ensuring that marking of assessments is sufficiently -resourced to enable feedback to be returned within 15 working days of assessment -submission. -</p> -<p>5.4 Programme Exam Boards are responsible for: making sure assessments across a -programme are marked fairly; ensuring university and faculty regulations are adhered to; -and responding to points made by External Examiners. -</p> -<p>5.5 Programme Leaders/Directors are responsible for ensuring a coherent and effective -approach to assessment and feedback is taken across a programme of study. This -approach will involve: -</p> -<p>- Ensuring a range of assessment methods are used throughout a programme of study, -as appropriate to the discipline; -</p> -<p>- Monitoring the intensity and equity of assessments across and between programmes, -with due consideration to joint honours degrees; -</p> -<p>- Designing and reviewing the Assessment Schedule and Exam Timetable within a -programme and requesting amendments where there is assessment/exam bunching; -</p> -<p>- Developing effective moderation and/or double marking procedures, in line with the -Policy on Moderation and Double Marking; -</p> -<p>- Ensuring procedures are in place for monitoring students’ use of the Policies and -Procedures on Extensions to Coursework Submission and Late Submission of -Coursework, as a mechanism for identifying students who may require support; and -</p> -<p>- Ensuring procedures related to assuring fairness in assessment, such as moderation -and the role of External Examiners, are communicated to students. -</p> -<p>5.6 Year Coordinators and/or Module Coordinators are responsible for: -</p> -<p>- Having an overview of summative assessment activities within a programme of study to -avoid, where possible, a concentration of assessment deadlines, including examinations; </p> -<p/> -</div> -<div class="page"> - -<p/> -<p> Assessment and Feedback Policy -</p> -<p> 6 -</p> -<p> -</p> -<p>- Making sure the details of all assessments are provided at the start of a class; -- Ensuring assessment criteria that is aligned to intended learning outcomes and the -</p> -<p>University’s Guidance on Marking Assessments in Undergraduate and Postgraduate -Taught programmes, is transparent and made available to students at the start of a -class; -</p> -<p>- Providing opportunities for students to use feedback prior to the submission of any -subsequent related assessments; -</p> -<p>- Working with students to develop effective practice in assessment and feedback, -including: providing students with opportunities to develop their understanding of the -criteria and standards used to assess their work and supporting students to engage with -and use feedback; -</p> -<p>- Obtaining feedback from students in relation to assessment and feedback for module(s) -for which they are responsible, and for communicating any actions on this feedback to -students; and -</p> -<p>- For undertaking effective moderation and/or double marking procedures. -</p> -<p>5.7 Staff involved in assessing students’ work are responsible for: -</p> -<p>- Designing assessments that effectively facilitate and measure students’ achievement of -intended learning outcomes; -</p> -<p>- Assessing students’ work according to published assessment criteria which are aligned -to intended learning outcomes and the University’s Guidance on Marking Assessments -in Undergraduate and Postgraduate Taught programmes; -</p> -<p>- Providing timely, informative and helpful feedback which enables students to further -improve their learning and performance wherever possible; -</p> -<p>- Informing students when, where and how feedback will be provided; -- Engaging in dialogue with students about assessment and feedback; and -- Continuously reviewing their approaches to assessment and feedback to reflect effective -</p> -<p>practice. -</p> -<p>5.8 Students are expected to be responsible for their own learning through: -</p> -<p>- Understanding the requirements of individual assessments, and actively engaging with -assessment tasks by devoting appropriate time and effort; -</p> -<p>- Developing an understanding of the relationship between intended learning outcomes -and assessment criteria, and standards in their programme of study; -</p> -<p>- Ensuring their academic work is authentic and honestly produced; -- Finding out where, how and when work is submitted and how and when feedback is -</p> -<p>provided; -- Actively engaging, reflecting, and using provided feedback; -- Understanding the academic policies and procedures related to assessment and -</p> -<p>feedback, including Personal Circumstances Procedure, Policy and Procedures for the -Late Submission of Coursework, Policy on Moderation and Double Marking, Policy on -Extensions to the Submission of Coursework, and Compensation and Motivational Merit; -</p> -<p>- Seeking academic support when needed, for example, if feedback needs to be clarified; -and -</p> -<p>- Participating in the development of assessment and feedback practices at class and -programme levels. </p> -<p/> -</div> -<div class="page"> - -<p/> -<p> Assessment and Feedback Policy -</p> -<p> 7 -</p> -<p> -</p> -<p>6. DEFINITIONS OF TERMS - -Assessment The process of measuring the performance of students -</p> -<p>(as in examinations, assignments and other assessable -work) that enables students to monitor their progress -and contributes to their academic results. - -</p> -<p>Assessment criteria - -</p> -<p> -</p> -<p>Specific criteria against which pieces of work are -assessed. An understanding of the criteria must be -shared by markers and students. Feedback should -relate to these criteria. -</p> -<p>Assessment bunching The perception of assessments within a programme of -study being scheduled too closely together. - -</p> -<p>Assessment schedule - -</p> -<p>A timeline of all cohort level assessment. -</p> -<p>Exam bunching Where the scheduling of two exams falls within a 23 -hour period. - -</p> -<p>Feedback Information provided to students on the quality of their -performance in relation to assessment criteria, which -forms the basis of improved student learning. - -</p> -<p>Formative assessment - -</p> -<p>This type of assessment normally has no or low -weighting in the final mark for a module or programme. -The goal of formative assessment is to provide an -opportunity for students to monitor their learning and -provide feedback to teachers that can be used to -review their teaching. - -</p> -<p>Graduate attributes Qualities, skills, dispositions, and understanding that -students are expected to develop. At Strathclyde these -are referred to as the 4 E’s – engaged, enquiring, -enterprising and ethically and globally aware. - -</p> -<p>Intended learning outcomes What the student is expected to be able to do or -demonstrate, in terms of particular knowledge, skills -and understanding, by the end of a module or -programme. - -</p> -<p>Ipsative feedback - -</p> -<p>This type of feedback compares a student’s -performance on a current assessment with -performance on a previous assessment. - -</p> -<p>Moderation The process of checking that assessment criteria are -consistently applied across markers in marking -students’ work. - -</p> -<p>Module The individual components of a programme, normally -worth 20 or 10 credits. - -</p> -<p>Module evaluation The process of obtaining feedback from students on all -aspects of teaching, learning, and assessment within a </p> -<p/> -</div> -<div class="page"> -<p/> -<p> Assessment and Feedback Policy -</p> -<p> 8 -</p> -<p> -</p> -<p>module. - -</p> -<p>Programme The full degree programme leading to an award. - -</p> -<p>Seen double marking Where an assessment is independently marked by two -markers, but where the second marker has access to -the marks or comments of the first marker. - -</p> -<p>Summative assessment Assessment is summative when the grading of an -assessment contributes to the final grade for a class or -course. The aim of summative assessment is to -evaluate students’ attainment of the intended learning -outcomes within a unit of study. - -</p> -<p> -Unseen double marking - -</p> -<p>Where an assessment is independently marked by two -markers who do not have access to the grades or -comments of the other marker. - -</p> -<p>Working days The University’s standard working week is normally -Monday to Friday, covering five working days. Fifteen -working days would normally equate to 21 calendar -days, but may be longer where the period includes -University closure days. -</p> -<p> - </p> -<p/> -</div> -</body> -</html> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Assessment and Feedback Policy - - -
- -

-

-

-

-

-

-

-

ASSESSMENT AND -FEEDBACK POLICY -

-

-

-

Version No. Description Author Approval Effective Date -

-

1.2 Assessment and -

-

Feedback Policy – -

-

applicable to both -

-

undergraduate and -

-

postgraduate taught -

-

courses -

-

Assessment -

-

and -

-

Feedback -

-

Working -

-

Group -

-

Senate From academic -

-

year 2019-20 -

-

Version 1.2 -

-

- -the place of useful learning -The University of Strathclyde is a charitable body, registered in -Scotland, number SC015263 -

-

-

-

- -

-

-

-
- -

-

Assessment and Feedback Policy -

-

1 -

-

-

-

1. RATIONALE -

-

In higher education, “assessment” describes any process that involves the evaluation or -appraisal of a student’s knowledge, understanding, skills, attitudes or abilities. In line with the -QAA UK Quality Code for Higher Education, assessment is taken to be an integral component -of teaching and learning, and serves multiple purposes. In addition to enabling evaluation and -measurement of students’ learning, effective assessment shapes and enhances student -learning. Assessments should therefore be designed to facilitate students’ attainment of -intended learning outcomes, and permit the measurement of such attainments against explicit -criteria. A holistic approach to assessment at course level can also support the development of -distinctive Strathclyde graduate attributes: graduates that are engaged, enquiring, enterprising, -and ethically and globally aware. Recent publications on assessment and feedback in higher -education suggest a ‘transformation’ is required to ensure that assessment and feedback -practices enable students to develop such attributes and the skills needed to be lifelong -learners in the 21st century.1 -

-

This approach recognises that assessment is central to learning and teaching, and is not -designed solely to measure student learning. Also essential to enhancing learning, is the -provision of continuous feedback to students on their learning; it is recognised that feedback -takes different forms (e.g. replies to posts on a discussion forum), but in relation to assessment, -useful feedback is feedback that is specific in informing learners the extent to which they have -met published assessment criteria, and explains to them what they need to do to improve. -Ensuring feedback is clear, specific, and adopts a supportive tone is most likely to foster -student engagement with feedback. The role of feedback in effectively supporting student -learning should be recognised at the assessment design stage, where factors such as the -timing of the release of feedback and the scheduling of subsequent assessments are -considered (particularly where feedback can be used by students to improve the development -of future work), as well as the provision of opportunities for students to clarify feedback. -

-

The University expects that feedback will normally be returned to students within 15 working -days of assessment submission2. The University’s VLE, Myplace, provides a platform for the -electronic return of grades and feedback, and where permitted by the nature of an assessment, -staff are expected to implement the submission of assessments online through Myplace, in -order to promote effectiveness and efficiency in each stage of the assessment and feedback -process. In addition, Myplace should be used to manage assessments that are submitted offline -in order to implement the Policy and Procedure on Extensions to Coursework Submission and -Procedures for the Recording and Publication of Marks. Staff should also be cognisant of -specific issues relevant to equality, diversity and inclusion in designing assessments and -providing feedback, which may affect particular groups of students.3 -

-

-1 For example: Boud, D. & Falachikov, N. (Eds.). (2007). Rethinking Assessment in Higher Education. Oxon: -Routledge; Higher Education Academy. (2012). A Marked Improvement. Transforming assessment in higher -education. York: The Higher Education Academy; Merry, S., Price, M., Carless, D. & Taras, M. (Eds.). (2013). -Reconceptualising Feedback in Higher Education. Developing Dialogue with Students. Oxon: Routledge; -Carless, D. (2015). Excellence in university assessment: learning from award-winning practice. London: -Routledge. -2 Exceptions to this are dissertations or assessments with credit weightings of 40 credits or higher, and where -double marking is required. (See the Policy on Moderation and Double Marking). -3 Staff are advised to consult the University’s equality impact assessment information where relevant.

-

-

- -
-
- -

-

Assessment and Feedback Policy -

-

2 -

-

-

-

Previous work on assessment at Strathclyde (through the Re-Engineering Assessment -Practices4 and Peer Evaluation in Education Review5 projects, together with the work on Peer -and Community Personal Development Planning), have emphasised the importance of -providing students with opportunities to self- and peer-assess, and for students to develop the -capacity to evaluate, reflect on6 and manage their own learning through the effective use of -feedback. Published research also highlights the importance of developing students’ -understanding of the criteria and standards by which their work is assessed, and suggests that -providing students with the opportunity to analyse and discuss the qualities of carefully selected -exemplars is an effective learning activity to achieve this7. It is widely recognised that these will, -in the long-term, help develop attitudes and skills that foster engagement and independence in -learning. - -The aim of the current policy is to develop an institution-wide approach to assessment and -feedback that enhances the effectiveness of assessment in aiding students to achieve the -necessary knowledge and skills described in a class or programme’s intended learning -outcomes. The policy is underpinned by principles of assessment and feedback (outlined in -section 3) that reflect this aim and a commitment to ensuring our assessment and feedback -practices are fair, transparent, and continuously reviewed. In short, to ensure they continue to -be fit for purpose. This policy should be adhered to across the University. The implementation -of the policy will be monitored by the Quality Assurance Committee. -

-

-2. SCOPE -

-

This policy applies to all undergraduate and postgraduate taught programmes offered by the -University. This policy should be read in conjunction with other relevant University policies and -procedures. The University website contains policies and procedures on a range of -assessment-related matters, including: ensuring equality, guidance for staff on supporting -students with a disability, guides for staff marking assessments in undergraduate and -postgraduate taught courses, guidance for students and staff on avoiding plagiarism, guidance -for staff in dealing with possible cases of plagiarism, policies on classifying honours degrees -and other degree awards, and on awarding motivational merit and distinction, guidance on the -compensation scheme, the role of external examiners in taught courses, the procedure for -dealing with student discipline cases, guidance for staff on the appeals procedure and -accounting for students’ personal circumstances. -

-

-

-

-

-

- -4 REAP Project, accessed at http://www.reap.ac.uk/ -5 PEER Project, accessed from: http://www.reap.ac.uk/PEER.aspx. The site contains a PEER toolkit -containing guidance for staff on using peer review. -6 Winstone, N. & Nash. R. (2016). The Developing Engagement with Feedback Toolkit (DEFT). The Higher -Education Academy. https://www.heacademy.ac.uk/knowledge-hub/developing-engagement-feedback-toolkit- -deft -7 Carless, D. & Boud, D. (2018): The development of student feedback literacy: enabling uptake of feedback, -Assessment & Evaluation in Higher Education, DOI: 10.1080/02602938.2018.1463354

-

-

- - - - -
-
- -

-

Assessment and Feedback Policy -

-

3 -

-

-

-

3. ASSESSMENT AND FEEDBACK PRINCIPLES - -

-

These principles have been widely consulted on across the University, and must be adhered to -throughout the institution. -

-

PRINCIPLE 1. ASSESSMENT AND FEEDBACK PRACTICES PROMOTE EFFECTIVE -STUDENT LEARNING -

-

1.1 Assessment and feedback activities are designed to foster student engagement, to -support and measure students’ attainment of knowledge, understanding, and -transferable skills. -

-

1.2 A range of assessment methods are used, increasing in complexity across a -programme, and taking into consideration student and staff workloads. -

-

1.3 Assessment and feedback practices align with intended learning outcomes and -assessment criteria, and provide opportunities for students to be active participants in -the process, including opportunities for students to discuss feedback. -

-

1.4 Timely, constructive, and supportive feedback helps students understand the extent to -which they have fulfilled the assessment criteria, and supports students to use their -feedback in the development of subsequent work. -

-

1.5 The timing of assessments and return of feedback should be scheduled to enable -students to use feedback to inform their approach to subsequent assessments. The -release of feedback should consider the ability of students to gain further feedback and -support, and avoid release in close proximity to periods where the university is closed -

-

1.6 Where examinations are used, students should receive at least general feedback soon -after the examination. Students who wish to view their exam scripts following the -provision of general feedback should be facilitated to do so under supervision of staff. -

-

PRINCIPLE 2. ASSESSMENT AND FEEDBACK PRACTICES ARE APPROPRIATE, FAIR, -AND TRANSPARENT -

-

2.1 Assessment tasks are appropriate to disciplinary and/or professional contexts. -2.2 Assessment applies rigorous academic standards related to and across, the discipline(s) -

-

or professional context and is based on clearly defined assessment criteria. -2.3 Assessment grading and feedback is based solely on students’ achievement against -

-

criteria and standards. -2.4 Assessment and feedback practices are fair, inclusive and accessible to all students. -

-

PRINCIPLE 3. ASSESSMENT AND FEEDBACK PRACTICES ARE CLEARLY -COMMUNICATED TO STUDENTS AND STAFF -

-

3.1 Students must be made aware at the beginning of a class of the details of assessments, -including the purpose, weighting, and timing of assessment. -

-

3.2 Students must be made aware at the beginning of a class of the nature and timing of -feedback. Where unexpected problems cause a delay in the return of feedback within 15 -working days, an explanation will be provided to students, along with a new date for the -return of feedback. -

-

3.3 All students and staff are made aware at the start of a class of the criteria and standards -used to assess and provide feedback on students’ work.

-

-

-
- -

-

Assessment and Feedback Policy -

-

4 -

-

-

-

3.4 Students must be made aware of relevant policies and procedures around academic -honesty in assessment, and of procedures associated with personal circumstances, -extensions to the deadlines of coursework, and late submission of coursework. -

-

3.5 There are opportunities for students and staff to engage in a dialogue around -assessment and feedback, including opportunities for students to clarify feedback. -

-

3.6 In each programme the processes for marking, moderation, and feedback are -appropriate and fair and are explained to students and staff. -

-

PRINCIPLE 4. ASSESSMENT AND FEEDBACK PRACTICES ARE CONTINUOUSLY -REVIEWED -

-

4.1 The timing of assessment activities at cohort level, including the timing of examinations, -are monitored and reviewed, in order to minimise bunching. -

-

4.2 Assessment and feedback activities, and the outcomes of assessment, are reviewed via -the class and course approval and/or review processes, and external examining -processes. -

-

4.3 Assessment and feedback activities are continuously reviewed to ensure effective -alignment with a programme’s intended learning outcomes and graduate attributes. -

-

4.4 Opportunities to develop effective practice and innovation in assessment and feedback -are available to all staff involved in assessment. -

-

-4. ANONYMOUS MARKING -

-

4.1 Anonymous marking (when the identity of a student is not known to a marker and/or exam -board) will be used for all assessments wherever possible and appropriate, including formal -written examinations at both undergraduate and postgraduate levels. -

-

4.2 Where anonymous marking is not possible, for example where an alternative method of -assessment has been used for a student with a disability, additional scrutiny by a second -marker may be appropriate. -

-

4.3 Other exceptions will be where the mode of assessment (e.g., presentations or oral -examinations) prevents anonymity, or where there is pedagogical justification (e.g., the use -of ipsative feedback). -

-

4.4 Double marking is effective practice for assessments that are not marked anonymously and -for dissertations, or comparable assessments. -

-

-5. RESPONSIBILITIES FOR IMPLEMENTATION -

-

5.1 The University assumes responsibility for: -- Providing staff with opportunities to develop effective practice in assessment and -

-

feedback through the Organisational Staff and Development Unit (Strathclyde Teaching -Excellence Programme) Education Enhancement (the University’s VLE, Myplace offers -various tools to support effective practice in assessment and feedback, including -handling peer review/assessment activities, online assessment submission and return of -feedback, and enabling efficient communication with different student and staff groups; -the Sharing Practice in Enhancing Learning and Teaching (SPELT) platform, and

-

-

- - -
-
- -

-

Assessment and Feedback Policy -

-

5 -

-

-

-

TESTA), the Disability and Wellbeing Service (for ensuring assessments are accessible -to all students), and the Equality and Diversity Office; -

-

- Providing adequate resources, including information technology systems, to support -effective practice (Information Services Directorate, Student Business, Education -Enhancement); and -

-

- Monitoring implementation of this policy through the Quality Assurance Committee -(Education Enhancement). -

-

5.2 Faculties are responsible for: overseeing and receiving reports from programme exam -boards; scrutinising external examiners’ reports and responding to them; ensuring -consistency in the implementation of this and other related policies across programmes, -and sharing effective practice within and between faculties. -

-

5.3 Heads of Department/School are responsible for: instructing colleagues within their -Department/School to note and adhere to the Assessment and Feedback Policy and -Procedures for Recording and Publication of Marks, Procedures on Preparing and -Conducting Exams, and other related policies such as the Policy on the Late Submission of -Coursework, Policy on Extensions to Coursework Submission, the Policy on Moderation -and Double Marking, and Guidance on Marking Assessments in Undergraduate and -Postgraduate Taught courses; and for ensuring that marking of assessments is sufficiently -resourced to enable feedback to be returned within 15 working days of assessment -submission. -

-

5.4 Programme Exam Boards are responsible for: making sure assessments across a -programme are marked fairly; ensuring university and faculty regulations are adhered to; -and responding to points made by External Examiners. -

-

5.5 Programme Leaders/Directors are responsible for ensuring a coherent and effective -approach to assessment and feedback is taken across a programme of study. This -approach will involve: -

-

- Ensuring a range of assessment methods are used throughout a programme of study, -as appropriate to the discipline; -

-

- Monitoring the intensity and equity of assessments across and between programmes, -with due consideration to joint honours degrees; -

-

- Designing and reviewing the Assessment Schedule and Exam Timetable within a -programme and requesting amendments where there is assessment/exam bunching; -

-

- Developing effective moderation and/or double marking procedures, in line with the -Policy on Moderation and Double Marking; -

-

- Ensuring procedures are in place for monitoring students’ use of the Policies and -Procedures on Extensions to Coursework Submission and Late Submission of -Coursework, as a mechanism for identifying students who may require support; and -

-

- Ensuring procedures related to assuring fairness in assessment, such as moderation -and the role of External Examiners, are communicated to students. -

-

5.6 Year Coordinators and/or Module Coordinators are responsible for: -

-

- Having an overview of summative assessment activities within a programme of study to -avoid, where possible, a concentration of assessment deadlines, including examinations;

-

-

-
- -

-

Assessment and Feedback Policy -

-

6 -

-

-

-

- Making sure the details of all assessments are provided at the start of a class; -- Ensuring assessment criteria that is aligned to intended learning outcomes and the -

-

University’s Guidance on Marking Assessments in Undergraduate and Postgraduate -Taught programmes, is transparent and made available to students at the start of a -class; -

-

- Providing opportunities for students to use feedback prior to the submission of any -subsequent related assessments; -

-

- Working with students to develop effective practice in assessment and feedback, -including: providing students with opportunities to develop their understanding of the -criteria and standards used to assess their work and supporting students to engage with -and use feedback; -

-

- Obtaining feedback from students in relation to assessment and feedback for module(s) -for which they are responsible, and for communicating any actions on this feedback to -students; and -

-

- For undertaking effective moderation and/or double marking procedures. -

-

5.7 Staff involved in assessing students’ work are responsible for: -

-

- Designing assessments that effectively facilitate and measure students’ achievement of -intended learning outcomes; -

-

- Assessing students’ work according to published assessment criteria which are aligned -to intended learning outcomes and the University’s Guidance on Marking Assessments -in Undergraduate and Postgraduate Taught programmes; -

-

- Providing timely, informative and helpful feedback which enables students to further -improve their learning and performance wherever possible; -

-

- Informing students when, where and how feedback will be provided; -- Engaging in dialogue with students about assessment and feedback; and -- Continuously reviewing their approaches to assessment and feedback to reflect effective -

-

practice. -

-

5.8 Students are expected to be responsible for their own learning through: -

-

- Understanding the requirements of individual assessments, and actively engaging with -assessment tasks by devoting appropriate time and effort; -

-

- Developing an understanding of the relationship between intended learning outcomes -and assessment criteria, and standards in their programme of study; -

-

- Ensuring their academic work is authentic and honestly produced; -- Finding out where, how and when work is submitted and how and when feedback is -

-

provided; -- Actively engaging, reflecting, and using provided feedback; -- Understanding the academic policies and procedures related to assessment and -

-

feedback, including Personal Circumstances Procedure, Policy and Procedures for the -Late Submission of Coursework, Policy on Moderation and Double Marking, Policy on -Extensions to the Submission of Coursework, and Compensation and Motivational Merit; -

-

- Seeking academic support when needed, for example, if feedback needs to be clarified; -and -

-

- Participating in the development of assessment and feedback practices at class and -programme levels.

-

-

-
- -

-

Assessment and Feedback Policy -

-

7 -

-

-

-

6. DEFINITIONS OF TERMS - -Assessment The process of measuring the performance of students -

-

(as in examinations, assignments and other assessable -work) that enables students to monitor their progress -and contributes to their academic results. - -

-

Assessment criteria - -

-

-

-

Specific criteria against which pieces of work are -assessed. An understanding of the criteria must be -shared by markers and students. Feedback should -relate to these criteria. -

-

Assessment bunching The perception of assessments within a programme of -study being scheduled too closely together. - -

-

Assessment schedule - -

-

A timeline of all cohort level assessment. -

-

Exam bunching Where the scheduling of two exams falls within a 23 -hour period. - -

-

Feedback Information provided to students on the quality of their -performance in relation to assessment criteria, which -forms the basis of improved student learning. - -

-

Formative assessment - -

-

This type of assessment normally has no or low -weighting in the final mark for a module or programme. -The goal of formative assessment is to provide an -opportunity for students to monitor their learning and -provide feedback to teachers that can be used to -review their teaching. - -

-

Graduate attributes Qualities, skills, dispositions, and understanding that -students are expected to develop. At Strathclyde these -are referred to as the 4 E’s – engaged, enquiring, -enterprising and ethically and globally aware. - -

-

Intended learning outcomes What the student is expected to be able to do or -demonstrate, in terms of particular knowledge, skills -and understanding, by the end of a module or -programme. - -

-

Ipsative feedback - -

-

This type of feedback compares a student’s -performance on a current assessment with -performance on a previous assessment. - -

-

Moderation The process of checking that assessment criteria are -consistently applied across markers in marking -students’ work. - -

-

Module The individual components of a programme, normally -worth 20 or 10 credits. - -

-

Module evaluation The process of obtaining feedback from students on all -aspects of teaching, learning, and assessment within a

-

-

-
-

-

Assessment and Feedback Policy -

-

8 -

-

-

-

module. - -

-

Programme The full degree programme leading to an award. - -

-

Seen double marking Where an assessment is independently marked by two -markers, but where the second marker has access to -the marks or comments of the first marker. - -

-

Summative assessment Assessment is summative when the grading of an -assessment contributes to the final grade for a class or -course. The aim of summative assessment is to -evaluate students’ attainment of the intended learning -outcomes within a unit of study. - -

-

-Unseen double marking - -

-

Where an assessment is independently marked by two -markers who do not have access to the grades or -comments of the other marker. - -

-

Working days The University’s standard working week is normally -Monday to Friday, covering five working days. Fifteen -working days would normally equate to 21 calendar -days, but may be longer where the period includes -University closure days. -

-

-

-

-

- - -array(14) { - ["areaid"]=> - string(19) "mod_folder-activity" - ["id"]=> - string(33) "mod_folder-activity-1-solrfile287" - ["itemid"]=> - int(1) - ["title"]=> - string(34) "Assessment_and_Feedback_Policy.pdf" - ["contextid"]=> - int(268) - ["courseid"]=> - int(4) - ["owneruserid"]=> - int(0) - ["modified"]=> - string(20) "2024-03-04T17:17:08Z" - ["type"]=> - int(2) - ["solr_filegroupingid"]=> - string(21) "mod_folder-activity-1" - ["solr_fileid"]=> - string(3) "287" - ["solr_filecontenthash"]=> - string(40) "6de274633203c03b996b248e841f2a1f17cdb537" - ["solr_fileindexstatus"]=> - int(1) - ["solr_vector"]=> - array(5) { - [0]=> - float(0.0053587136790156364) - [1]=> - float(-0.00049990462139248848) - [2]=> - float(0.038883671164512634) - [3]=> - float(-0.0030010775662958622) - [4]=> - float(-0.0090081822127103806) - } -} diff --git a/search/cli/result2.txt b/search/cli/result2.txt deleted file mode 100644 index 21e64b0f5bf0..000000000000 --- a/search/cli/result2.txt +++ /dev/null @@ -1,64 +0,0 @@ -Running full reindex of site -============================ -Processing area: Text block content - No new documents to index. -Processing area: Courses - Processed 5 records containing 5 documents (1 batch), in 0.2 seconds. -Processing area: Course custom fields - No new documents to index. -Processing area: Course sections - No new documents to index. -Processing area: Messages - received - No new documents to index. -Processing area: Messages - sent - No new documents to index. -Processing area: Course Teacher - No new documents to index. -Processing area: Users - Processed 103 records containing 103 documents (2 batches), in 1 seconds. -Processing area: Assignment - activity information - Processed 22 records containing 22 documents (1 batch), in 0.3 seconds. -Processing area: Attendance - activity information - Processed 1 records containing 1 documents, in 0.1 seconds. -Processing area: BigBlueButton - activity information - No new documents to index. -Processing area: BigBlueButton - tags information - No new documents to index. -Processing area: Book - resource information - No new documents to index. -Processing area: Book - chapters - No new documents to index. -Processing area: Chat - activity information - No new documents to index. -Processing area: Choice - activity information - No new documents to index. -Processing area: Database - activity information - No new documents to index. -Processing area: Database - entries - No new documents to index. -Processing area: Feedback - activity information - No new documents to index. -Processing area: Folder -++ http://172.22.0.6:8983/solr/moodle/update/extract?wt=xml&uprefix=ignored_&captureAttr=true&fmap.content=solr_filecontent&fmap.media_white_point=ignored_mwp&fmap.media_black_point=ignored_mbp&fmap.areaid=ignored_areaid&literal.mdltmp_areaid=mod_folder-activity&fmap.mdltmp_areaid=areaid&fmap.id=ignored_id&literal.mdltmp_id=mod_folder-activity-1-solrfile287&fmap.mdltmp_id=id&fmap.itemid=ignored_itemid&literal.mdltmp_itemid=1&fmap.mdltmp_itemid=itemid&fmap.title=ignored_title&literal.mdltmp_title=Assessment_and_Feedback_Policy.pdf&fmap.mdltmp_title=title&fmap.contextid=ignored_contextid&literal.mdltmp_contextid=268&fmap.mdltmp_contextid=contextid&fmap.courseid=ignored_courseid&literal.mdltmp_courseid=4&fmap.mdltmp_courseid=courseid&fmap.owneruserid=ignored_owneruserid&literal.mdltmp_owneruserid=0&fmap.mdltmp_owneruserid=owneruserid&fmap.modified=ignored_modified&literal.mdltmp_modified=2024-03-04T17%3A17%3A08Z&fmap.mdltmp_modified=modified&fmap.type=ignored_type&literal.mdltmp_type=2&fmap.mdltmp_type=type&fmap.solr_filegroupingid=ignored_solr_filegroupingid&literal.mdltmp_solr_filegroupingid=mod_folder-activity-1&fmap.mdltmp_solr_filegroupingid=solr_filegroupingid&fmap.solr_fileid=ignored_solr_fileid&literal.mdltmp_solr_fileid=287&fmap.mdltmp_solr_fileid=solr_fileid&fmap.solr_filecontenthash=ignored_solr_filecontenthash&literal.mdltmp_solr_filecontenthash=6de274633203c03b996b248e841f2a1f17cdb537&fmap.mdltmp_solr_filecontenthash=solr_filecontenthash&fmap.solr_fileindexstatus=ignored_solr_fileindexstatus&literal.mdltmp_solr_fileindexstatus=1&fmap.mdltmp_solr_fileindexstatus=solr_fileindexstatus&fmap.solr_vector=ignored_solr_vector&literal.mdltmp_solr_vector&fmap.mdltmp_solr_vector=solr_vector&resource.name=Assessment_and_Feedback_Policy.pdf&extractOnly=true ++ -* line 139 of /search/engine/solrrag/classes/engine.php: call to debugging() -* line 970 of /search/engine/solr/classes/engine.php: call to search_solrrag\engine->add_stored_file() -* line 776 of /search/engine/solr/classes/engine.php: call to search_solr\engine->process_document_files() -* line 318 of /search/classes/engine.php: call to search_solr\engine->add_document_batch() -* line 1283 of /search/classes/manager.php: call to core_search\engine->add_documents() -* line 108 of /search/cli/indexer.php: call to core_search\manager->index() -++ Got SOLR update/extract response ++ -* line 181 of /search/engine/solrrag/classes/engine.php: call to debugging() -* line 970 of /search/engine/solr/classes/engine.php: call to search_solrrag\engine->add_stored_file() -* line 776 of /search/engine/solr/classes/engine.php: call to search_solr\engine->process_document_files() -* line 318 of /search/classes/engine.php: call to search_solr\engine->add_document_batch() -* line 1283 of /search/classes/manager.php: call to core_search\engine->add_documents() -* line 108 of /search/cli/indexer.php: call to core_search\manager->index() -++ Using vector field solr_vector_1356 ++ -* line 205 of /search/engine/solrrag/classes/engine.php: call to debugging() -* line 970 of /search/engine/solr/classes/engine.php: call to search_solrrag\engine->add_stored_file() -* line 776 of /search/engine/solr/classes/engine.php: call to search_solr\engine->process_document_files() -* line 318 of /search/classes/engine.php: call to search_solr\engine->add_document_batch() -* line 1283 of /search/classes/manager.php: call to core_search\engine->add_documents() -* line 108 of /search/cli/indexer.php: call to core_search\manager->index() -bool(false) -Goodbye \ No newline at end of file diff --git a/search/engine/solrrag/classes/engine.php b/search/engine/solrrag/classes/engine.php index a5d562b87b1c..7eca210b1139 100644 --- a/search/engine/solrrag/classes/engine.php +++ b/search/engine/solrrag/classes/engine.php @@ -282,4 +282,102 @@ protected function create_solr_document(array $doc): \SolrInputDocument { return $solrdoc; } + + /** + * @param $filters \stdClass + * @param $accessinfo + * @param $limit + * @return void + * @throws \core_search\engine_exception + */ + public function execute_query($filters, $accessinfo, $limit = 0) { + var_dump($filters->similarity); + if (isset($filters->similarity) && + $filters->similarity + ) { + // Do a vector similarity search. + debugging("Running similarity search", DEBUG_DEVELOPER); + $this->execute_solr_knn_query($filters, $accessinfo, $limit); + } else { + debugging("Running regular search", DEBUG_DEVELOPER); + return parent::execute_query($filters, $accessinfo, $limit); + } + } + + protected function execute_solr_knn_query($filters, $accessinfo, $limit) { + $vector = $filters->vector; + $topK = 3; // Nearest neighbours to retrieve. + $field = "solr_vector_" . count($vector); + $requestbody = "{!knn f={$field} topK={$topK}}[" . implode(",", $vector) . "]"; + $filters->mainquery = $requestbody; + if (empty($limit)) { + $limit = \core_search\manager::MAX_RESULTS; + } + + $curl = $this->get_curl_object(); + $requesturl = $this->get_connection_url('/select'); + $requesturl->param('fl', 'id,areaid,score'); + $requesturl->param('wt', 'xml'); + + $body = [ + 'query' => $requestbody + ]; + echo $requesturl->out(false); + $result = $curl->post($requesturl->out(false), + json_encode($body) + ); + // Probably have to duplicate error handling code from the add_stored_file() function. + $code = $curl->get_errno(); + $info = $curl->get_info(); + + // Now error handling. It is just informational, since we aren't tracking per file/doc results. + if ($code != 0) { + // This means an internal cURL error occurred error is in result. + $message = 'Curl error ' . $code . ' retrieving'; +// . $filedoc['id'] . ': ' . $result . '.'; + debugging($message, DEBUG_DEVELOPER); + } else if (isset($info['http_code']) && ($info['http_code'] !== 200)) { + // Unexpected HTTP response code. + $message = 'Error while indexing file with document id ' ; + // Try to get error message out of msg or title if it exists. + if (preg_match('|]*name="msg"[^>]*>(.*?)|i', $result, $matches)) { + $message .= ': ' . $matches[1]; + } else if (preg_match('|]*>([^>]*)|i', $result, $matches)) { + $message .= ': ' . $matches[1]; + } + // This is a common error, happening whenever a file fails to index for any reason, so we will make it quieter. + if (CLI_SCRIPT && !PHPUNIT_TEST) { + mtrace($message); + if (debugging()) { + mtrace($requesturl); + } + // Suspiciion that this fails due to the file contents being PDFs. + } + } else { + // Check for the expected status field. + + if (preg_match('|]*name="status"[^>]*>(\d*)|i', $result, $matches)) { + // Now check for the expected status of 0, if not, error. + if ((int)$matches[1] !== 0) { + $message = 'Unexpected Solr status code ' . (int)$matches[1]; + debugging($message, DEBUG_DEVELOPER); + } else { + // We got a result back. +// echo htmlentities($result); +// debugging("Got SOLR update/extract response"); + $xml = simplexml_load_string($result); + print_r($xml->result); + print_r($xml->result['numFound']); + print_r($xml->result['maxScore']); + + } + } else { + // We received an unprocessable response. + $message = 'Unexpected Solr response'; + $message .= strtok($result, "\n"); + debugging($message, DEBUG_DEVELOPER); + } + } + return []; + } } diff --git a/search/engine/solrrag/tests/testdoc.txt b/search/engine/solrrag/tests/testdoc.txt new file mode 100644 index 000000000000..e69de29bb2d1 From 36e7bf5bf32b589f6bd73b0110fbaa06c1fee0d9 Mon Sep 17 00:00:00 2001 From: Michael hughes Date: Mon, 11 Mar 2024 20:50:49 +0000 Subject: [PATCH 09/21] stashing --- search/engine/solrrag/classes/document.php | 4 +- search/engine/solrrag/classes/engine.php | 36 +++++ search/engine/solrrag/cli/test.php | 37 +++++ .../tests/fixtures/mock_vector_search.php | 14 ++ .../tests/fixtures/testable_engine.php | 32 +++++ search/engine/solrrag/tests/testdoc.txt | 10 ++ .../engine/solrrag/tests/testdoc_vector.txt | 1 + search/engine/solrrag/tests/testvector.txt | 131 ++++++++++++++++++ .../solrrag/tests/vectorsearch_test.php | 115 +++++++++++++++ 9 files changed, 378 insertions(+), 2 deletions(-) create mode 100644 search/engine/solrrag/cli/test.php create mode 100644 search/engine/solrrag/tests/fixtures/mock_vector_search.php create mode 100644 search/engine/solrrag/tests/fixtures/testable_engine.php create mode 100644 search/engine/solrrag/tests/testdoc_vector.txt create mode 100644 search/engine/solrrag/tests/testvector.txt create mode 100644 search/engine/solrrag/tests/vectorsearch_test.php diff --git a/search/engine/solrrag/classes/document.php b/search/engine/solrrag/classes/document.php index 662c69ad9182..a9fc773d43d6 100644 --- a/search/engine/solrrag/classes/document.php +++ b/search/engine/solrrag/classes/document.php @@ -31,8 +31,8 @@ class document extends \search_solr\document { 'indexed' => true, 'mainquery' => true ), - 'solr_vector_1356' => [ - 'type' => 'knn_vector_1356', // this field def seems to be related to the size of the LLM embedding too :-( + 'solr_vector_1536' => [ + 'type' => 'knn_vector_1536', // this field def seems to be related to the size of the LLM embedding too :-( 'stored' => true, 'indexed' => true ], diff --git a/search/engine/solrrag/classes/engine.php b/search/engine/solrrag/classes/engine.php index 7eca210b1139..382a148cae4f 100644 --- a/search/engine/solrrag/classes/engine.php +++ b/search/engine/solrrag/classes/engine.php @@ -380,4 +380,40 @@ protected function execute_solr_knn_query($filters, $accessinfo, $limit) { } return []; } + + /** + * @see \search_solr\engine::get_schema_version() + * + * @param $oldversion + * @param $newversion + * @return bool|\lang_string|string + * @throws \coding_exception + * @throws \moodle_exception + */ + protected function update_schema($oldversion, $newversion) { + // Construct schema. + $schema = new \search_solrrag\schema($this); + $cansetup = $schema->can_setup_server(); + if ($cansetup !== true) { + return $cansetup; + } + + switch ($newversion) { + // This version just requires a setup call to add new fields. + case 2017091700: + $setup = true; + break; + + // If we don't know about the schema version we might not have implemented the + // change correctly, so return. + default: + return get_string('schemaversionunknown', 'search'); + } + + if ($setup) { + $schema->setup(); + } + + return true; + } } diff --git a/search/engine/solrrag/cli/test.php b/search/engine/solrrag/cli/test.php new file mode 100644 index 000000000000..7cafb28e6f84 --- /dev/null +++ b/search/engine/solrrag/cli/test.php @@ -0,0 +1,37 @@ +get_engine(); + +/** + * \core\ai\AIProvider + */ +$provider = core\ai\api::get_provider(1); + +$doccontent = file_get_contents($CFG->dirroot . "/search/engine/solrrag/tests/testdoc.txt"); +if (file_exists($CFG->dirroot . "/search/engine/solrrag/tests/testdoc_vector.txt")) { + $vector = file_get_contents($CFG->dirroot . "/search/engine/solrrag/tests/testdoc_vector.txt"); +} else { + $client = new \core\ai\AIClient($provider); + $vector = $client->embed_query($doccontent); + file_put_contents($CFG->dirroot . "/search/engine/solrrag/tests/testdoc_vector.txt", $vector); +} +$doc = [ + 'id' => 'testdoc', + 'solr_vector_1356' => $vector, + 'title' => "this is a test document" +]; + +$document = new \search_solrrag\document("1", "mod_xaichat", "files"); +$document->set('title', 'test document'); +$document->set('solr_vector_1536', $vector); +$document->set('content',$doccontent); +$document->set('contextid', context_system::instance()->id); +$document->set('courseid', SITEID); +$document->set('owneruserid', $USER->id); +$document->set('modified', time()); +$engine->add_document($document); + diff --git a/search/engine/solrrag/tests/fixtures/mock_vector_search.php b/search/engine/solrrag/tests/fixtures/mock_vector_search.php new file mode 100644 index 000000000000..11f5723ad929 --- /dev/null +++ b/search/engine/solrrag/tests/fixtures/mock_vector_search.php @@ -0,0 +1,14 @@ +. + +namespace search_solr; + +/** + * Search engine for testing purposes. + * + * @package search_solr + * @category phpunit + * @copyright 2016 Eric Merrill {@link http://www.merrilldigital.com} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die; + +class testable_enginer extends \search_solrrag\engine { + +} diff --git a/search/engine/solrrag/tests/testdoc.txt b/search/engine/solrrag/tests/testdoc.txt index e69de29bb2d1..eee75d9730c4 100644 --- a/search/engine/solrrag/tests/testdoc.txt +++ b/search/engine/solrrag/tests/testdoc.txt @@ -0,0 +1,10 @@ +Moodle requires a directory to store all of its files (all your site's uploaded files, temporary data, cache, session data etc.). The web server needs to be able to write to this directory. On larger systems consider how much free space you are going to use when allocating this directory. + +Due to the default way Moodle caches data you may have serious performance issues if you use relatively slow storage (e.g. NFS) for this directory. Read the Performance recommendations carefully and consider using (e.g.) redis or memcached for Caching. + +IMPORTANT: This directory must NOT be accessible directly via the web. This would be a serious security hole. Do not try to place it inside your web root or inside your Moodle program files directory. Moodle will not install. It can go anywhere else convenient. + +Here is an example (Unix/Linux) of creating the directory and setting the permissions for anyone on the server to write here. This is only appropriate for Moodle servers that are not shared. Discuss this with your server administrator for better permissions that just allow the web server user to access these files. + +# mkdir /path/to/moodledata +# chmod 0777 /path/to/moodledata diff --git a/search/engine/solrrag/tests/testdoc_vector.txt b/search/engine/solrrag/tests/testdoc_vector.txt new file mode 100644 index 000000000000..c8d2397208ca --- /dev/null +++ b/search/engine/solrrag/tests/testdoc_vector.txt @@ -0,0 +1 @@ +0.00557748930.0128397360.09669043-0.00146631470.0584328320.00249966750.026286405-0.021661168-0.014503565-0.016617360.033360290.010537583-0.021870455-0.04028768-0.0034584620.0182497970.035097370.0409992560.0203008060.0400365370.040392324-0.0048554502-0.006100706-0.0029849508-0.029446632-0.028065340.0069221560.0183649040.04955908-0.03323472-0.025114398-0.031079069-0.02572133-0.007194229-0.01420010.031853430.019149728-0.0096010250.007952893-0.058097973-0.05328438-0.0048240570.031267427-0.0098469370.017025469-0.022770388-0.03735767-0.034092795-0.008449948-0.05424710.000114371884-0.0143047430.0121700190.007947661-0.0254283290.0035107837-0.000110120750.015005853-0.0108096550.0301582070.02226810.01395942-0.0222681-0.014273350.09233727-0.034929942-0.0235866070.0021020228-0.0193799440.06739030.0081203230.0029247810.025930617-0.044243198-0.018825335-0.0534099530.0337997940.012358377-0.014011742-0.0043374660.0061896527-0.007424444-0.0352648-0.010364922-0.00115827090.037859954-0.0431967640.0392831040.0046697087-0.011280551-0.011594481-0.015434891-0.0388226730.0602327-0.011803768-0.0237958930.0017436194-0.007984286-0.0054937745-0.015979037-0.05939555-0.067892590.014398922-0.010626530.04922422-0.0141687070.0015186362-0.015832536-0.06584158-0.038592458-0.0890724-0.0190764780.073710760.0370646680.027688624-0.018187010.06132099-0.0010490493-0.02637012-0.03246036-0.0012289050.027018907-0.008256358-0.0253655430.049307935-0.0384041-0.032209218-0.0189718340.01112358550.00561934660.0184067620.00511444270.07203647-0.082124084-0.039534250.037755310.0056716683-0.00206016540.0012269430.0039921430.007429676-0.00199083940.0289652720.081496224-0.013404810.03949239-0.026056190.047005784-0.022247171-0.0255539-0.028839702-0.0168161820.0060902415-0.0046252350.0021648088-0.028839702-0.006315225-0.010914299-0.024193536-0.013101345-0.0075290874-0.0095748640.022058813-0.0286513440.0213053820.03398815-0.017328935-0.0088162-0.0540378130.0139908130.0016756012-0.0235866070.027835125-0.008162180.021556525-0.0144303150.0052217020.000844340830.05052180.012368841-0.0444943420.019055549-0.00256899370.027542124-0.0420875470.009810312-0.0298233480.042254977-0.0413341150.021996027-0.017621936-0.037755310.0053786670.06626015-0.0087691110.020782165-0.009914955-0.04173176-0.020698450.027018907-0.0179044730.00760757-0.02274946-0.037232097-0.018406762-0.02147281-0.04767550.0158220720.0018770397-0.00075735606-0.0273956230.045164060.0449547730.03338122-0.036248450.0215774530.0249678980.018710228-0.027960697-0.031267427-0.0160522870.007607570.022791317-0.00078286290.00706342470.018647442-0.0386971-0.039638890.049517220.037001880.032188290.012379305-0.00104512520.00526094340.032020860.0119816605-0.0194218010.0029666384-0.0159267150.0811613650.015393034-0.04403391-0.0369181670.042003833-0.019222979-0.0031837733-0.034092795-0.065255580.0340509380.0205310210.0020954825-0.0171405770.04158526-0.013059488-0.037399527-0.06433472-0.054498244-0.0086801640.039011030.0009018947-0.035641517-0.0134571320.0124002340.0204682350.029572204-0.0076598916-0.0455826330.013457132-0.00358665010.0229796750.03480437-0.04767550.0061844205-0.00036265454-0.0158639290.0212112030.0154348910.00541006-0.0351810870.009841705-0.011238693-0.0228122450.0126932360.0305558520.0049705580.041982904-0.014032670.041501544-0.0050124153-0.0050385760.0081046260.025051612-0.0330254320.00014388458-0.0065820655-0.012190947-0.0240051780.03513923-0.021273990.0261817610.032146430.0312883560.01652318-0.0031733090.035243873-0.0091876840.029237345-0.039576106-0.022519244-0.0014179171-0.0472569280.00793196450.026642190.032272004-0.0245283950.01772658-0.02337732-0.0262654760.020091519-0.027751410.010767798-0.0355578030.00929756-0.052823950.03218829-0.01811376-0.0120549110.040685326-0.035746160.0068279770.02994892-0.0187730140.0234191770.051275230.047842927-0.0306186380.050479940.031246498-0.0315395-0.0260980460.01912880.00332765779.777611E-50.057386402-0.0511078-0.010045759-0.0336532930.0621581380.0424014780.017998653-0.067599590.005849562-0.02413075-0.043322336-0.0145349580.012326984-0.009752758-0.031267427-0.030283779-0.00372791850.0085388950.0078744110.00699540650.0499776530.0297396330.06939945-0.00022792624-0.019191585-0.007042496-0.0319580730.011992125-0.011866554-0.06998546-0.022707602-0.051442660.044661770.0196520160.03934589-0.0082197330.0182602610.0108829060.0080680010.003900580.00592281250.025470186-0.0094388280.031079069-0.014262886-0.014001277-0.00659253-0.033862580.041564330.0021124870.040769040.0150791030.0138129190.0497683660.00633092130.019379944-0.023963321-0.0235866070.05114966-0.0310999970.0244028230.022602959-0.0308488530.015686035-0.029990777-0.036625165-0.063706860.008878986-0.029572204-0.00435316240.0183753690.013205987-0.016565038-0.007900571-0.0136140970.0018953523-0.009778919-0.023900535-0.011228229-0.053075094-0.030744210.0208135580.0199554820.02854670.030765139-0.018092832-0.02649569-0.047424357-0.0061791884-0.068646020.027688624-0.008716789-0.024946969-0.046838354-0.068311160.0003806401-0.03216736-0.0152779260.0171196480.0121176970.03794367-0.0133315595-0.024172608-0.0137815260.0161046090.0193799440.009396971-0.0076965170.014608208-0.0179881880.00029627140.0508148-0.021472810.04688021-0.005389131-0.0099934380.014974460.0285048430.0388436020.00348462280.0245283950.0013512069-0.037797168-0.019976411-0.00390319620.0022851487-0.0586839770.00387703530.0316441430.026579404-0.03478344-0.052656524-0.026453832-0.06374872-0.060776845-0.017475436-0.0447454860.0231471040.033339363-0.05110780.009187684-0.0087691110.0728736150.028379270.0081831080.004188349-0.0097109005-0.054414530.0085127340.03578802-0.006477422-0.04407577-0.032355720.021430952-0.0500613670.0191497280.0221215990.0188462620.029069915-0.06546486-0.005781544-0.026893334-0.017590543-0.062995285-0.0363321640.0378390250.046084920.00537343460.01772658-0.0273537650.0264538320.0066553154-0.01820794-0.0176847220.040099323-0.0141896350.0022576798-0.049224220.027583981-0.028797844-0.016460394-0.000296925430.0020837102-0.03273243-0.04238055-0.0027625838-0.0316022860.0193590150.01026551050.0158430.021514667-0.00335643460.037587885-0.008261590.012515342-0.024214465-0.0146919230.0237121790.025574830.012138626-0.0123688410.007947661-0.011437516-0.009841705-0.0132687730.020782165-0.04859636-0.032439433-0.036980953-0.00671810140.0171405770.033716080.030848853-0.010495726-0.05646554-0.00746630130.03105814-0.0060536163-9.115415E-50.014670994-0.0016010429-0.008790039-0.015194211-0.036771666-0.0212112030.0028907720.0018665753-0.019515980.030827925-0.0149430670.00149116750.0004087630.030723281-0.02413075-0.016408073-0.019400872-0.0342811530.030200064-0.029572204-0.020332199-0.016094144-0.0125467350.024570253-0.00241987690.0038901158-0.025909688-0.00176324010.00116938920.00487114680.027939769-0.0153930340.013111809-0.0027416550.0252818280.014922138-0.00905688-0.0014833192-0.01903462-0.0209286660.014221028-0.044033910.0233563910.008115090.0257631880.041292258-0.0097736865-0.00560365-0.0207717-0.033757936-0.006702405-0.0378180970.028881560.00122498090.0263910460.0199450180.0026553245-0.0208344870.0053028010.026663119-0.00201830830.006759959-0.039597034-0.015058175-0.031581357-0.009982973-0.0429037620.01089337-0.039220320.0039319730.044494342-0.037755310.014336136-0.0205833430.021786740.005187693-0.0237749650.0102602780.0302419220.0504380840.016753396-0.0039842950.000973837-0.014765173-0.0198822320.026223619-0.023188962-0.025051612-0.053284380.0375251-0.015403498-0.00234270260.0229378179.180817E-50.004470886-0.049768366-0.0530750940.018657906-0.0036363555-0.0144931-0.020070590.006142563-0.0233982480.012452556-0.0164813230.004878995-0.025365543-0.0045075114-0.048303360.0275211950.0159895010.0382576-0.015905786-0.0312883560.04771736-0.05177752-0.01195026750.0281699840.008533663-0.0103492250.003746231-0.0122432690.008156948-0.017245220.0223936720.026160832-0.014650065-0.00523739870.05345181-0.04533149-0.0277723390.0103963150.0148907450.0436571950.009161524-0.0145140290.070822604-0.0592281220.00699017430.00423020640.0154453550.031560430.014451243-0.027353765-0.038843602-0.0221215990.008894683-0.007884875-0.022205314-0.059646696-0.00406539350.017517293-0.00617918840.00168737360.033988150.0027547355-0.01195026750.00223936720.0118456250.020541485-0.008899915-0.038027383-0.0184381550.0231052470.016554574-0.017077790.010563744-0.015267462-0.00156572580.0073041040.00706342470.0445362-0.01176191-0.0231680330.0272491220.033674220.02031127-0.0110607995-0.0205100920.0089627010.00366251640.0114584440.012609521-0.042547980.0129757730.0282955560.0117305170.05960484-0.0081307870.00397383050.00186526730.019913625-0.016031358-0.018009117-0.0167533960.04545706-0.0248632540.0250725410.0043060730.043699052-0.0064878864-0.009328953-0.011427051-0.025512043-0.007298872-0.0159057860.00462785130.0069901743-0.009240006-0.04482920.024612110.0142524210.00910397-0.002486587-0.007979054-0.0246958250.019076478-0.018291654-0.028107198-0.0036180430.0180928320.0012282510.0038744193-0.0172870770.020907737-0.048973076-0.008031376-0.064418435-0.047884785-0.038718030.013802455-0.012839736-0.015665106-0.0024316492-0.0028541468-0.055419106-0.0372320970.022833174-0.0226029590.018228868-0.0066448510.022895960.0279397690.05550282-0.024402823-0.009475453-0.00387965140.033695150.0227076020.0029221650.01608368-0.00045389042-0.0035186320.00161804750.02492604-0.019714803-0.039408676-0.0071366750.00049738283-0.02743748-0.038425030.009909723-0.014440780.007701749-0.040727183-0.0019489820.021336775-0.0221215990.045205917-0.0222890280.023272676-0.0249051120.017716115-0.000464681770.0214937390.0127037-0.0128920580.0178312230.026830548-0.0074924620.015947644-0.0134885250.0046330835-0.044494342-0.021619310.009794615-0.0094754530.0189195130.0023924080.020876344-0.021326311-0.010757334-0.0035421767-0.019683410.013049023-0.007874411-0.0415434-0.003652052-0.0224773870.0085964490.0322720040.0044970470.014336136-0.013488525-0.015194211-0.0216820970.028672272-0.0168161820.0133838810.0264538320.012306055-0.0135094530.0019345935-0.0117514460.0019908394-0.01277695-0.008544127-0.020886809-0.0095487030.0041020180.00411771470.014639601-0.00541006-0.0250306840.0175800790.0307232810.00070895854-0.013603632-0.00029512687-0.016690610.024235394-0.025386471-0.0194741230.040559754-0.018260261-0.0011170675-0.00293786150.024842326-0.024298180.0211274880.015508141-0.016533645-0.06513001-0.0198717680.0231261760.020583343-0.010872441-0.0233563910.0306604950.0079110360.005007183-0.00917722-0.009402203-0.02272853-0.0209705230.0120444470.0021844294-0.006801816-0.0239005350.017318470.00291170060.006173956-0.0111026570.0157174280.024967898-0.0212844540.07906850.0051013622-0.0037619276-0.049768366-0.0265794040.0135199180.014901210.026872406-0.0277932680.00612163450.040329540.02093913-0.019034620.0078587140.0258469020.0206461290.00693262040.0193380860.0045022790.017747508-0.02840020.015298855-0.016805718-0.0023230820.0045336722-0.0136978110.0073198006-0.005127523-0.020520557-0.010657923-0.0022210546-0.0375251-0.0276886240.0594792660.00387703530.0270189070.0168998970.0203426630.0118874820.01927530.002009152-0.021577453-0.0044054840.0091353630.014545422-0.028190913-0.01294438-0.035432230.0035683375-0.0017671642-0.048512645-0.073585190.0082720550.050647370.010129474-0.030011706-0.057177115-0.004217126-0.019118335-0.021420488-0.0112910150.014116385-0.0031052907-0.009653347-0.049266078-0.0112386930.010333529-0.00558795360.0065506725-0.00489992370.0092609350.0117933030.011667731-0.0048240570.03637402-0.00432438540.0122432690.0283583420.003994759-0.017799830.02139956-0.0041778847-0.0340509380.022100670.039304033-0.0064564934-0.0177579730.020708915-0.0192543720.03009542-0.023523820.00134335870.023063390.00044538817-0.023021532-0.012023518-0.0041281790.025198113-0.0050673530.041899190.0143884580.0031366837-0.0072308537-0.023230819-0.008585985-0.0180614390.021786740.031916216-0.0144826360.02272853-0.016575502-0.016240645-0.0206252-0.0517356620.0295722040.004873763-0.01625111-0.006765191-0.01427335-0.024130750.0079424290.010725941-0.053619240.0130908810.0106160660.0301582070.014890745-0.00592804470.0168998970.004677557-0.015497677-0.04062254-0.025365543-0.00397644660.02385868-0.0211170240.0237331070.0318325-0.009553935-0.0114689090.021807669-0.030639566-0.0025336766-0.0195264440.00728317540.0028489146-0.014555886-0.0222471710.020541485-0.018061439-0.00213734-0.0126199850.0149535310.0133106310.0128292720.0241098220.000413014120.0124106980.0458756350.0073930510.00580247260.0120444470.017328935-0.01222234-0.008125555-0.0137605970.0049522454-0.00564027530.0016206636-0.0053629703-0.0051118266-0.007905804-0.0167220030.0014009125-0.02695612-0.0118874820.016983612-0.00086134540.0145663510.00411509860.0142210280.0007560480.0046723248-0.037692524-0.018050974-0.024988826-0.00139437230.015549999-0.0032151663-0.0017436194-0.002677561-0.0159999650.00111445140.000586329670.018867190.0008580753-0.014775638-0.0204682350.00246304230.035704304-0.002168733-0.026328262-0.023021532-0.043657195-0.0097946150.0058234013-0.00225113960.022749460.0158743930.0111445140.0080470720.032481290.0284420570.0089260760.029048987-0.00047874323-0.01676386-0.00342445290.0300744920.0325859340.019139264-0.026704976-0.0254492570.0100405270.0030765138-0.0087691110.018323047-0.0094126680.04487106-0.0270607640.0203740560.016805718-0.0147128515-0.011740982-0.034134652-0.0271863360.031895287-0.020541485-0.0167324670.03963889-0.0344067250.0135513110.0096114890.011887482-0.021054238-0.011437516-0.03248129-0.025219042-0.0067756553-0.012923451-0.00249181920.0402248950.0036337394-0.041627117-0.0329835750.032585934-0.005221702-0.007016335-0.0150267820.00160627510.027270050.0017475436-0.0026030028-0.0138129190.007361658-0.007879643-0.03449044-0.0070372640.0116049450.0350136570.0237540360.02980242-0.0295303460.019568302-0.00165859670.009925419-0.00905688-0.0177161150.013467596-0.015372105-0.033444006-0.022749460.0087115570.010542816-0.011050335-0.01112358550.0175486860.0024054884-0.03777624-0.0103335290.03352772-0.016648753-0.008240662-0.0118665540.0466290680.0368135240.00039241248-0.058767690.01339434550.00016988190.0212425960.00044538817-0.011563088-0.020750772-0.0035709536-0.006571601-0.0228331740.0298861340.06579972-0.0042485190.0123479120.0215983820.043531623-0.0132897020.0631627140.0165336450.0234401060.0199659470.0248632540.00208240210.0009790692-0.01763240.01277695-0.01020272450.023733107-0.0039476696-0.009396971-0.00567690050.007220390.015330248-0.0225401730.027939769-0.020238020.0137919905-0.02101238-0.03526480.00722562150.011918875-0.0044944310.00737212230.0266003330.0163452870.027521195-0.000854151150.0145663510.027604910.00155133730.00126749230.027897911-0.005624579-0.018741620.026349190.0053943633-0.045164060.00668147630.0201019830.0139698840.022937817-0.032606862-0.0279816260.07375262-0.0011948960.00058142450.006974478-0.0069587813-0.019066013-0.0200915190.044075770.0032988808-0.04110390.019610160.0149953890.00105297340.0189299770.0138757050.0072099254-0.0245911810.0048711468-0.00486068240.042464264-0.0076232664-0.0125990570.00627336740.006378011-0.0029535580.016387144-0.0020627815-0.048847504-0.004622619-0.013478060.000274688730.01936948-0.0192125140.0023322382-0.01277695-0.00090647280.02729098-0.024026107-0.0234401060.00151471210.0094388280.04275726-0.012002589-0.00356048930.0372320975.0686613E-5-0.017412650.000205689550.012672307-0.011604945-0.014231493-0.040706255-0.03009542-0.012818808-0.0217448830.0097527580.051191516-0.0056716683-0.06646944-0.020227555-0.0108096550.0025559133-0.0454152040.00028220998-0.00013031364-0.0019254372-0.00204316080.037336740.011918875-0.012557199-0.001258336-0.00400260740.0048162090.00663438670.036729810.040141180.019212514-0.01789401-0.0214518810.010323064-0.04487106-0.003432301-0.016272038-0.008779575-0.00722562150.014116385-0.0100039020.0029378615-0.0187206920.03702281-0.0286094860.003984295-0.0176219360.0196520160.021106560.00128645890.0054362207-0.0147651730.0183230470.019965947-0.0073878190.0222053140.0221634560.0160418230.0262236190.0274165510.018971834-0.019746196-0.018281190.0094388280.0087481820.009062112-0.01652318-0.011050335-0.014430315-0.013153667-0.00796335750.0082720550.014974460.021661168-0.0084342520.025302757-0.04453620.02007059-0.0049287006-0.00353171230.006163492-0.0048632985-0.00258338220.0286932010.0100248310.036771666-0.020761237-0.033757936-0.0109142990.0213367750.0147756380.031581357-0.028295556-0.0178626160.0186265130.018647442-0.0237331070.0006020261-0.009062112-0.043238620.05726083-0.0055984180.012149090.0342811530.024402823-0.016324360.0194845870.008088930.0235028920.00486329850.014294279-0.010286439-0.021064702-0.0041883490.030597710.032565005-0.0133001660.00452844-0.0172870770.02605619-0.012766486-0.0220169560.00278612850.01523606850.027730482-0.0141373140.020007804-0.0102812070.003803785-0.02888156-0.0049443970.000238554090.0452477750.008790039-0.023461035-0.00081033180.0347206560.0278560540.0101347060.015068639-0.008099394-0.00507258530.0027965930.0389063880.00592804470.019285765-0.0190032270.02164024-0.010150403-0.00555656060.0702366-0.0264329030.00299803120.0079267320.0065140473-0.0164708590.0178312230.040057465-0.004460422-0.0111968360.034574155-0.0227076020.0053158810.0085598240.022875031-0.0240051780.007387819-0.013143202-0.010385850.055795822-0.013258309-0.024800468-0.0123165190.0044578060.026746834-0.0297605620.0171091840.015434891-0.0038508745-0.0087324860.00045519846-0.00586002650.011291015-0.023042460.0078901070.02161931-0.00369129330.0068018160.0188985840.00280444110.0079790540.0441594830.014786102-0.04482920.004102018-0.0296559180.0105218870.015403498-0.03398815-0.021661168-0.012170019-0.005389131 \ No newline at end of file diff --git a/search/engine/solrrag/tests/testvector.txt b/search/engine/solrrag/tests/testvector.txt new file mode 100644 index 000000000000..575d8b57c08f --- /dev/null +++ b/search/engine/solrrag/tests/testvector.txt @@ -0,0 +1,131 @@ +[-0.027786661000000001,0.0090511510000000003,-0.044274903999999997,-0.014116816000000001,-0.027637671999999999,-0.026917553,-0.020126086000000001,0.072856180000000006,-0.018462362,-0.028258463000000001,-0.0012904718999999999,0.0 + 22807906999999999,0.0097464390000000008,-0.0071825663999999997,-0.013272539,-0.043306466000000002,-6.3679705e-5,0.00088850889999999997,-0.046609079999999997,0.053785439999999997,-0.0091194380000000005,-0.0021650129999999999,-0.0 + 13061468999999999,-0.041270266999999999,0.010646587000000001,0.042685675999999999,-0.013620182,0.016376499999999999,0.039755534000000002,0.041295100000000001,0.0019896391000000001,-0.063320810000000005,0.0015030932999999999,-0.0 + 61185284999999999,0.013483607999999999,-0.041568250000000001,0.0011445857000000001,-0.061880574000000001,0.012353766,0.022311273999999999,-0.023714265000000002,-0.0024816170000000002,0.047105714999999999,0.023143134999999999,-0. + 016500656999999998,-0.027513513,-0.0022922754,-0.012788320000000001,-0.0023962580000000001,-0.0092001410000000002,-0.017034540000000001,0.020970362999999999,0.0086290120000000001,0.018400282,-0.0040817090000000002,0.01021824,0.0 + 033367584000000001,0.009131854,-0.0040910210000000002,0.0036564662999999998,0.001761498,0.0088152500000000002,-0.027215533,-0.089940383999999998,-0.038762268000000002,0.022820323999999999,-0.084676064999999995,0.0099450920000000 + 009,-0.029947017999999999,0.027836324999999999,0.017940896000000001,0.0044790159999999997,0.016339251999999999,0.032008050000000003,0.023230046000000001,-0.0093987949999999997,0.016997293,-0.017940896000000001,0.014514123,0.0392 + 34072000000002,-0.010187201,-0.0056398977000000003,-0.0148990145,-0.046435259999999999,-0.0056709372000000001,0.0055716102999999999,0.013669845999999999,-0.028183969,-0.037446189999999997,-0.026768561,0.026867888999999999,-0.040 + 425990000000002,-0.023602521000000001,0.024359887,0.016897965000000001,-0.054629717000000001,0.0031970800000000001,0.021255927000000001,0.048322469999999999,0.013433944,-0.0063879521999999998,-0.058155819999999997,-0.02345353199 + 9999999,0.019778442,0.012540002999999999,0.041841400000000001,0.031536248000000003,0.028010146999999999,-0.054331740000000003,0.035558979999999997,-0.048272807000000001,-0.0078468140000000006,-0.0033957337,-0.010255488,-0.022869 + 987000000001,0.045963459999999998,0.0138933305,-0.0084303590000000001,0.02984769,0.017369768000000001,0.039383059999999998,-0.005764056,0.016438580000000001,-0.046956724999999998,0.033075809999999997,0.0016652752,0.0289289190000 + 00001,0.0010925943,-0.026048442000000002,-0.031039612000000001,0.0114287855,0.013483607999999999,-0.025216580999999998,0.021889135000000001,-0.037595179999999999,-0.019654283000000002,-0.014501707000000001,-0.013421529,-0.032504 + 680000000001,0.017071787000000001,-0.064810709999999994,0.0017537382,-0.058304809999999999,-0.0014231662000000001,-0.027910819999999999,0.0062265460000000003,-0.00046249022000000001,0.016724143,-0.018251291999999999,-0.013694677 + 5,-0.025551809000000002,-0.035608639999999997,-0.048819102000000003,0.021802223999999999,-0.078021170000000001,0.0022814115999999999,0.023950164999999999,-0.021255927000000001,-0.013942994,0.06351946,-0.048918429999999999,0.0108 + 01786000000001,-0.024993095999999999,0.0083248239999999998,0.0013168555,0.012502756,0.031958385999999998,0.0066921404000000002,-0.027861156000000002,-0.036055613,0.004379689,0.010776954,-0.023950164999999999,-0.02244784899999999 + 9,-0.048843934999999998,0.031412086999999998,-0.019592202999999999,0.0013766068999999999,0.035211336000000003,0.011006647,0.012285478000000001,0.0045938626999999996,0.027414186,-0.038091812000000003,0.051053955999999998,-0.01070 + 2458,0.013011806000000001,-0.067095230000000006,-0.041692406000000001,-0.035236169999999997,0.014787272000000001,0.035112009999999999,-0.013645014,-0.024732362000000001,-0.028680601999999999,0.025775295,-0.00060760044,0.01929422 + 2999999999,0.030319492999999999,0.0049942737000000003,-0.0044107290000000004,0.017096618000000001,-0.00083962149999999998,0.018598936,-0.031511415000000001,-0.02674373,-0.025067592,0.012155111999999999,-0.0054443479999999999,-0. + 028481949999999999,0.057659183000000003,0.0069280415999999996,0.011012855,-0.025166918,-0.011484656500000001,0.022062957000000001,-0.033795930000000002,0.068982440000000006,-0.016364085,0.048595619999999999,-0.028233632000000002 + ,0.081398280000000003,0.012751073,-0.040972290000000001,-0.0039575505000000004,0.02291965,0.0042306990000000001,-0.041220605,-0.027439017,0.01542048,0.0006921834,0.016910382000000002,-0.024732362000000001,-0.033100642,-0.0164634 + 10000000001,-0.0070149525999999999,-0.040525316999999998,-0.017071787000000001,0.018139550000000001,-0.017742243000000001,-0.022770660000000002,-0.038191139999999998,-0.013868498999999999,0.012825568000000001,0.00844277400000000 + 01,-0.010497596999999999,0.0013890227,-0.0070646160000000001,0.031362425999999999,-0.031337592999999997,0.035236169999999997,-0.0023947059999999999,0.021106936,0.019915016000000001,0.022969313000000002,0.025874620000000001,0.026 + 917553,-0.040699140000000002,0.040972290000000001,-0.019964678,-0.032703336,-0.033795930000000002,0.017692579,-0.054331740000000003,-0.040574983000000002,-0.0091132299999999999,-0.016860718,-0.011503279999999999,-0.0124282609999 + 99999,0.064314074999999998,-0.032703336,0.021442164,-0.0040475655999999997,0.035658307,-0.057559859999999997,0.074594400000000005,-0.016041270999999999,0.022112619999999999,-0.012726240999999999,-0.04248702,-0.007964765000000000 + 4,0.023403868000000001,-0.017332520000000001,-0.012527587999999999,-0.015308736999999999,0.011497073,0.070770316,0.0032995108000000001,0.0061892989999999997,-0.00035889552000000001,0.028630938000000002,-0.048943263000000001,0.02 + 3465946000000001,0.030220167999999999,-0.034367059999999998,-0.026247097,0.0021060377000000001,0.017034540000000001,-0.022447848999999999,-0.030145670999999999,0.055126349999999998,0.036105279999999997,0.010752121999999999,0.030 + 319492999999999,-0.0055405706999999997,0.0056647295,-0.032281197999999997,0.0025530079999999998,0.021578738,-0.02124351,-0.054530393000000003,0.038265634,-0.035012685000000002,0.0023605625999999998,0.086811589999999994,-0.006273 + 1057,-0.0061365315000000004,-0.0084179430000000006,0.019877767000000001,-0.035856961999999999,0.016513073999999999,-0.041915894000000002,-0.010007171,0.044771537,-0.024210898000000002,-0.045243338000000001,0.021678065999999999,- + 0.011801260500000001,-0.069379739999999995,-0.010789369,0.024111569999999999,-0.06287384,-0.0070832394999999996,0.046162113999999997,0.0070335763000000003,-0.033572446999999998,-0.0025499042,-0.051202945,-0.00016218198,-0.005860 + 2790000000004,-0.012887647,0.006400368,0.019964678,-0.001118202,-0.061483264000000003,0.027612839,0.059546391999999997,-0.039556883000000001,-0.044101080000000001,0.048620447999999997,0.019095569999999999,0.009318092,0.014303054 + 000000001,-0.021988460000000001,0.0292269,-0.028953751999999999,0.027339690999999999,0.049315735999999999,-0.026942383,0.027662503000000001,-0.071167624999999998,0.031387255000000003,-0.038091812000000003,0.031461753000000002,-0 + .025253828999999998,-0.018412698000000002,-0.0069466652000000004,-0.0095167450000000001,0.0036005949999999998,-0.012260647,0.023453531999999999,0.057410865999999998,-0.013794004,0.055374670000000001,0.01556947,-0.000937396299999 + 99995,-0.0081261700000000003,-0.023192799,-0.03866294,0.027588008000000001,-0.023639767999999999,-0.029475215999999999,0.0095415770000000007,-0.041468921999999998,-0.030046346000000002,-0.0028649562000000002,-0.03233086000000000 + 3,-0.0049601300000000001,-0.052593517999999999,-0.023379036999999998,-0.00059091660000000002,-0.034093909999999998,0.014315469000000001,-0.0088649130000000003,0.0031815602,-0.029003412999999999,-0.082093570000000005,0.0556726499 + 99999997,-0.0099575080000000003,0.019455627999999999,0.044721875000000001,0.0017521861,-0.022720996,0.033199970000000002,-0.05408342,0.010013378999999999,-0.055076689999999998,0.04966338,-0.0080206359999999994,0.0281343049999999 + 98,0.0063103530000000003,-0.012242022999999999,0.026545078,0.003845808,0.0039823823999999997,-0.0095229530000000007,-0.0056430018000000002,-0.015855035,-0.0057609519999999997,0.043231970000000002,0.025725630999999999,-0.04174206 + 9999999999,0.024210898000000002,0.04055015,-0.0022286442000000002,0.0026942384,0.045739975000000002,0.036328763,0.030096008,0.033994585000000001,0.015110083999999999,-0.025949117000000001,-0.029475215999999999,0.0307168009999999 + 98,-0.011751598,-0.029798029,-0.010243072000000001,-0.0026880304999999998,-0.031983215000000002,0.021442164,-0.017866401000000001,0.053835105000000001,0.020858620000000001,-0.04966338,-0.088102840000000002,0.026470581,0.03344828 + 6000000001,0.010491389,0.040128009999999999,-0.020933113999999999,-0.024471631000000001,-0.022820323999999999,-0.0037092335999999999,-0.012130281,0.053040490000000003,0.0094050030000000003,0.030989948999999999,0.0533384699999999 + 99,-0.0049849619999999999,-0.042313200000000002,-0.056864570000000003,-0.0141044,-0.011155636999999999,-0.015743291,0.020746877,-0.034689869999999998,0.018474778000000001,0.024782025999999999,-0.070968970000000006,-0.03997902200 + 0000003,-0.0028261568000000001,0.00065571179999999996,-0.0036626741999999999,0.025502145,-0.029673868999999999,0.060887302999999997,-0.0094360420000000004,-0.020088836999999998,0.036403257000000001,-0.0090201115000000005,-0.0176 + 55331999999999,0.025266245,0.015693627000000002,-0.015345984,-0.019952264000000001,-0.029971850000000001,0.0090821909999999999,-0.016438580000000001,-0.015668795999999999,-0.00081866980000000001,0.016537904999999999,0.0099326759 + 999999997,0.01601644,-0.012688993500000001,-0.012912478999999999,-0.0056833530000000004,0.029450384999999999,-0.061234946999999998,0.011906795,-0.010317567,0.016773807000000002,0.015544638,-0.012862815499999999,0.037768999999999 + 997,-0.047503023999999998,0.0041065407000000003,-0.011074934,0.0097836859999999998,-0.0054195164999999998,0.06595297,0.00099481960000000007,0.042611179999999999,-0.045293002999999998,-0.00044774642000000001,0.0090635669999999998 + ,-0.037520683999999999,0.0063444966999999996,-0.018785172999999999,-0.00020447344,-0.035782464,0.021044858,0.0014805895,0.032728170000000001,0.016848301999999999,-0.010447933,0.020846203000000001,0.026843058,0.014588617999999999 + ,-0.011211508,-0.0072073980000000003,0.045566152999999998,0.044746703999999998,0.0075798732999999997,-0.02011367,-0.029723532,0.017556004,0.0050253132000000001,0.065754320000000005,0.00530467,-0.019815689000000001,-0.03203288,-0 + .0012268406999999999,-0.022323688000000001,-0.0048608035000000001,-0.032827492999999999,0.040103180000000002,-0.027165870000000002,-0.0057392245999999996,0.057013559999999998,0.0094546660000000005,0.020895866999999999,0.01173918 + 1,0.043927260000000003,-0.024335057,-0.024868937000000001,-0.053338469999999999,-0.034044247,-0.0079771800000000004,-0.00023337909,0.0064810709999999997,0.0060868681999999997,0.0056740413,-0.032827492999999999,0.0043610656000000 + 001,-0.011137012999999999,0.040773634000000003,0.028829591000000002,-0.0048359715999999999,-0.0034888523999999999,-0.024993095999999999,0.015631549000000002,0.031312760000000002,0.013272539,-0.062824174999999996,0.04695672499999 + 9998,0.0018794485000000001,0.025253828999999998,-0.0089890714999999993,-0.023043807999999999,0.065903305999999995,0.011981291,0.016972460000000002,-0.027364521999999999,-0.032504680000000001,-0.0097153989999999996,0.004935298599 + 9999996,-0.019244560000000001,-0.0045907585999999997,-0.040922627000000003,0.041915894000000002,-0.024744779000000001,0.033522780000000002,-0.0058602790000000004,-0.0064189920000000001,-0.034242901999999999,0.016239925999999998, + 0.024881354000000001,0.025502145,0.0094360420000000004,-0.022584422,0.03143692,0.0081075469999999997,-0.0030775775999999999,-0.00019118461999999999,-0.01903349,-0.042611179999999999,0.0077102400000000001,-0.069330080000000002,0. + 035683140000000002,-0.026718898000000001,0.0069839129999999996,-0.0022022604999999999,-0.034143575000000002,0.0041034365999999996,-0.02542765,0.0042648427000000003,-0.028556444,0.0013494472,-0.024918599999999999,-0.0038240803000 + 000002,-0.0059565017000000001,-0.015743291,-0.049290907000000002,0.021988460000000001,-0.013632597999999999,0.033994585000000001,-0.035434822999999997,0.059149085999999997,-0.0028416766000000002,0.018946579000000002,-0.011776429 + ,-0.0068535465,-0.036552247000000003,-0.010267904,0.018176798000000001,-0.022671332999999998,-0.0051215360000000003,0.018462362,-0.011484656500000001,-0.0084055269999999994,0.0044666002999999999,0.057311542,-0.011174261,0.003575 + 7634000000002,0.021752560000000001,-0.020523392000000001,0.015494974,0.0041375802999999997,0.009299468,0.0055964420000000001,-0.028159136000000001,-0.011621230999999999,0.0020749980000000002,-0.041617910000000001,6.7996150000000 + 001e-5,0.025291075999999999,-0.020262658999999999,0.015271489500000001,0.0075612496999999997,0.02982286,-0.0021588050000000002,0.037992484999999999,0.023341787999999999,0.01873551,0.008945616,0.041841400000000001,0.027712166,0.0 + 093677550000000002,-0.0031365528999999998,-0.035037513999999999,0.012924895,0.030393988,0.0041406843999999996,-0.031958385999999998,-0.00031932,-0.0055126350000000001,-0.0024241938000000001,-0.016662065,0.0067418039999999997,0.0 + 34044247,-0.0124034295,-0.039879695,-0.0053605409999999999,-0.021255927000000001,0.014663113,-0.018139550000000001,0.005847863,-0.044672209999999997,0.0041096450000000003,0.0013277193999999999,-0.034218070000000003,0.035782464,0 + .033771097999999999,0.033175137,-0.010826617,0.0020346467,0.015445311999999999,-0.0035354117999999999,-0.011074934,-0.036279100000000002,0.015134915000000001,-0.021926383000000001,0.023130719000000001,-0.010336191,0.033175137,-0 + .032057714000000001,0.019517709000000001,-0.0085669330000000005,0.020200579999999999,-0.0010111154000000001,0.010193408500000001,-0.011447409,0.0048235557,-0.023316956999999999,0.0010491389999999999,0.033969751999999999,0.017742 + 243000000001,0.015681212999999999,0.022336104999999998,0.012316518,-0.0022814115999999999,0.012167528,-0.032877160000000002,0.0075364179999999996,0.010174785,-0.0044479766000000004,-0.0292269,-0.024049493000000002,-0.016587568,- + 0.0073253483999999999,0.040748804999999999,-0.00400411,0.0037309613999999998,0.013818835999999999,0.014886597999999999,0.0060868681999999997,-0.022820323999999999,0.017046954,-0.012496548,-0.0065493584000000001,-0.010547261,0.01 + 5544638,-0.0030418820000000001,-0.0011647615,0.013545687000000001,0.02540282,0.023403868000000001,0.0014107504,-0.0014565337999999999,-0.028084641,-0.036204603000000002,0.0086165960000000007,0.0036130110000000002,-0.0051743034,- + 0.021094522000000001,-0.010516220999999999,-0.015395648,-0.054778710000000001,-0.013433944,0.011975082999999999,-0.0083062009999999992,0.0028168450000000001,-0.021144183,-0.0035012683000000002,0.029351057,0.0060527246,-0.0008047 + 0194000000004,-0.011832301,-0.013371865,-0.015879865999999999,0.034069080000000002,-0.018487192999999999,0.017704995000000001,0.03116377,0.027439017,0.036229434999999997,0.027935651999999998,0.019182479999999998,0.00227054770000 + 00002,0.024161234,0.020697213999999999,-0.025005512000000001,0.012502756,-0.027215533,-0.018648600000000001,0.049812370000000002,-0.0078716454999999998,-0.023863254,-0.034789197000000001,0.02704171,-0.0069466652000000004,-0.0226 + 46501999999999,0.0011391537,-0.059298075999999998,0.024347473000000001,0.039234072000000002,-0.014700361,-0.042909160000000002,-0.0035074762999999999,0.0075302099999999999,-0.0067542195000000003,0.0087097159999999993,0.031660404 + 000000003,0.040997119999999998,0.046559419999999997,0.0082565369999999996,0.025191749999999999,-0.061830907999999997,0.010143746,-0.024335057,-0.036105279999999997,0.030691968,0.030120839999999999,-0.03143692,-0.0143154690000000 + 01,0.011776429,0.0022208841999999999,-0.0045473029999999999,0.019666698,-0.017233193000000001,-0.01072729,0.0078964770000000007,0.035757635000000003,-0.039258899999999999,0.044920526000000002,0.052295540000000001,-0.018437530000 + 000001,0.031511415000000001,0.048918429999999999,-0.011192884,0.0092932599999999994,-0.026768561,-0.0061210114999999997,-0.0030279143000000001,0.0025607680000000002,-0.0055747143999999998,0.01822646,0.016984875999999999,-0.04343 + 0626,0.03866294,-0.0077412794000000004,0.022907235000000001,-0.018164381,-0.023602521000000001,0.0094546660000000005,-0.0041034365999999996,0.033572446999999998,0.020734460999999999,-0.028283294,0.0014542057999999999,0.013185628 + ,0.018276122999999998,0.00029254835999999998,0.024794442999999999,0.0078840620000000007,0.0099140530000000008,-0.016239925999999998,-0.029897355,0.004181036,0.004742853,0.0072322297000000004,0.0090014880000000002,0.0022301962999 + 999999,-0.029947017999999999,0.032604010000000003,0.027736996999999999,-0.00025219685999999997,-0.023503195000000001,-0.037048879999999999,-0.046733240000000002,0.023999829,-0.0027407978,-0.038836761999999997,-0.0076481607,-0.01 + 7481509999999999,0.0051029124000000004,-0.026818225000000001,-0.021094522000000001,0.014278222,0.012682785,-0.019654283000000002,-0.039457555999999998,-0.0060278926999999998,0.015755707000000001,-0.011379122,-0.00276097350000000 + 01,0.0029503150999999998,-0.00075891849999999998,-0.027339690999999999,0.044796370000000002,-0.038687773000000002,-0.029996682,0.019753608999999998,-0.041518588000000002,-0.026594739999999999,0.0014673977000000001,0.004063085000 + 0000001,0.042909160000000002,0.014116816000000001,-0.024335057,-0.036676407000000001,-0.04303332,-0.023279709999999999,0.020225410999999999,-0.017270440000000001,0.0029518672,-0.00062079227000000001,0.0030930974000000001,0.00953 + 53690000000001,0.016984875999999999,-0.0038178724000000001,0.013421529,-0.0020765502,-0.022596838000000001,0.0026771666000000001,-0.0011507936,0.019381134000000001,0.00080082199999999997,0.028606106999999999,-0.02223678000000000 + 1,-0.015035588000000001,-0.014464459000000001,-0.022211946999999999,-0.016997293,0.0095353690000000001,0.034888524999999997,-0.010832825000000001,0.016302003999999998,-0.010180992999999999,0.0025607680000000002,0.036800563000000 + 001,0.016935213000000001,-0.025924283999999999,0.0087904184999999992,0.021206263,0.030890622999999999,-0.020635133999999999,-0.044796370000000002,0.019145234000000001,0.034516048000000001,0.00067317159999999996,-0.02279549000000 + 0002,0.033895257999999998,0.035385158,-0.025055175999999998,-0.015470143,-0.0094608739999999993,-0.032157036999999999,-0.012713825,-0.0013432392,0.017680162999999999,0.010640379,0.036552247000000003,-0.024769612,0.01765533199999 + 9999,0.014563786,-0.0080765079999999996,0.00035617955,0.011434994,-0.010776954,-0.027339690999999999,0.0063134569999999997,0.023205215000000001,0.025998779,-0.020461312999999998,-0.043207143000000003,0.0037713130000000001,0.0099 + 078445000000005,-0.02181464,0.0059937489999999996,-0.034590546,0.0054971150000000003,-0.009131854,-0.017357351,0.0088835370000000004,0.010615546999999999,0.017258024,0.040574983000000002,0.0040289415,-0.026445750000000001,0.0332 + 49630000000002,0.011453616999999999,-0.0092063490000000008,-0.019617036000000001,-0.026470581,0.014389964999999999,0.051004290000000001,-0.0090759824999999995,-0.0091442699999999995,0.040351495000000001,0.043604450000000003,-0.0 + 15333569,-0.014166478999999999,-0.0078033586,-0.0041344759999999998,-0.0018002974999999999,-0.049787539999999998,0.017034540000000001,0.036030779999999998,0.025452481999999998,0.0065555659999999997,0.032082542999999998,0.0328771 + 60000000002,0.018164381,-0.021578738,-0.013396697000000001,-0.024570957000000001,-0.002633711,-0.045640646999999999,0.0066114375000000001,0.025725630999999999,0.02512967,0.022373351999999999,-0.0075860815000000003,0.014601034000 + 000001,-0.017183529999999999,0.035484485000000003,0.034118740000000002,-0.052841840000000001,-0.012136488000000001,0.034863690000000003,0.0075426260000000002,0.035310662999999999,-0.023292126,0.025824957999999999,-0.001839097,-0 + .0016311316000000001,-0.002408674,-0.0099885470000000004,0.053139816999999999,0.01476244,0.0076854080000000003,-0.014253389999999999,0.0046745655000000001,0.032157036999999999,0.039830033000000001,-0.001766154,-0.009647110999999 + 9999,-0.0051339519999999998,0.011838507999999999,0.038315295999999999,0.026818225000000001,-0.013806419,0.044945359999999997,-0.00072787893999999998,-0.013545687000000001,-0.04998619,0.044995019999999997,0.0072322297000000004,-0 + .020275075,0.013570518,-0.029475215999999999,-0.042064882999999997,-0.002006711,-0.013843668,0.013297369999999999,-0.014414796000000001,0.0038116643999999999,-0.012297895,0.020473728,-0.016488243,0.009665735,-0.00726326929999999 + 98,0.014340301,0.022236780000000001,-0.015730876000000001,-0.019915016000000001,-0.015010756,-0.0048328675,0.035136840000000003,0.0051991353000000002,-0.015842617999999999,-0.00064484790000000002,0.029698702,-0.01797814299999999 + 9,-0.0021029337999999998,0.029152404999999999,-0.00044076249999999999,0.019778442,-0.036055613,-0.0016776911000000001,0.014774855,-0.017816736999999999,0.0078778540000000001,-0.031635574999999999,0.018363034,-0.01580537099999999 + 9,0.021007609999999999,-0.042735339999999997,-0.0079399329999999997,0.010125122,0.016537904999999999,0.010298943,-0.014191311,-0.030145670999999999,-0.0027345898999999998,-8.9287390000000001e-5,0.016860718,-0.031635574999999999, + -0.020275075,-0.030567810000000001,0.0095912399999999991,-0.0048328675,-0.0094546660000000005,0.024558541999999999,-0.0058230315,-0.072309880000000007,-0.010261696000000001,0.070422670000000007,-0.0094050030000000003,0.006890794 + 0000000004,0.0073253483999999999,0.010832825000000001,0.0036099068999999998,0.011037686,-0.0107397055,-0.0041965559999999997,-0.022410599999999999,-0.0051215360000000003,0.00020932338999999999,-0.0036005949999999998,-0.008852498 + 0000000003,0.019617036000000001,0.0030791296000000001,0.018810006000000001,-0.018363034,0.0076109130000000004,-0.069230749999999994,-0.031238265000000001,0.00063864,-0.00027858053,-0.016798638000000001,-0.009131854,0.00534191730 + 00000004,0.047329202000000001,0.014961093999999999,0.0059751253999999997,0.014799687000000001,-0.015470143,0.019542540000000001,0.022596838000000001,-0.0091380620000000006,-0.0073998435000000003,0.0077723189999999998,0.014228558 + 000000001,-0.014067152499999999,-0.013322202,0.07732588,-0.016264757000000001,-0.0079585565,-0.00093506834999999996,-0.015259073999999999,-0.0026942384,0.0095788239999999997,-0.0090076949999999996,-0.0022177803,-0.02172773000000 + 0001,-0.015507391000000001,-0.069777049999999993,0.020461312999999998,0.040053516999999997,0.010932150999999999,-0.014563786,0.0033833177000000001,0.012030954,-0.013297369999999999,-0.013446359999999999,0.0039575505000000004,0.0 + 053295013,0.0087035075000000007,-0.0063258730000000001,-0.012304102000000001,-0.021827055000000001,-0.0041841400000000003,-0.0083744869999999999,0.024359887,0.0030496417999999999,0.024595789999999999,-0.0055933379999999998,-0.01 + 6066104000000001,0.027488680000000001,0.0092746359999999993,0.021851888,-0.04934057,-0.01682347,0.0048483876000000002,0.0049290903999999998,0.01075833,-0.016922796,-0.021640818999999999,-0.0036130110000000002,-0.0121054490000000 + 01,0.022435432000000002,-0.0030030825,-0.044721875000000001,-0.0083124080000000003,-0.010398271000000001,0.0071949824000000001,0.010174785,0.021963630000000001,0.032157036999999999,-0.0024288497000000001,0.0068038832000000002,0. + 010677626000000001,-0.031337592999999997,0.065505999999999995,-0.0039730705000000002,0.021678065999999999,-0.0045690310000000003,-0.0011026822000000001,0.0068349229999999997,-0.014961093999999999,0.0099637149999999997,0.01636408 + 5,-6.7511159999999996e-5,0.0021945005,0.010236864,-0.0102244485,-0.015159746999999999,0.011795053,-0.014402379999999999,0.016190263,-0.018896916999999999,-0.0025157606999999999,-0.021876718999999999,-0.013011806000000001,0.00191 + 04880999999999,-0.022261610000000001,0.034814030000000003,0.057162549999999999,-0.019554955999999998,-0.014961093999999999,0.034342225999999997,-0.018884499999999999,0.0074991705000000001,0.01822646,0.051451261999999998,0.028034 + 977999999999,-0.0048080359999999999,0.012273063000000001,0.00272683,-0.0092870520000000005,-0.005379165,0.015581885,-0.0060372044999999997,-0.0060930760000000002,-0.015917113,-0.0030279143000000001,-0.00052612139999999997,-0.019 + 33147,-0.044001753999999997,0.0084862300000000009,-0.024037076000000001,-0.015470143,-0.01189438,0.043256804000000003,0.033249630000000002,0.018300956,-0.023416283999999999,-0.0083558629999999998,0.023167968000000001,-0.00643761 + 54999999996,-0.010801786000000001,0.013905747,0.024012245000000002,-0.033249630000000002,-0.034367059999999998,0.022075371999999999,0.027836324999999999,0.00032630393999999999,-0.017419430999999999,0.00094050022999999999,0.01770 + 4995000000001,0.021082105,0.013781588000000001,0.012751073,0.0046838773,0.046609079999999997,0.014923845,0.012837984,-0.023230046000000001,0.02925173,0.0094670819999999999,0.012446885,0.023267293000000001,-0.026420920000000001,0 + .028407452999999999,0.020548224,-0.0088214580000000008,0.014154063999999999,-0.056715580000000002,0.031461753000000002,-0.037669674,0.022100205000000001,0.045590980000000003,-0.013880915000000001,0.019194896999999999,-0.04549166 + 0000000003,0.0051401596999999999,-0.020622720000000001,0.023652184999999999,-0.0029425554,-0.00054164125999999998,-0.018934164,-0.0076543684000000001,-0.0060372044999999997,-0.019170064000000001,-0.014365133,-0.01843753000000000 + 1,0.00094282825000000002,-0.035981119999999998,0.0148990145,0.0067418039999999997,0.024868937000000001,0.0067976750000000004,-0.027513513,0.0031520726999999999,0.018425113999999999,-0.0084179430000000006,-0.055076689999999998,0. + 0037092335999999999,-0.0029518672,-0.0102244485,-0.024198482,0.028730266000000001,-0.015408063,0.037247534999999998,9.1178860000000006e-5,-0.013545687000000001,-0.026967215999999999,0.010149953,0.02121868,-0.016612400999999999,- + 0.0014456699,0.0034205653000000001,0.0046528378000000002,-0.0098830129999999995,0.023366620000000001,-0.0074184669999999998,0.0091628934999999998,0.030170504000000001,0.00042912264999999999,0.025949117000000001,-0.02111935200000 + 0001,-0.039904527000000002,0.0085048539999999992,-0.039681040000000001,0.017407015000000001,0.0012423604,0.031859060000000002,0.016165430000000001,-0.017704995000000001,0.022149868,-0.017990559999999999,0.012813152,-0.014427212, + 0.0025048967999999998,0.0016311316000000001,-0.011875755999999999,0.020386817000000002,0.019828104999999999,-0.033721436,-0.013098716999999999,0.0054971150000000003,0.0067914673000000004,-0.0031365528999999998,0.023391452,-0.033 + 175137,-0.029996682,0.030393988,-0.0050842889999999996,0.005286046,-0.027885988,-0.034590546,0.0061458433000000003,-3.8526704999999997e-6,-0.017034540000000001,0.039705873000000003,0.029996682,0.012018538,-0.0081882499999999993, + -0.030642306000000001,0.00064019200000000004,0.0036968180000000002,-0.0063755362999999999,-0.011348083,0.022733413000000001,-0.014427212,0.017779489999999998,-0.010994230000000001,-0.0082813690000000002,0.0074681310000000002,0.0 + 15656380000000001,0.038712605999999997,-0.028804759999999999,0.026147770000000001,-0.0040289415,0.0018437530000000001,-0.025874620000000001,-0.013682260999999999,-0.0046745655000000001,0.040152844,0.017071787000000001,0.00448522 + 40000000003,0.0011779532999999999,-0.0037961446000000002,0.0028059809999999999,-0.00084738140000000005,-0.020858620000000001,0.011844716,0.022472679999999998,-0.0052208630000000001,-0.0095850320000000003,-0.0063041453000000003,- + 0.012086825000000001,0.024099154000000001,0.019977095,-0.0052084469999999997,0.024918599999999999,0.025626303999999999,-0.00034027173999999998,-0.025328323,0.011962667,0.010994230000000001,-0.017258024,0.015308736999999999,-0.03 + 7421357000000002,0.0037216494999999998,-0.014377548,-0.013520855,0.022720996,-0.018797589999999999,0.0083869029999999994,-0.032231532,0.0050656646000000003,-0.021082105,0.016450995999999999,0.00023202109999999999,-0.008200666000 + 0000006,-0.020064005999999999,-0.020212995000000001,0.0058602790000000004,-0.017431846000000001,-0.0059844369999999996,-0.020647550000000001,-0.030716800999999998,0.016066104000000001,-0.015768124000000001,0.052593517999999999,0 + .0048576994000000002,-0.0070646160000000001,-0.010752121999999999,0.022460263000000001,-0.025179334000000001,0.051401599999999999,-0.02432264,-0.0041375802999999997,0.017680162999999999,-0.0025731840000000001,-0.0364032570000000 + 01,0.00044541843999999997,-0.0022255399999999998,0.00075659057000000002,0.041170944000000001,-0.019157647999999999,-0.00062932812999999998,0.024111569999999999,0.019095569999999999,-0.016115766,0.0096719430000000006,0.0174070150 + 00000001,-0.018474778000000001,0.00049663379999999996,-0.021615986,-0.0077164475999999997,-0.0051215360000000003,-0.023130719000000001,0.0086290120000000001,-0.0028990997999999999,0.015507391000000001,-0.011782637,0.004482120299 + 9999997,0.026247097,-0.017469093000000002,0.028779929999999999,0.032554346999999997,0.013918162,-0.018611351000000002,-0.010013378999999999,0.0016063,0.04303332,-0.036800563000000001,0.0086352200000000007,-0.0033181347000000002, + -0.012962141999999999,-0.0087966260000000001,0.00067161960000000002,-0.011490864999999999,-0.023006559999999999,0.0034143572999999999,-0.024782025999999999,0.025315909000000001,-0.0031241369999999999,-0.023304539999999999,0.0038 + 923675,0.022845154999999999,0.030220167999999999,0.019617036000000001,-0.036825396000000003,-0.0062420660000000001,-0.0088835370000000004,-0.0070459920000000001,0.018971412,0.0065245264999999998,-0.00065105589999999997,0.0173201 + 03999999999,0.0023559065999999998,-0.031089275999999999,0.010901111999999999,-0.025216580999999998,0.021082105,-0.030195335,-0.026445750000000001,-0.0082565369999999996,-0.05025934,0.01933147,-0.015619133,0.0064127840000000004,- + 0.0092622199999999998,-0.014948677,-0.028829591000000002,0.0039482387000000004,-0.024409551000000002,-0.012695201999999999,0.038240799999999998,0.0064251999999999998] diff --git a/search/engine/solrrag/tests/vectorsearch_test.php b/search/engine/solrrag/tests/vectorsearch_test.php new file mode 100644 index 000000000000..7afdb90b0bcc --- /dev/null +++ b/search/engine/solrrag/tests/vectorsearch_test.php @@ -0,0 +1,115 @@ +dirroot . '/search/tests/fixtures/testable_core_search.php'); +require_once($CFG->dirroot . '/search/tests/fixtures/mock_search_area.php'); +//require_once($CFG->dirroot . '/search/engine/solr/tests/fixtures/testable_engine.php'); + +class vectorsearch_test extends \advanced_testcase { + + /** + * @var \core_search\manager + */ + protected $search = null; + /** + * @var Instace of core_search_generator. + */ + protected $generator = null; + + /** + * @var Instace of testable_engine. + */ + protected $engine = null; + public function setUp(): void { + $this->resetAfterTest(); + set_config('enableglobalsearch', true); + set_config('searchengine', 'solrrag'); + + if (!function_exists('solr_get_version')) { + $this->markTestSkipped('Solr extension is not loaded.'); + } + + if (!defined('TEST_SEARCH_SOLR_HOSTNAME') || !defined('TEST_SEARCH_SOLR_INDEXNAME') || + !defined('TEST_SEARCH_SOLR_PORT')) { + $this->markTestSkipped('Solr extension test server not set.'); + } + + set_config('server_hostname', TEST_SEARCH_SOLR_HOSTNAME, 'search_solrrag'); + set_config('server_port', TEST_SEARCH_SOLR_PORT, 'search_solrrag'); + set_config('indexname', TEST_SEARCH_SOLR_INDEXNAME, 'search_solrrag'); + + if (defined('TEST_SEARCH_SOLR_USERNAME')) { + set_config('server_username', TEST_SEARCH_SOLR_USERNAME, 'search_solrrag'); + } + + if (defined('TEST_SEARCH_SOLR_PASSWORD')) { + set_config('server_password', TEST_SEARCH_SOLR_PASSWORD, 'search_solrrag'); + } + + if (defined('TEST_SEARCH_SOLR_SSLCERT')) { + set_config('secure', true, 'search_solr'); + set_config('ssl_cert', TEST_SEARCH_SOLR_SSLCERT, 'search_solrrag'); + } + + if (defined('TEST_SEARCH_SOLR_SSLKEY')) { + set_config('ssl_key', TEST_SEARCH_SOLR_SSLKEY, 'search_solrrag'); + } + + if (defined('TEST_SEARCH_SOLR_KEYPASSWORD')) { + set_config('ssl_keypassword', TEST_SEARCH_SOLR_KEYPASSWORD, 'search_solrrag'); + } + + if (defined('TEST_SEARCH_SOLR_CAINFOCERT')) { + set_config('ssl_cainfo', TEST_SEARCH_SOLR_CAINFOCERT, 'search_solrrag'); + } + + set_config('fileindexing', 1, 'search_solrrag'); + + // We are only test indexing small string files, so setting this as low as we can. + set_config('maxindexfilekb', 1, 'search_solrrag'); + + $this->generator = self::getDataGenerator()->get_plugin_generator('core_search'); + $this->generator->setup(); + + // Inject search solr engine into the testable core search as we need to add the mock + // search component to it. + $this->engine = new \search_solrrag\testable_engine(); + $this->search = \testable_core_search::instance($this->engine); + $areaid = \core_search\manager::generate_areaid('core_mocksearch', 'mock_search_area'); + $this->search->add_search_area($areaid, new \core_mocksearch\search\mock_search_area()); + + $this->setAdminUser(); + + // Cleanup before doing anything on it as the index it is out of this test control. + $this->search->delete_index(); + + // Add moodle fields if they don't exist. + $schema = new \search_solrrag\schema($this->engine); + $schema->setup(false); + } + + public function tearDown(): void { + parent::tearDown(); // TODO: Change the autogenerated stub + } + + public function test_vectorsearch() { + $this->markTestIncomplete('This test has not been implemented yet.'); + + $this->engine->test_set_config('fileindexing', true); + // id 1 is our "fake" but real implementation. + $aiprovider = \core\ai\api::get_provider(1); + $file = $this->generator->create_file(); + $record = new \stdClass(); + $record->attachfileids = [$file->get_id()]; + $this->generator->create_record($record); + + $this->search->index(); + $querydata = new \stdClass(); + $querydata->q = '"File contents"'; + $this->assertCount(1, $this->search->search($querydata)); + } +} From 596fd8958683424b8e58d3e687b60b717ce46121 Mon Sep 17 00:00:00 2001 From: Michael hughes Date: Mon, 11 Mar 2024 22:17:10 +0000 Subject: [PATCH 10/21] still trying to get search to work --- search/engine/solrrag/classes/document.php | 23 ++++++++ search/engine/solrrag/classes/engine.php | 48 +++++++++++++++- search/engine/solrrag/classes/schema.php | 51 ++++++++++++++++- .../engine/solrrag/cli/{test.php => add.php} | 26 +++++++-- search/engine/solrrag/cli/search.php | 56 +++++++++++++++++++ .../engine/solrrag/tests/testdoc_vector.txt | 2 +- 6 files changed, 197 insertions(+), 9 deletions(-) rename search/engine/solrrag/cli/{test.php => add.php} (57%) create mode 100644 search/engine/solrrag/cli/search.php diff --git a/search/engine/solrrag/classes/document.php b/search/engine/solrrag/classes/document.php index a9fc773d43d6..ba33197d8f81 100644 --- a/search/engine/solrrag/classes/document.php +++ b/search/engine/solrrag/classes/document.php @@ -78,4 +78,27 @@ public function export_file_for_engine($file) { public function fetch_document_contents() { } + public function set_data_from_engine($docdata) { + $fields = static::$requiredfields + static::$optionalfields + static::$enginefields; + foreach ($fields as $fieldname => $field) { + + // Optional params might not be there. + if (isset($docdata[$fieldname])) { + if ($field['type'] === 'tdate') { + // Time fields may need a preprocessing. + $this->set($fieldname, static::import_time_from_engine($docdata[$fieldname])); + } else { + // No way we can make this work if there is any multivalue field. + if($fieldname === 'solr_vector_1536' || $fieldname === 'solr_vector_3072') { + debugging("Skipping $fieldname"); + continue; + } + if (is_array($docdata[$fieldname])) { + throw new \core_search\engine_exception('multivaluedfield', 'search_solr', '', $fieldname); + } + $this->set($fieldname, $docdata[$fieldname]); + } + } + } + } } diff --git a/search/engine/solrrag/classes/engine.php b/search/engine/solrrag/classes/engine.php index 382a148cae4f..57794cbdf381 100644 --- a/search/engine/solrrag/classes/engine.php +++ b/search/engine/solrrag/classes/engine.php @@ -72,6 +72,17 @@ public function is_server_ready() */ public function add_document($document, $fileindexing = false) { $docdata = $document->export_for_engine(); + debugging("Adding document"); + if ($this->aiprovider->use_for_embeddings() && $this->aiclient) { + debugging('Generating vector using provider'); + $vector = $this->aiclient->embed_query($document['content']); + $vlength = count($vector); + $vectorfield = "solr_vector_" . $vlength; + $docdata[$vectorfield] = $vector; + var_dump($docdata); + } else { + debugging("Err didn't do any vector stuff!"); + } if (!$this->add_solr_document($docdata)) { return false; @@ -85,6 +96,37 @@ public function add_document($document, $fileindexing = false) { return true; } + public function add_document_batch(array $documents, bool $fileindexing = false): array { + $docdatabatch = []; + foreach ($documents as $document) { + //$docdatabatch[] = $document->export_for_engine(); + $doc = $document->export_for_engine(); + if ($this->aiprovider->use_for_embeddings() && $this->aiclient) { + debugging('Generating vector using provider'); + $vector = $this->aiclient->embed_query($doc['content']); + $vlength = count($vector); + $vectorfield = "solr_vector_" . $vlength; + $doc[$vectorfield] = $vector; + var_dump($doc); + } else { + debugging("Err didn't do any vector stuff!"); + } + $docdatabatch[] = $doc; + } + + $resultcounts = $this->add_solr_documents($docdatabatch); + + // Files are processed one document at a time (if there are files it's slow anyway). + if ($fileindexing) { + foreach ($documents as $document) { + // This will take care of updating all attached files in the index. + $this->process_document_files($document); + } + } + + return $resultcounts; + } + /** * Adds a file to the search engine. * @@ -291,7 +333,7 @@ protected function create_solr_document(array $doc): \SolrInputDocument { * @throws \core_search\engine_exception */ public function execute_query($filters, $accessinfo, $limit = 0) { - var_dump($filters->similarity); + if (isset($filters->similarity) && $filters->similarity ) { @@ -300,11 +342,13 @@ public function execute_query($filters, $accessinfo, $limit = 0) { $this->execute_solr_knn_query($filters, $accessinfo, $limit); } else { debugging("Running regular search", DEBUG_DEVELOPER); + print_r($filters); + print_r($accessinfo); return parent::execute_query($filters, $accessinfo, $limit); } } - protected function execute_solr_knn_query($filters, $accessinfo, $limit) { + public function execute_solr_knn_query($filters, $accessinfo, $limit) { $vector = $filters->vector; $topK = 3; // Nearest neighbours to retrieve. $field = "solr_vector_" . count($vector); diff --git a/search/engine/solrrag/classes/schema.php b/search/engine/solrrag/classes/schema.php index f79017846310..54f00cb74fa8 100644 --- a/search/engine/solrrag/classes/schema.php +++ b/search/engine/solrrag/classes/schema.php @@ -2,6 +2,7 @@ namespace search_solrrag; +use search_solr\document; use \search_solrrag\engine; class schema extends \search_solr\schema @@ -94,6 +95,54 @@ protected function validate_fields(&$fields, $requireexisting = false) { } } } + public function setup($checkexisting = true) { + $fields = \search_solrrag\document::get_default_fields_definition(); + + // Field id is already there. + unset($fields['id']); + + $this->check_index(); + + $return = $this->add_fields($fields, $checkexisting); + + // Tell the engine we are now using the latest schema version. + $this->engine->record_applied_schema_version(document::SCHEMA_VERSION); + + return $return; + } + protected function validate_add_field_result($result) { + + if (!$result) { + throw new \moodle_exception('errorcreatingschema', 'search_solrrag', '', get_string('nodatafromserver', 'search_solrrag')); + } + + $results = json_decode($result); + if (!$results) { + if (is_scalar($result)) { + $errormsg = $result; + } else { + $errormsg = json_encode($result); + } + throw new \moodle_exception('errorcreatingschema', 'search_solrrag', '', $errormsg); + } + + // It comes as error when fetching fields data. + if (!empty($results->error)) { + throw new \moodle_exception('errorcreatingschema', 'search_solrrag', '', $results->error); + } + + // It comes as errors when adding fields. + if (!empty($results->errors)) { + + // We treat this error separately. + $errorstr = ''; + foreach ($results->errors as $error) { + $errorstr .= implode(', ', $error->errorMessages); + } + throw new \moodle_exception('errorcreatingschema', 'search_solrrag', '', $errorstr); + } + + } // public function can_setup_server() { //print_r($this->engine); // $status = $this->engine->is_server_configured(); @@ -111,4 +160,4 @@ protected function validate_fields(&$fields, $requireexisting = false) { // // return true; // } -} \ No newline at end of file +} diff --git a/search/engine/solrrag/cli/test.php b/search/engine/solrrag/cli/add.php similarity index 57% rename from search/engine/solrrag/cli/test.php rename to search/engine/solrrag/cli/add.php index 7cafb28e6f84..9d192fee959a 100644 --- a/search/engine/solrrag/cli/test.php +++ b/search/engine/solrrag/cli/add.php @@ -1,7 +1,9 @@ libdir ."/clilib.php"); +$admin = $DB->get_record('user', ['id' => 2]); +\core\session\manager::set_user($admin); $search = $search = \core_search\manager::instance(true, true); $engine = $search->get_engine(); @@ -14,24 +16,38 @@ $doccontent = file_get_contents($CFG->dirroot . "/search/engine/solrrag/tests/testdoc.txt"); if (file_exists($CFG->dirroot . "/search/engine/solrrag/tests/testdoc_vector.txt")) { $vector = file_get_contents($CFG->dirroot . "/search/engine/solrrag/tests/testdoc_vector.txt"); + $vector = json_decode($vector, true); } else { $client = new \core\ai\AIClient($provider); $vector = $client->embed_query($doccontent); - file_put_contents($CFG->dirroot . "/search/engine/solrrag/tests/testdoc_vector.txt", $vector); + file_put_contents( + $CFG->dirroot . "/search/engine/solrrag/tests/testdoc_vector.txt", + json_encode($vector) + ); } $doc = [ 'id' => 'testdoc', 'solr_vector_1356' => $vector, 'title' => "this is a test document" ]; +cli_heading("Adding document to solr"); -$document = new \search_solrrag\document("1", "mod_xaichat", "files"); +$document = new \search_solrrag\document("1", "mod_page", "activity"); $document->set('title', 'test document'); $document->set('solr_vector_1536', $vector); $document->set('content',$doccontent); -$document->set('contextid', context_system::instance()->id); +$document->set('contextid', \core\context\system::instance()->id); $document->set('courseid', SITEID); $document->set('owneruserid', $USER->id); $document->set('modified', time()); -$engine->add_document($document); +var_dump($document); + +$result = $engine->add_document($document); +var_dump($result); +if ($result == false) { + cli_error("Failed to add document"); +} else { + cli_writeln("Document added to solr"); +} +cli_writeln("End of script"); diff --git a/search/engine/solrrag/cli/search.php b/search/engine/solrrag/cli/search.php new file mode 100644 index 000000000000..d986baf25857 --- /dev/null +++ b/search/engine/solrrag/cli/search.php @@ -0,0 +1,56 @@ +libdir ."/clilib.php"); +$search = $search = \core_search\manager::instance(true, true); + +$engine = $search->get_engine(); + +/** + * \core\ai\AIProvider + */ +$provider = core\ai\api::get_provider(1); + +$doccontent = file_get_contents($CFG->dirroot . "/search/engine/solrrag/tests/testdoc.txt"); +if (file_exists($CFG->dirroot . "/search/engine/solrrag/tests/testdoc_vector.txt")) { + $vector = file_get_contents($CFG->dirroot . "/search/engine/solrrag/tests/testdoc_vector.txt"); + $vector = json_decode($vector, true); +} else { + $client = new \core\ai\AIClient($provider); + $vector = $client->embed_query($doccontent); + file_put_contents( + $CFG->dirroot . "/search/engine/solrrag/tests/testdoc_vector.txt", + json_encode($vector) + ); +} +$admin = $DB->get_record('user', ['id' => 2]); +\core\session\manager::set_user($admin); +$doc = [ + 'id' => 'testdoc', + 'solr_vector_1356' => $vector, + 'title' => "this is a test document" +]; +$formdata = (object) [ + 'q' => 'directory', + 'areaids' => [], + 'title' => '', + 'courseids' => [], + 'timestart' => 0, + 'timeend' => 0, + 'context' => \core\context\system::instance(), +]; +//print_r($formdata); +cli_heading("Searching for document"); +$result = $search->search($formdata,0); + +var_dump($result); + + +cli_heading("Similarity search"); +$formdata->similarity = true; +$formdata->vector = $vector; +$result = $search->search($formdata,0); +var_dump($result); + + +cli_writeln("End of script"); diff --git a/search/engine/solrrag/tests/testdoc_vector.txt b/search/engine/solrrag/tests/testdoc_vector.txt index c8d2397208ca..5aa9c005a1ce 100644 --- a/search/engine/solrrag/tests/testdoc_vector.txt +++ b/search/engine/solrrag/tests/testdoc_vector.txt @@ -1 +1 @@ -0.00557748930.0128397360.09669043-0.00146631470.0584328320.00249966750.026286405-0.021661168-0.014503565-0.016617360.033360290.010537583-0.021870455-0.04028768-0.0034584620.0182497970.035097370.0409992560.0203008060.0400365370.040392324-0.0048554502-0.006100706-0.0029849508-0.029446632-0.028065340.0069221560.0183649040.04955908-0.03323472-0.025114398-0.031079069-0.02572133-0.007194229-0.01420010.031853430.019149728-0.0096010250.007952893-0.058097973-0.05328438-0.0048240570.031267427-0.0098469370.017025469-0.022770388-0.03735767-0.034092795-0.008449948-0.05424710.000114371884-0.0143047430.0121700190.007947661-0.0254283290.0035107837-0.000110120750.015005853-0.0108096550.0301582070.02226810.01395942-0.0222681-0.014273350.09233727-0.034929942-0.0235866070.0021020228-0.0193799440.06739030.0081203230.0029247810.025930617-0.044243198-0.018825335-0.0534099530.0337997940.012358377-0.014011742-0.0043374660.0061896527-0.007424444-0.0352648-0.010364922-0.00115827090.037859954-0.0431967640.0392831040.0046697087-0.011280551-0.011594481-0.015434891-0.0388226730.0602327-0.011803768-0.0237958930.0017436194-0.007984286-0.0054937745-0.015979037-0.05939555-0.067892590.014398922-0.010626530.04922422-0.0141687070.0015186362-0.015832536-0.06584158-0.038592458-0.0890724-0.0190764780.073710760.0370646680.027688624-0.018187010.06132099-0.0010490493-0.02637012-0.03246036-0.0012289050.027018907-0.008256358-0.0253655430.049307935-0.0384041-0.032209218-0.0189718340.01112358550.00561934660.0184067620.00511444270.07203647-0.082124084-0.039534250.037755310.0056716683-0.00206016540.0012269430.0039921430.007429676-0.00199083940.0289652720.081496224-0.013404810.03949239-0.026056190.047005784-0.022247171-0.0255539-0.028839702-0.0168161820.0060902415-0.0046252350.0021648088-0.028839702-0.006315225-0.010914299-0.024193536-0.013101345-0.0075290874-0.0095748640.022058813-0.0286513440.0213053820.03398815-0.017328935-0.0088162-0.0540378130.0139908130.0016756012-0.0235866070.027835125-0.008162180.021556525-0.0144303150.0052217020.000844340830.05052180.012368841-0.0444943420.019055549-0.00256899370.027542124-0.0420875470.009810312-0.0298233480.042254977-0.0413341150.021996027-0.017621936-0.037755310.0053786670.06626015-0.0087691110.020782165-0.009914955-0.04173176-0.020698450.027018907-0.0179044730.00760757-0.02274946-0.037232097-0.018406762-0.02147281-0.04767550.0158220720.0018770397-0.00075735606-0.0273956230.045164060.0449547730.03338122-0.036248450.0215774530.0249678980.018710228-0.027960697-0.031267427-0.0160522870.007607570.022791317-0.00078286290.00706342470.018647442-0.0386971-0.039638890.049517220.037001880.032188290.012379305-0.00104512520.00526094340.032020860.0119816605-0.0194218010.0029666384-0.0159267150.0811613650.015393034-0.04403391-0.0369181670.042003833-0.019222979-0.0031837733-0.034092795-0.065255580.0340509380.0205310210.0020954825-0.0171405770.04158526-0.013059488-0.037399527-0.06433472-0.054498244-0.0086801640.039011030.0009018947-0.035641517-0.0134571320.0124002340.0204682350.029572204-0.0076598916-0.0455826330.013457132-0.00358665010.0229796750.03480437-0.04767550.0061844205-0.00036265454-0.0158639290.0212112030.0154348910.00541006-0.0351810870.009841705-0.011238693-0.0228122450.0126932360.0305558520.0049705580.041982904-0.014032670.041501544-0.0050124153-0.0050385760.0081046260.025051612-0.0330254320.00014388458-0.0065820655-0.012190947-0.0240051780.03513923-0.021273990.0261817610.032146430.0312883560.01652318-0.0031733090.035243873-0.0091876840.029237345-0.039576106-0.022519244-0.0014179171-0.0472569280.00793196450.026642190.032272004-0.0245283950.01772658-0.02337732-0.0262654760.020091519-0.027751410.010767798-0.0355578030.00929756-0.052823950.03218829-0.01811376-0.0120549110.040685326-0.035746160.0068279770.02994892-0.0187730140.0234191770.051275230.047842927-0.0306186380.050479940.031246498-0.0315395-0.0260980460.01912880.00332765779.777611E-50.057386402-0.0511078-0.010045759-0.0336532930.0621581380.0424014780.017998653-0.067599590.005849562-0.02413075-0.043322336-0.0145349580.012326984-0.009752758-0.031267427-0.030283779-0.00372791850.0085388950.0078744110.00699540650.0499776530.0297396330.06939945-0.00022792624-0.019191585-0.007042496-0.0319580730.011992125-0.011866554-0.06998546-0.022707602-0.051442660.044661770.0196520160.03934589-0.0082197330.0182602610.0108829060.0080680010.003900580.00592281250.025470186-0.0094388280.031079069-0.014262886-0.014001277-0.00659253-0.033862580.041564330.0021124870.040769040.0150791030.0138129190.0497683660.00633092130.019379944-0.023963321-0.0235866070.05114966-0.0310999970.0244028230.022602959-0.0308488530.015686035-0.029990777-0.036625165-0.063706860.008878986-0.029572204-0.00435316240.0183753690.013205987-0.016565038-0.007900571-0.0136140970.0018953523-0.009778919-0.023900535-0.011228229-0.053075094-0.030744210.0208135580.0199554820.02854670.030765139-0.018092832-0.02649569-0.047424357-0.0061791884-0.068646020.027688624-0.008716789-0.024946969-0.046838354-0.068311160.0003806401-0.03216736-0.0152779260.0171196480.0121176970.03794367-0.0133315595-0.024172608-0.0137815260.0161046090.0193799440.009396971-0.0076965170.014608208-0.0179881880.00029627140.0508148-0.021472810.04688021-0.005389131-0.0099934380.014974460.0285048430.0388436020.00348462280.0245283950.0013512069-0.037797168-0.019976411-0.00390319620.0022851487-0.0586839770.00387703530.0316441430.026579404-0.03478344-0.052656524-0.026453832-0.06374872-0.060776845-0.017475436-0.0447454860.0231471040.033339363-0.05110780.009187684-0.0087691110.0728736150.028379270.0081831080.004188349-0.0097109005-0.054414530.0085127340.03578802-0.006477422-0.04407577-0.032355720.021430952-0.0500613670.0191497280.0221215990.0188462620.029069915-0.06546486-0.005781544-0.026893334-0.017590543-0.062995285-0.0363321640.0378390250.046084920.00537343460.01772658-0.0273537650.0264538320.0066553154-0.01820794-0.0176847220.040099323-0.0141896350.0022576798-0.049224220.027583981-0.028797844-0.016460394-0.000296925430.0020837102-0.03273243-0.04238055-0.0027625838-0.0316022860.0193590150.01026551050.0158430.021514667-0.00335643460.037587885-0.008261590.012515342-0.024214465-0.0146919230.0237121790.025574830.012138626-0.0123688410.007947661-0.011437516-0.009841705-0.0132687730.020782165-0.04859636-0.032439433-0.036980953-0.00671810140.0171405770.033716080.030848853-0.010495726-0.05646554-0.00746630130.03105814-0.0060536163-9.115415E-50.014670994-0.0016010429-0.008790039-0.015194211-0.036771666-0.0212112030.0028907720.0018665753-0.019515980.030827925-0.0149430670.00149116750.0004087630.030723281-0.02413075-0.016408073-0.019400872-0.0342811530.030200064-0.029572204-0.020332199-0.016094144-0.0125467350.024570253-0.00241987690.0038901158-0.025909688-0.00176324010.00116938920.00487114680.027939769-0.0153930340.013111809-0.0027416550.0252818280.014922138-0.00905688-0.0014833192-0.01903462-0.0209286660.014221028-0.044033910.0233563910.008115090.0257631880.041292258-0.0097736865-0.00560365-0.0207717-0.033757936-0.006702405-0.0378180970.028881560.00122498090.0263910460.0199450180.0026553245-0.0208344870.0053028010.026663119-0.00201830830.006759959-0.039597034-0.015058175-0.031581357-0.009982973-0.0429037620.01089337-0.039220320.0039319730.044494342-0.037755310.014336136-0.0205833430.021786740.005187693-0.0237749650.0102602780.0302419220.0504380840.016753396-0.0039842950.000973837-0.014765173-0.0198822320.026223619-0.023188962-0.025051612-0.053284380.0375251-0.015403498-0.00234270260.0229378179.180817E-50.004470886-0.049768366-0.0530750940.018657906-0.0036363555-0.0144931-0.020070590.006142563-0.0233982480.012452556-0.0164813230.004878995-0.025365543-0.0045075114-0.048303360.0275211950.0159895010.0382576-0.015905786-0.0312883560.04771736-0.05177752-0.01195026750.0281699840.008533663-0.0103492250.003746231-0.0122432690.008156948-0.017245220.0223936720.026160832-0.014650065-0.00523739870.05345181-0.04533149-0.0277723390.0103963150.0148907450.0436571950.009161524-0.0145140290.070822604-0.0592281220.00699017430.00423020640.0154453550.031560430.014451243-0.027353765-0.038843602-0.0221215990.008894683-0.007884875-0.022205314-0.059646696-0.00406539350.017517293-0.00617918840.00168737360.033988150.0027547355-0.01195026750.00223936720.0118456250.020541485-0.008899915-0.038027383-0.0184381550.0231052470.016554574-0.017077790.010563744-0.015267462-0.00156572580.0073041040.00706342470.0445362-0.01176191-0.0231680330.0272491220.033674220.02031127-0.0110607995-0.0205100920.0089627010.00366251640.0114584440.012609521-0.042547980.0129757730.0282955560.0117305170.05960484-0.0081307870.00397383050.00186526730.019913625-0.016031358-0.018009117-0.0167533960.04545706-0.0248632540.0250725410.0043060730.043699052-0.0064878864-0.009328953-0.011427051-0.025512043-0.007298872-0.0159057860.00462785130.0069901743-0.009240006-0.04482920.024612110.0142524210.00910397-0.002486587-0.007979054-0.0246958250.019076478-0.018291654-0.028107198-0.0036180430.0180928320.0012282510.0038744193-0.0172870770.020907737-0.048973076-0.008031376-0.064418435-0.047884785-0.038718030.013802455-0.012839736-0.015665106-0.0024316492-0.0028541468-0.055419106-0.0372320970.022833174-0.0226029590.018228868-0.0066448510.022895960.0279397690.05550282-0.024402823-0.009475453-0.00387965140.033695150.0227076020.0029221650.01608368-0.00045389042-0.0035186320.00161804750.02492604-0.019714803-0.039408676-0.0071366750.00049738283-0.02743748-0.038425030.009909723-0.014440780.007701749-0.040727183-0.0019489820.021336775-0.0221215990.045205917-0.0222890280.023272676-0.0249051120.017716115-0.000464681770.0214937390.0127037-0.0128920580.0178312230.026830548-0.0074924620.015947644-0.0134885250.0046330835-0.044494342-0.021619310.009794615-0.0094754530.0189195130.0023924080.020876344-0.021326311-0.010757334-0.0035421767-0.019683410.013049023-0.007874411-0.0415434-0.003652052-0.0224773870.0085964490.0322720040.0044970470.014336136-0.013488525-0.015194211-0.0216820970.028672272-0.0168161820.0133838810.0264538320.012306055-0.0135094530.0019345935-0.0117514460.0019908394-0.01277695-0.008544127-0.020886809-0.0095487030.0041020180.00411771470.014639601-0.00541006-0.0250306840.0175800790.0307232810.00070895854-0.013603632-0.00029512687-0.016690610.024235394-0.025386471-0.0194741230.040559754-0.018260261-0.0011170675-0.00293786150.024842326-0.024298180.0211274880.015508141-0.016533645-0.06513001-0.0198717680.0231261760.020583343-0.010872441-0.0233563910.0306604950.0079110360.005007183-0.00917722-0.009402203-0.02272853-0.0209705230.0120444470.0021844294-0.006801816-0.0239005350.017318470.00291170060.006173956-0.0111026570.0157174280.024967898-0.0212844540.07906850.0051013622-0.0037619276-0.049768366-0.0265794040.0135199180.014901210.026872406-0.0277932680.00612163450.040329540.02093913-0.019034620.0078587140.0258469020.0206461290.00693262040.0193380860.0045022790.017747508-0.02840020.015298855-0.016805718-0.0023230820.0045336722-0.0136978110.0073198006-0.005127523-0.020520557-0.010657923-0.0022210546-0.0375251-0.0276886240.0594792660.00387703530.0270189070.0168998970.0203426630.0118874820.01927530.002009152-0.021577453-0.0044054840.0091353630.014545422-0.028190913-0.01294438-0.035432230.0035683375-0.0017671642-0.048512645-0.073585190.0082720550.050647370.010129474-0.030011706-0.057177115-0.004217126-0.019118335-0.021420488-0.0112910150.014116385-0.0031052907-0.009653347-0.049266078-0.0112386930.010333529-0.00558795360.0065506725-0.00489992370.0092609350.0117933030.011667731-0.0048240570.03637402-0.00432438540.0122432690.0283583420.003994759-0.017799830.02139956-0.0041778847-0.0340509380.022100670.039304033-0.0064564934-0.0177579730.020708915-0.0192543720.03009542-0.023523820.00134335870.023063390.00044538817-0.023021532-0.012023518-0.0041281790.025198113-0.0050673530.041899190.0143884580.0031366837-0.0072308537-0.023230819-0.008585985-0.0180614390.021786740.031916216-0.0144826360.02272853-0.016575502-0.016240645-0.0206252-0.0517356620.0295722040.004873763-0.01625111-0.006765191-0.01427335-0.024130750.0079424290.010725941-0.053619240.0130908810.0106160660.0301582070.014890745-0.00592804470.0168998970.004677557-0.015497677-0.04062254-0.025365543-0.00397644660.02385868-0.0211170240.0237331070.0318325-0.009553935-0.0114689090.021807669-0.030639566-0.0025336766-0.0195264440.00728317540.0028489146-0.014555886-0.0222471710.020541485-0.018061439-0.00213734-0.0126199850.0149535310.0133106310.0128292720.0241098220.000413014120.0124106980.0458756350.0073930510.00580247260.0120444470.017328935-0.01222234-0.008125555-0.0137605970.0049522454-0.00564027530.0016206636-0.0053629703-0.0051118266-0.007905804-0.0167220030.0014009125-0.02695612-0.0118874820.016983612-0.00086134540.0145663510.00411509860.0142210280.0007560480.0046723248-0.037692524-0.018050974-0.024988826-0.00139437230.015549999-0.0032151663-0.0017436194-0.002677561-0.0159999650.00111445140.000586329670.018867190.0008580753-0.014775638-0.0204682350.00246304230.035704304-0.002168733-0.026328262-0.023021532-0.043657195-0.0097946150.0058234013-0.00225113960.022749460.0158743930.0111445140.0080470720.032481290.0284420570.0089260760.029048987-0.00047874323-0.01676386-0.00342445290.0300744920.0325859340.019139264-0.026704976-0.0254492570.0100405270.0030765138-0.0087691110.018323047-0.0094126680.04487106-0.0270607640.0203740560.016805718-0.0147128515-0.011740982-0.034134652-0.0271863360.031895287-0.020541485-0.0167324670.03963889-0.0344067250.0135513110.0096114890.011887482-0.021054238-0.011437516-0.03248129-0.025219042-0.0067756553-0.012923451-0.00249181920.0402248950.0036337394-0.041627117-0.0329835750.032585934-0.005221702-0.007016335-0.0150267820.00160627510.027270050.0017475436-0.0026030028-0.0138129190.007361658-0.007879643-0.03449044-0.0070372640.0116049450.0350136570.0237540360.02980242-0.0295303460.019568302-0.00165859670.009925419-0.00905688-0.0177161150.013467596-0.015372105-0.033444006-0.022749460.0087115570.010542816-0.011050335-0.01112358550.0175486860.0024054884-0.03777624-0.0103335290.03352772-0.016648753-0.008240662-0.0118665540.0466290680.0368135240.00039241248-0.058767690.01339434550.00016988190.0212425960.00044538817-0.011563088-0.020750772-0.0035709536-0.006571601-0.0228331740.0298861340.06579972-0.0042485190.0123479120.0215983820.043531623-0.0132897020.0631627140.0165336450.0234401060.0199659470.0248632540.00208240210.0009790692-0.01763240.01277695-0.01020272450.023733107-0.0039476696-0.009396971-0.00567690050.007220390.015330248-0.0225401730.027939769-0.020238020.0137919905-0.02101238-0.03526480.00722562150.011918875-0.0044944310.00737212230.0266003330.0163452870.027521195-0.000854151150.0145663510.027604910.00155133730.00126749230.027897911-0.005624579-0.018741620.026349190.0053943633-0.045164060.00668147630.0201019830.0139698840.022937817-0.032606862-0.0279816260.07375262-0.0011948960.00058142450.006974478-0.0069587813-0.019066013-0.0200915190.044075770.0032988808-0.04110390.019610160.0149953890.00105297340.0189299770.0138757050.0072099254-0.0245911810.0048711468-0.00486068240.042464264-0.0076232664-0.0125990570.00627336740.006378011-0.0029535580.016387144-0.0020627815-0.048847504-0.004622619-0.013478060.000274688730.01936948-0.0192125140.0023322382-0.01277695-0.00090647280.02729098-0.024026107-0.0234401060.00151471210.0094388280.04275726-0.012002589-0.00356048930.0372320975.0686613E-5-0.017412650.000205689550.012672307-0.011604945-0.014231493-0.040706255-0.03009542-0.012818808-0.0217448830.0097527580.051191516-0.0056716683-0.06646944-0.020227555-0.0108096550.0025559133-0.0454152040.00028220998-0.00013031364-0.0019254372-0.00204316080.037336740.011918875-0.012557199-0.001258336-0.00400260740.0048162090.00663438670.036729810.040141180.019212514-0.01789401-0.0214518810.010323064-0.04487106-0.003432301-0.016272038-0.008779575-0.00722562150.014116385-0.0100039020.0029378615-0.0187206920.03702281-0.0286094860.003984295-0.0176219360.0196520160.021106560.00128645890.0054362207-0.0147651730.0183230470.019965947-0.0073878190.0222053140.0221634560.0160418230.0262236190.0274165510.018971834-0.019746196-0.018281190.0094388280.0087481820.009062112-0.01652318-0.011050335-0.014430315-0.013153667-0.00796335750.0082720550.014974460.021661168-0.0084342520.025302757-0.04453620.02007059-0.0049287006-0.00353171230.006163492-0.0048632985-0.00258338220.0286932010.0100248310.036771666-0.020761237-0.033757936-0.0109142990.0213367750.0147756380.031581357-0.028295556-0.0178626160.0186265130.018647442-0.0237331070.0006020261-0.009062112-0.043238620.05726083-0.0055984180.012149090.0342811530.024402823-0.016324360.0194845870.008088930.0235028920.00486329850.014294279-0.010286439-0.021064702-0.0041883490.030597710.032565005-0.0133001660.00452844-0.0172870770.02605619-0.012766486-0.0220169560.00278612850.01523606850.027730482-0.0141373140.020007804-0.0102812070.003803785-0.02888156-0.0049443970.000238554090.0452477750.008790039-0.023461035-0.00081033180.0347206560.0278560540.0101347060.015068639-0.008099394-0.00507258530.0027965930.0389063880.00592804470.019285765-0.0190032270.02164024-0.010150403-0.00555656060.0702366-0.0264329030.00299803120.0079267320.0065140473-0.0164708590.0178312230.040057465-0.004460422-0.0111968360.034574155-0.0227076020.0053158810.0085598240.022875031-0.0240051780.007387819-0.013143202-0.010385850.055795822-0.013258309-0.024800468-0.0123165190.0044578060.026746834-0.0297605620.0171091840.015434891-0.0038508745-0.0087324860.00045519846-0.00586002650.011291015-0.023042460.0078901070.02161931-0.00369129330.0068018160.0188985840.00280444110.0079790540.0441594830.014786102-0.04482920.004102018-0.0296559180.0105218870.015403498-0.03398815-0.021661168-0.012170019-0.005389131 \ No newline at end of file +[0.0055774893000000002,0.012839735999999999,0.096690429999999994,-0.0014663147000000001,0.058432831999999997,0.0024996674999999999,0.026286404999999999,-0.021661168000000001,-0.014503565,-0.016617360000000001,0.033360290000000001,0.010537583,-0.021870455,-0.040287679999999999,-0.0034584619999999998,0.018249797000000002,0.035097370000000003,0.040999255999999998,0.020300806000000001,0.040036536999999997,0.040392324,-0.0048554501999999999,-0.006100706,-0.0029849507999999999,-0.029446632,-0.028065340000000001,0.0069221559999999996,0.018364904000000001,0.049559079999999998,-0.033234720000000002,-0.025114398,-0.031079069000000001,-0.025721330000000001,-0.0071942289999999999,-0.0142001,0.031853430000000002,0.019149728000000001,-0.0096010249999999991,0.0079528930000000008,-0.058097972999999997,-0.053284379999999999,-0.0048240569999999997,0.031267427,-0.0098469370000000001,0.017025469000000001,-0.022770387999999999,-0.037357670000000003,-0.034092795000000002,-0.0084499480000000005,-0.054247099999999999,0.000114371884,-0.014304743,0.012170019000000001,0.007947661,-0.025428329,0.0035107837000000002,-0.00011012074999999999,0.015005853,-0.010809655,0.030158206999999999,0.022268099999999999,0.01395942,-0.022268099999999999,-0.014273350000000001,0.092337269999999999,-0.034929941999999999,-0.023586606999999999,0.0021020228000000001,-0.019379944,0.0673903,0.0081203230000000005,0.002924781,0.025930617,-0.044243197999999997,-0.018825334999999999,-0.053409953000000003,0.033799794000000001,0.012358377,-0.014011742000000001,-0.0043374659999999999,0.0061896527000000002,-0.0074244439999999997,-0.035264799999999999,-0.010364922,-0.0011582709,0.037859954000000001,-0.043196763999999999,0.039283103999999999,0.0046697086999999997,-0.011280551,-0.011594481,-0.015434890999999999,-0.038822673000000002,0.0602327,-0.011803767999999999,-0.023795892999999999,0.0017436194,-0.0079842860000000002,-0.0054937744999999996,-0.015979037000000001,-0.059395549999999998,-0.067892590000000003,0.014398922,-0.01062653,0.049224219999999999,-0.014168706999999999,0.0015186361999999999,-0.015832536000000001,-0.065841579999999997,-0.038592458000000003,-0.089072399999999996,-0.019076478000000001,0.07371076,0.037064668000000002,0.027688623999999998,-0.01818701,0.061320989999999999,-0.0010490492999999999,-0.02637012,-0.032460360000000001,-0.001228905,0.027018907000000002,-0.0082563580000000001,-0.025365543000000001,0.049307934999999997,-0.038404099999999997,-0.032209217999999998,-0.018971834,0.0111235855,0.0056193466000000001,0.018406762,0.0051144427000000001,0.072036470000000005,-0.082124084,-0.03953425,0.03775531,0.0056716683,-0.0020601653999999998,0.0012269430000000001,0.0039921430000000001,0.0074296759999999996,-0.0019908394,0.028965272,0.081496224000000006,-0.01340481,0.039492390000000002,-0.02605619,0.047005784000000002,-0.022247171,-0.025553900000000001,-0.028839701999999998,-0.016816181999999999,0.0060902415000000003,-0.0046252350000000001,0.0021648088000000001,-0.028839701999999998,-0.0063152249999999998,-0.010914299000000001,-0.024193536000000002,-0.013101345,-0.0075290874000000004,-0.0095748640000000006,0.022058813,-0.028651343999999999,0.021305382000000001,0.033988150000000002,-0.017328935,-0.0088161999999999997,-0.054037812999999997,0.013990813,0.0016756011999999999,-0.023586606999999999,0.027835124999999999,-0.0081621799999999998,0.021556525,-0.014430315000000001,0.0052217019999999999,0.00084434083,0.050521799999999999,0.012368841,-0.044494341999999999,0.019055549000000001,-0.0025689937000000001,0.027542124000000001,-0.042087547000000003,0.0098103119999999999,-0.029823348,0.042254976999999999,-0.041334114999999998,0.021996027000000001,-0.017621936000000001,-0.03775531,0.0053786670000000002,0.066260150000000004,-0.0087691109999999996,0.020782164999999998,-0.0099149549999999996,-0.04173176,-0.02069845,0.027018907000000002,-0.017904473000000001,0.0076075700000000001,-0.022749459999999999,-0.037232096999999999,-0.018406762,-0.021472809999999998,-0.047675500000000003,0.015822072,0.0018770397000000001,-0.00075735606000000002,-0.027395623000000001,0.045164059999999999,0.044954773000000003,0.033381220000000003,-0.036248450000000002,0.021577453,0.024967897999999999,0.018710227999999999,-0.027960697,-0.031267427,-0.016052286999999998,0.0076075700000000001,0.022791316999999998,-0.00078286290000000004,0.0070634247000000002,0.018647442,-0.038697099999999998,-0.039638890000000003,0.049517220000000001,0.037001880000000001,0.032188290000000001,0.012379305,-0.0010451251999999999,0.0052609433999999998,0.032020859999999998,0.0119816605,-0.019421800999999999,0.0029666383999999999,-0.015926715000000001,0.081161364999999999,0.015393034,-0.044033910000000002,-0.036918167000000002,0.042003832999999997,-0.019222979000000001,-0.0031837733000000001,-0.034092795000000002,-0.065255579999999994,0.034050938000000003,0.020531021,0.0020954824999999998,-0.017140577000000001,0.041585259999999999,-0.013059487999999999,-0.037399527000000002,-0.064334719999999998,-0.054498244000000001,-0.0086801640000000006,0.039011030000000002,0.0009018947,-0.035641516999999998,-0.013457132,0.012400234,0.020468235000000001,0.029572204000000001,-0.0076598915999999996,-0.045582632999999997,0.013457132,-0.0035866500999999999,0.022979675000000001,0.034804370000000001,-0.047675500000000003,0.0061844205000000001,-0.00036265454000000003,-0.015863928999999999,0.021211203000000001,0.015434890999999999,0.0054100600000000004,-0.035181087,0.0098417049999999992,-0.011238692999999999,-0.022812244999999998,0.012693236,0.030555852000000001,0.0049705579999999999,0.041982904000000001,-0.014032670000000001,0.041501544000000001,-0.0050124152999999998,-0.0050385760000000003,0.0081046260000000002,0.025051612000000001,-0.033025432,0.00014388458000000001,-0.0065820655000000004,-0.012190947000000001,-0.024005177999999999,0.03513923,-0.02127399,0.026181761000000001,0.032146429999999997,0.031288356000000003,0.016523179999999998,-0.003173309,0.035243873000000002,-0.0091876839999999998,0.029237345000000001,-0.039576106,-0.022519244000000001,-0.0014179170999999999,-0.047256927999999997,0.0079319644999999994,0.02664219,0.032272004,-0.024528395000000001,0.017726579999999999,-0.02337732,-0.026265475999999999,0.020091518999999999,-0.027751410000000001,0.010767798,-0.035557802999999999,0.0092975599999999999,-0.052823950000000001,0.032188290000000001,-0.01811376,-0.012054911,0.040685326000000001,-0.035746159999999999,0.0068279769999999998,0.02994892,-0.018773014000000001,0.023419176999999999,0.051275229999999998,0.047842927,-0.030618638,0.050479940000000001,0.031246498000000001,-0.031539499999999998,-0.026098046,0.019128800000000001,0.0033276577000000002,9.7776110000000001e-5,0.057386402000000003,-0.051107800000000002,-0.010045759,-0.033653293000000001,0.062158138000000002,0.042401477999999999,0.017998653,-0.067599590000000001,0.005849562,-0.024130749999999999,-0.043322336000000003,-0.014534958000000001,0.012326983999999999,-0.0097527580000000003,-0.031267427,-0.030283779,-0.0037279184999999999,0.0085388949999999995,0.0078744109999999996,0.0069954064999999998,0.049977652999999997,0.029739633000000001,0.069399450000000001,-0.00022792624,-0.019191585000000001,-0.0070424959999999997,-0.031958072999999997,0.011992124999999999,-0.011866554,-0.069985459999999999,-0.022707602,-0.051442660000000001,0.044661770000000003,0.019652016000000001,0.039345890000000001,-0.0082197329999999999,0.018260261,0.010882905999999999,0.008068001,0.0039005799999999998,0.0059228125000000001,0.025470185999999999,-0.0094388279999999998,0.031079069000000001,-0.014262886000000001,-0.014001276999999999,-0.0065925300000000001,-0.033862580000000003,0.041564329999999997,0.0021124870000000001,0.040769039999999999,0.015079103,0.013812919,0.049768366000000001,0.0063309213000000003,0.019379944,-0.023963320999999999,-0.023586606999999999,0.05114966,-0.031099997000000001,0.024402823000000001,0.022602958999999999,-0.030848852999999999,0.015686035000000001,-0.029990777,-0.036625165000000001,-0.063706860000000004,0.0088789860000000002,-0.029572204000000001,-0.0043531624,0.018375368999999999,0.013205987000000001,-0.016565038000000001,-0.0079005710000000003,-0.013614097,0.0018953523,-0.0097789190000000005,-0.023900535000000001,-0.011228228999999999,-0.053075094000000003,-0.030744210000000001,0.020813557999999999,0.019955482,0.028546700000000001,0.030765139,-0.018092832,-0.026495689999999999,-0.047424357,-0.0061791883999999997,-0.068646020000000002,0.027688623999999998,-0.0087167890000000008,-0.024946968999999999,-0.046838353999999999,-0.068311159999999996,0.00038064009999999999,-0.032167359999999999,-0.015277926000000001,0.017119648000000001,0.012117697,0.037943669999999999,-0.0133315595,-0.024172608000000002,-0.013781526000000001,0.016104608999999999,0.019379944,0.0093969710000000005,-0.0076965169999999999,0.014608207999999999,-0.017988187999999999,0.0002962714,0.0508148,-0.021472809999999998,0.046880209999999999,-0.0053891310000000001,-0.0099934380000000003,0.01497446,0.028504842999999998,0.038843601999999998,0.0034846228,0.024528395000000001,0.0013512069000000001,-0.037797167999999999,-0.019976410999999999,-0.0039031961999999999,0.0022851487000000001,-0.058683976999999998,0.0038770353000000001,0.031644143,0.026579404000000001,-0.034783439999999999,-0.052656524000000003,-0.026453832,-0.063748719999999995,-0.060776845000000003,-0.017475436,-0.044745486000000001,0.023147104000000002,0.033339362999999997,-0.051107800000000002,0.0091876839999999998,-0.0087691109999999996,0.072873615000000003,0.028379270000000002,0.0081831079999999997,0.0041883490000000001,-0.0097109004999999995,-0.054414530000000003,0.0085127339999999992,0.035788019999999997,-0.006477422,-0.04407577,-0.032355719999999998,0.021430952,-0.050061367000000002,0.019149728000000001,0.022121598999999999,0.018846261999999999,0.029069915000000002,-0.06546486,-0.0057815439999999996,-0.026893334000000001,-0.017590543,-0.062995284999999998,-0.036332164,0.037839024999999998,0.046084920000000001,0.0053734346000000001,0.017726579999999999,-0.027353764999999999,0.026453832,0.0066553154000000003,-0.018207939999999999,-0.017684722,0.040099322999999999,-0.014189635000000001,0.0022576798000000001,-0.049224219999999999,0.027583981,-0.028797844,-0.016460394,-0.00029692542999999998,0.0020837102,-0.03273243,-0.042380550000000003,-0.0027625838000000002,-0.031602286,0.019359015,0.0102655105,0.015842999999999999,0.021514667000000001,-0.0033564345999999999,0.037587885000000001,-0.0082615899999999992,0.012515342,-0.024214465000000001,-0.014691923000000001,0.023712179,0.02557483,0.012138626,-0.012368841,0.007947661,-0.011437516,-0.0098417049999999992,-0.013268772999999999,0.020782164999999998,-0.048596359999999998,-0.032439432999999997,-0.036980952999999997,-0.0067181013999999999,0.017140577000000001,0.033716080000000002,0.030848852999999999,-0.010495726,-0.056465540000000002,-0.0074663013000000004,0.031058140000000001,-0.0060536163,-9.1154149999999999e-5,0.014670994,-0.0016010429000000001,-0.0087900389999999995,-0.015194210999999999,-0.036771666000000001,-0.021211203000000001,0.0028907719999999998,0.0018665753,-0.019515979999999999,0.030827924999999999,-0.014943067000000001,0.0014911675,0.00040876299999999998,0.030723281000000002,-0.024130749999999999,-0.016408072999999999,-0.019400872,-0.034281153000000002,0.030200063999999999,-0.029572204000000001,-0.020332198999999999,-0.016094144000000001,-0.012546735,0.024570253,-0.0024198768999999999,0.0038901157999999998,-0.025909688,-0.0017632400999999999,0.0011693891999999999,0.0048711468000000001,0.027939769,-0.015393034,0.013111809,-0.002741655,0.025281827999999999,0.014922138,-0.0090568799999999998,-0.0014833191999999999,-0.019034619999999999,-0.020928665999999999,0.014221028,-0.044033910000000002,0.023356391000000001,0.0081150900000000002,0.025763187999999999,0.041292257999999998,-0.0097736864999999999,-0.00560365,-0.020771700000000001,-0.033757936000000002,-0.0067024049999999998,-0.037818097000000002,0.028881560000000001,0.0012249809,0.026391046000000001,0.019945017999999998,0.0026553244999999999,-0.020834486999999999,0.0053028010000000002,0.026663118999999999,-0.0020183083,0.0067599590000000003,-0.039597034000000003,-0.015058175,-0.031581356999999997,-0.0099829729999999992,-0.042903761999999998,0.010893369999999999,-0.039220320000000003,0.0039319730000000001,0.044494341999999999,-0.03775531,0.014336135999999999,-0.020583343,0.021786739999999999,0.0051876930000000002,-0.023774964999999999,0.010260277999999999,0.030241922000000001,0.050438084000000001,0.016753396,-0.0039842949999999997,0.00097383699999999997,-0.014765172999999999,-0.019882232,0.026223619,-0.023188962,-0.025051612000000001,-0.053284379999999999,0.037525099999999999,-0.015403498,-0.0023427026000000001,0.022937816999999999,9.1808169999999998e-5,0.0044708860000000003,-0.049768366000000001,-0.053075094000000003,0.018657905999999998,-0.0036363555000000001,-0.0144931,-0.020070589999999999,0.0061425630000000002,-0.023398248,0.012452556,-0.016481322999999999,0.0048789949999999997,-0.025365543000000001,-0.0045075113999999998,-0.048303359999999997,0.027521194999999998,0.015989501,0.038257600000000003,-0.015905786000000002,-0.031288356000000003,0.04771736,-0.05177752,-0.0119502675,0.028169983999999999,0.0085336630000000004,-0.010349225,0.003746231,-0.012243268999999999,0.0081569480000000007,-0.017245219999999999,0.022393672,0.026160831999999998,-0.014650065,-0.0052373986999999997,0.053451810000000002,-0.045331490000000002,-0.027772339,0.010396315,0.014890745,0.043657195000000003,0.0091615240000000008,-0.014514028999999999,0.070822603999999997,-0.059228122000000001,0.0069901742999999997,0.0042302064000000004,0.015445354999999999,0.03156043,0.014451243000000001,-0.027353764999999999,-0.038843601999999998,-0.022121598999999999,0.0088946830000000005,-0.0078848749999999995,-0.022205314,-0.059646695999999999,-0.0040653935000000002,0.017517293,-0.0061791883999999997,0.0016873736,0.033988150000000002,0.0027547355000000001,-0.0119502675,0.0022393672,0.011845625,0.020541485000000002,-0.0088999149999999996,-0.038027382999999998,-0.018438155000000001,0.023105246999999999,0.016554573999999999,-0.017077789999999999,0.010563744,-0.015267462000000001,-0.0015657258000000001,0.0073041039999999996,0.0070634247000000002,0.044536199999999998,-0.01176191,-0.023168033000000001,0.027249122000000001,0.033674219999999998,0.020311269999999999,-0.011060799499999999,-0.020510092000000001,0.008962701,0.0036625163999999999,0.011458444,0.012609521,-0.042547979999999999,0.012975772999999999,0.028295555999999999,0.011730516999999999,0.059604839999999999,-0.0081307870000000004,0.0039738305,0.0018652673,0.019913625000000001,-0.016031357999999999,-0.018009117000000002,-0.016753396,0.045457060000000001,-0.024863254000000001,0.025072541,0.0043060729999999997,0.043699052000000002,-0.0064878864000000001,-0.0093289529999999992,-0.011427051000000001,-0.025512043000000002,-0.0072988719999999997,-0.015905786000000002,0.0046278513000000002,0.0069901742999999997,-0.0092400060000000003,-0.0448292,0.02461211,0.014252420999999999,0.0091039699999999994,-0.0024865870000000002,-0.0079790539999999993,-0.024695825000000001,0.019076478000000001,-0.018291654000000001,-0.028107198,-0.003618043,0.018092832,0.0012282510000000001,0.0038744193000000001,-0.017287077000000001,0.020907736999999999,-0.048973075999999997,-0.0080313759999999998,-0.064418434999999996,-0.047884784999999999,-0.038718030000000001,0.013802455,-0.012839735999999999,-0.015665106000000002,-0.0024316492000000002,-0.0028541468,-0.055419106000000003,-0.037232096999999999,0.022833174000000001,-0.022602958999999999,0.018228867999999999,-0.0066448510000000002,0.02289596,0.027939769,0.055502820000000001,-0.024402823000000001,-0.009475453,-0.0038796514000000002,0.03369515,0.022707602,0.002922165,0.01608368,-0.00045389041999999998,-0.0035186319999999998,0.0016180475,0.02492604,-0.019714802999999999,-0.039408675999999997,-0.0071366750000000003,0.00049738283000000003,-0.02743748,-0.038425029999999999,0.0099097230000000005,-0.01444078,0.0077017489999999999,-0.040727183,-0.0019489819999999999,0.021336774999999999,-0.022121598999999999,0.045205916999999998,-0.022289027999999999,0.023272675999999999,-0.024905112,0.017716115000000001,-0.00046468177,0.021493739000000001,0.0127037,-0.012892058,0.017831223,0.026830547999999999,-0.0074924620000000001,0.015947644,-0.013488524999999999,0.0046330835000000003,-0.044494341999999999,-0.021619309999999999,0.0097946149999999996,-0.009475453,0.018919512999999999,0.0023924079999999999,0.020876344000000002,-0.021326311000000001,-0.010757334,-0.0035421767,-0.019683409999999998,0.013049023,-0.0078744109999999996,-0.041543400000000001,-0.0036520519999999998,-0.022477387000000001,0.0085964490000000008,0.032272004,0.0044970469999999997,0.014336135999999999,-0.013488524999999999,-0.015194210999999999,-0.021682097000000001,0.028672271999999999,-0.016816181999999999,0.013383881,0.026453832,0.012306055,-0.013509452999999999,0.0019345935,-0.011751446,0.0019908394,-0.01277695,-0.0085441270000000003,-0.020886808999999999,-0.0095487030000000004,0.0041020179999999998,0.0041177147000000004,0.014639601,-0.0054100600000000004,-0.025030684000000001,0.017580078999999998,0.030723281000000002,0.00070895853999999999,-0.013603631999999999,-0.00029512686999999999,-0.016690610000000002,0.024235394,-0.025386471000000001,-0.019474122999999999,0.040559754000000003,-0.018260261,-0.0011170675,-0.0029378615000000001,0.024842326000000001,-0.024298179999999999,0.021127488,0.015508141,-0.016533645,-0.065130010000000002,-0.019871768000000001,0.023126175999999998,0.020583343,-0.010872441,-0.023356391000000001,0.030660494999999999,0.0079110359999999998,0.0050071830000000001,-0.0091772199999999998,-0.0094022029999999996,-0.02272853,-0.020970523000000001,0.012044447,0.0021844294,-0.0068018159999999996,-0.023900535000000001,0.017318469999999999,0.0029117005999999999,0.0061739560000000004,-0.011102657,0.015717427999999999,0.024967897999999999,-0.021284454000000001,0.0790685,0.0051013622,-0.0037619276000000002,-0.049768366000000001,-0.026579404000000001,0.013519918000000001,0.01490121,0.026872406000000001,-0.027793267999999999,0.0061216344999999997,0.040329539999999997,0.02093913,-0.019034619999999999,0.0078587139999999993,0.025846902000000001,0.020646128999999999,0.0069326203999999997,0.019338086000000001,0.0045022789999999997,0.017747507999999999,-0.0284002,0.015298855,-0.016805718000000001,-0.0023230820000000002,0.0045336721999999999,-0.013697811000000001,0.0073198005999999998,-0.0051275230000000002,-0.020520556999999998,-0.010657923,-0.0022210545999999999,-0.037525099999999999,-0.027688623999999998,0.059479266000000003,0.0038770353000000001,0.027018907000000002,0.016899897000000001,0.020342663,0.011887482,0.019275299999999999,0.0020091520000000002,-0.021577453,-0.0044054840000000003,0.0091353630000000005,0.014545422000000001,-0.028190913000000001,-0.01294438,-0.035432230000000002,0.0035683375000000002,-0.0017671642,-0.048512645,-0.073585189999999995,0.0082720550000000004,0.050647369999999997,0.010129473999999999,-0.030011705999999999,-0.057177115000000001,-0.0042171259999999999,-0.019118335,-0.021420488000000001,-0.011291015,0.014116385,-0.0031052906999999999,-0.0096533469999999996,-0.049266077999999998,-0.011238692999999999,0.010333528999999999,-0.0055879535999999999,0.0065506725000000002,-0.0048999237000000003,0.0092609349999999997,0.011793303,0.011667731000000001,-0.0048240569999999997,0.03637402,-0.0043243854000000002,0.012243268999999999,0.028358342000000002,0.0039947589999999996,-0.017799829999999999,0.021399560000000001,-0.0041778846999999996,-0.034050938000000003,0.022100669999999999,0.039304033000000002,-0.0064564933999999999,-0.017757973,0.020708915000000001,-0.019254371999999999,0.030095420000000001,-0.023523820000000001,0.0013433587,0.02306339,0.00044538816999999998,-0.023021532000000001,-0.012023518,-0.004128179,0.025198113000000001,-0.0050673530000000001,0.041899190000000003,0.014388458,0.0031366837000000002,-0.0072308537000000004,-0.023230819,-0.0085859850000000008,-0.018061438999999999,0.021786739999999999,0.031916215999999997,-0.014482636,0.02272853,-0.016575501999999999,-0.016240645000000001,-0.0206252,-0.051735662000000002,0.029572204000000001,0.0048737629999999997,-0.016251109999999999,-0.0067651910000000003,-0.014273350000000001,-0.024130749999999999,0.0079424290000000008,0.010725940999999999,-0.053619239999999999,0.013090881,0.010616066,0.030158206999999999,0.014890745,-0.0059280447000000002,0.016899897000000001,0.0046775569999999997,-0.015497677,-0.040622539999999999,-0.025365543000000001,-0.0039764466,0.02385868,-0.021117024000000002,0.023733107,0.0318325,-0.0095539349999999995,-0.011468908999999999,0.021807668999999998,-0.030639566,-0.0025336766000000001,-0.019526444,0.0072831754000000004,0.0028489145999999999,-0.014555886000000001,-0.022247171,0.020541485000000002,-0.018061438999999999,-0.0021373400000000002,-0.012619985,0.014953531000000001,0.013310631,0.012829271999999999,0.024109822,0.00041301412000000002,0.012410698,0.045875634999999998,0.0073930510000000003,0.0058024725999999997,0.012044447,0.017328935,-0.01222234,-0.0081255549999999996,-0.013760596999999999,0.0049522454000000002,-0.0056402752999999998,0.0016206636,-0.0053629703000000004,-0.0051118266000000001,-0.0079058040000000007,-0.016722002999999999,0.0014009125,-0.02695612,-0.011887482,0.016983611999999999,-0.00086134540000000004,0.014566351,0.0041150986000000004,0.014221028,0.00075604800000000005,0.0046723247999999997,-0.037692523999999998,-0.018050974000000001,-0.024988825999999999,-0.0013943722999999999,0.015549999,-0.0032151662999999999,-0.0017436194,-0.0026775610000000002,-0.015999965000000001,0.0011144513999999999,0.00058632967000000005,0.018867189999999999,0.00085807530000000002,-0.014775638000000001,-0.020468235000000001,0.0024630423,0.035704303999999999,-0.0021687329999999999,-0.026328262000000002,-0.023021532000000001,-0.043657195000000003,-0.0097946149999999996,0.0058234013000000003,-0.0022511395999999999,0.022749459999999999,0.015874393000000001,0.011144513999999999,0.0080470720000000006,0.032481290000000003,0.028442057,0.0089260759999999998,0.029048986999999998,-0.00047874323000000001,-0.016763859999999998,-0.0034244529,0.030074492000000001,0.032585933999999997,0.019139264,-0.026704975999999998,-0.025449256999999999,0.010040527,0.0030765138000000002,-0.0087691109999999996,0.018323046999999999,-0.0094126680000000008,0.044871059999999997,-0.027060764000000001,0.020374056000000001,0.016805718000000001,-0.0147128515,-0.011740982000000001,-0.034134652000000001,-0.027186335999999998,0.031895287000000001,-0.020541485000000002,-0.016732467000000001,0.039638890000000003,-0.034406724999999999,0.013551311,0.0096114890000000008,0.011887482,-0.021054237999999999,-0.011437516,-0.032481290000000003,-0.025219042000000001,-0.0067756552999999999,-0.012923451000000001,-0.0024918191999999998,0.040224894999999997,0.0036337394000000001,-0.041627116999999998,-0.032983575000000001,0.032585933999999997,-0.0052217019999999999,-0.0070163350000000003,-0.015026782000000001,0.0016062750999999999,0.027270050000000001,0.0017475436,-0.0026030027999999999,-0.013812919,0.0073616580000000001,-0.0078796430000000004,-0.034490439999999997,-0.0070372639999999997,0.011604945,0.035013656999999997,0.023754035999999999,0.02980242,-0.029530345999999999,0.019568301999999999,-0.0016585967000000001,0.0099254189999999996,-0.0090568799999999998,-0.017716115000000001,0.013467596,-0.015372105,-0.033444005999999998,-0.022749459999999999,0.008711557,0.010542816,-0.011050335,-0.0111235855,0.017548686000000001,0.0024054884,-0.037776240000000003,-0.010333528999999999,0.033527719999999997,-0.016648752999999999,-0.0082406619999999993,-0.011866554,0.046629068000000003,0.036813524,0.00039241248,-0.058767689999999997,0.0133943455,0.00016988189999999999,0.021242595999999999,0.00044538816999999998,-0.011563087999999999,-0.020750772000000001,-0.0035709536000000002,-0.0065716009999999998,-0.022833174000000001,0.029886134000000002,0.065799720000000006,-0.0042485190000000001,0.012347912000000001,0.021598381999999999,0.043531622999999998,-0.013289702,0.063162713999999995,0.016533645,0.023440105999999999,0.019965947000000001,0.024863254000000001,0.0020824021000000002,0.00097906920000000001,-0.017632399999999999,0.01277695,-0.0102027245,0.023733107,-0.0039476696000000002,-0.0093969710000000005,-0.0056769005000000001,0.0072203900000000001,0.015330247999999999,-0.022540173,0.027939769,-0.020238019999999999,0.0137919905,-0.021012380000000001,-0.035264799999999999,0.0072256215000000004,0.011918875000000001,-0.0044944310000000001,0.0073721222999999997,0.026600333,0.016345287,0.027521194999999998,-0.00085415114999999998,0.014566351,0.02760491,0.0015513373,0.0012674922999999999,0.027897911000000001,-0.0056245790000000002,-0.018741620000000001,0.026349190000000001,0.0053943632999999998,-0.045164059999999999,0.0066814763000000001,0.020101983,0.013969884,0.022937816999999999,-0.032606862,-0.027981625999999999,0.073752620000000005,-0.001194896,0.00058142450000000004,0.0069744780000000001,-0.0069587813000000004,-0.019066013,-0.020091518999999999,0.04407577,0.0032988808,-0.041103899999999999,0.019610160000000001,0.014995389,0.0010529733999999999,0.018929977000000001,0.013875705,0.0072099254,-0.024591181,0.0048711468000000001,-0.0048606824,0.042464264000000002,-0.0076232664000000002,-0.012599057,0.0062733674000000003,0.0063780110000000003,-0.0029535579999999998,0.016387143999999999,-0.0020627814999999998,-0.048847504,-0.0046226189999999997,-0.01347806,0.00027468872999999999,0.019369480000000001,-0.019212514,0.0023322382,-0.01277695,-0.00090647280000000002,0.027290979999999999,-0.024026107000000001,-0.023440105999999999,0.0015147120999999999,0.0094388279999999998,0.042757259999999998,-0.012002588999999999,-0.0035604893000000001,0.037232096999999999,5.0686612999999999e-5,-0.017412649999999998,0.00020568955,0.012672307000000001,-0.011604945,-0.014231493,-0.040706254999999997,-0.030095420000000001,-0.012818807999999999,-0.021744883,0.0097527580000000003,0.051191515999999999,-0.0056716683,-0.066469440000000005,-0.020227555000000001,-0.010809655,0.0025559133,-0.045415204000000001,0.00028220998,-0.00013031364,-0.0019254372,-0.0020431607999999999,0.03733674,0.011918875000000001,-0.012557199,-0.0012583360000000001,-0.0040026074000000002,0.0048162090000000001,0.0066343866999999997,0.036729810000000002,0.040141179999999999,0.019212514,-0.017894009999999998,-0.021451880999999999,0.010323064,-0.044871059999999997,-0.003432301,-0.016272037999999999,-0.0087795749999999995,-0.0072256215000000004,0.014116385,-0.010003902,0.0029378615000000001,-0.018720692000000001,0.037022810000000003,-0.028609486,0.0039842949999999997,-0.017621936000000001,0.019652016000000001,0.02110656,0.0012864589,0.0054362207000000001,-0.014765172999999999,0.018323046999999999,0.019965947000000001,-0.0073878190000000003,0.022205314,0.022163456000000002,0.016041823,0.026223619,0.027416551000000001,0.018971834,-0.019746196000000001,-0.018281189999999999,0.0094388279999999998,0.0087481820000000002,0.0090621120000000006,-0.016523179999999998,-0.011050335,-0.014430315000000001,-0.013153667000000001,-0.0079633575000000005,0.0082720550000000004,0.01497446,0.021661168000000001,-0.0084342519999999997,0.025302756999999999,-0.044536199999999998,0.020070589999999999,-0.0049287005999999996,-0.0035317122999999999,0.0061634919999999996,-0.0048632985,-0.0025833822,0.028693201000000002,0.010024831,0.036771666000000001,-0.020761236999999998,-0.033757936000000002,-0.010914299000000001,0.021336774999999999,0.014775638000000001,0.031581356999999997,-0.028295555999999999,-0.017862616000000001,0.018626513000000001,0.018647442,-0.023733107,0.00060202610000000001,-0.0090621120000000006,-0.043238619999999998,0.057260829999999999,-0.005598418,0.01214909,0.034281153000000002,0.024402823000000001,-0.01632436,0.019484587000000001,0.0080889299999999994,0.023502892000000001,0.0048632985,0.014294279,-0.010286439,-0.021064702000000001,-0.0041883490000000001,0.03059771,0.032565005000000001,-0.013300166,0.0045284399999999999,-0.017287077000000001,0.02605619,-0.012766486000000001,-0.022016956000000001,0.0027861284999999999,0.0152360685,0.027730482000000001,-0.014137314,0.020007804000000001,-0.010281207000000001,0.0038037850000000001,-0.028881560000000001,-0.0049443969999999997,0.00023855409,0.045247774999999997,0.0087900389999999995,-0.023461035000000002,-0.00081033180000000002,0.034720656000000003,0.027856054000000002,0.010134706,0.015068639,-0.0080993939999999993,-0.0050725852999999998,0.002796593,0.038906388,0.0059280447000000002,0.019285765,-0.019003227000000001,0.021640240000000002,-0.010150403000000001,-0.0055565605999999997,0.070236599999999996,-0.026432903000000001,0.0029980312,0.0079267320000000006,0.0065140472999999999,-0.016470859000000001,0.017831223,0.040057465,-0.0044604220000000003,-0.011196836,0.034574155000000002,-0.022707602,0.0053158809999999997,0.0085598240000000006,0.022875031000000001,-0.024005177999999999,0.0073878190000000003,-0.013143202,-0.01038585,0.055795822000000002,-0.013258308999999999,-0.024800467999999999,-0.012316519,0.0044578059999999999,0.026746834000000001,-0.029760562000000001,0.017109184,0.015434890999999999,-0.0038508745,-0.0087324859999999994,0.00045519845999999998,-0.0058600264999999997,0.011291015,-0.023042460000000001,0.0078901070000000004,0.021619309999999999,-0.0036912933000000001,0.0068018159999999996,0.018898584,0.0028044411,0.0079790539999999993,0.044159482999999999,0.014786102000000001,-0.0448292,0.0041020179999999998,-0.029655918,0.010521887000000001,0.015403498,-0.033988150000000002,-0.021661168000000001,-0.012170019000000001,-0.0053891310000000001] \ No newline at end of file From 1978c36294a5b465e514f86c10f81663d8e0a601 Mon Sep 17 00:00:00 2001 From: Michael Hughes Date: Mon, 18 Mar 2024 23:45:54 +0000 Subject: [PATCH 11/21] first RAG based response --- mod/xaichat/view.php | 19 +++++++-- search/engine/solr/classes/engine.php | 3 +- search/engine/solrrag/classes/ai/aiclient.php | 2 +- search/engine/solrrag/classes/engine.php | 39 ++++++++++++------- search/engine/solrrag/classes/schema.php | 1 + 5 files changed, 44 insertions(+), 20 deletions(-) diff --git a/mod/xaichat/view.php b/mod/xaichat/view.php index cf44d40d0a9c..b519113eb513 100644 --- a/mod/xaichat/view.php +++ b/mod/xaichat/view.php @@ -92,9 +92,7 @@ $progress->update(1, $totalsteps,'Processing System Prompts'); } $progress->update(1, $totalsteps,'Looking for relevant context'); -// $vector = $aiclient->embed_query($data->userprompt); - $vector = [-0.027786661000000001,0.0090511510000000003,-0.044274903999999997,-0.014116816000000001,-0.027637671999999999,-0.026917553,-0.020126086000000001,0.072856180000000006,-0.018462362,-0.028258463000000001,-0.0012904718999999999,0.022807906999999999,0.0097464390000000008,-0.0071825663999999997,-0.013272539,-0.043306466000000002,-6.3679705e-5,0.00088850889999999997,-0.046609079999999997,0.053785439999999997,-0.0091194380000000005,-0.0021650129999999999,-0.013061468999999999,-0.041270266999999999,0.010646587000000001,0.042685675999999999,-0.013620182,0.016376499999999999,0.039755534000000002,0.041295100000000001,0.0019896391000000001,-0.063320810000000005,0.0015030932999999999,-0.061185284999999999,0.013483607999999999,-0.041568250000000001,0.0011445857000000001,-0.061880574000000001,0.012353766,0.022311273999999999,-0.023714265000000002,-0.0024816170000000002,0.047105714999999999,0.023143134999999999,-0.016500656999999998,-0.027513513,-0.0022922754,-0.012788320000000001,-0.0023962580000000001,-0.0092001410000000002,-0.017034540000000001,0.020970362999999999,0.0086290120000000001,0.018400282,-0.0040817090000000002,0.01021824,0.0033367584000000001,0.009131854,-0.0040910210000000002,0.0036564662999999998,0.001761498,0.0088152500000000002,-0.027215533,-0.089940383999999998,-0.038762268000000002,0.022820323999999999,-0.084676064999999995,0.0099450920000000009,-0.029947017999999999,0.027836324999999999,0.017940896000000001,0.0044790159999999997,0.016339251999999999,0.032008050000000003,0.023230046000000001,-0.0093987949999999997,0.016997293,-0.017940896000000001,0.014514123,0.039234072000000002,-0.010187201,-0.0056398977000000003,-0.0148990145,-0.046435259999999999,-0.0056709372000000001,0.0055716102999999999,0.013669845999999999,-0.028183969,-0.037446189999999997,-0.026768561,0.026867888999999999,-0.040425990000000002,-0.023602521000000001,0.024359887,0.016897965000000001,-0.054629717000000001,0.0031970800000000001,0.021255927000000001,0.048322469999999999,0.013433944,-0.0063879521999999998,-0.058155819999999997,-0.023453531999999999,0.019778442,0.012540002999999999,0.041841400000000001,0.031536248000000003,0.028010146999999999,-0.054331740000000003,0.035558979999999997,-0.048272807000000001,-0.0078468140000000006,-0.0033957337,-0.010255488,-0.022869987000000001,0.045963459999999998,0.0138933305,-0.0084303590000000001,0.02984769,0.017369768000000001,0.039383059999999998,-0.005764056,0.016438580000000001,-0.046956724999999998,0.033075809999999997,0.0016652752,0.028928919000000001,0.0010925943,-0.026048442000000002,-0.031039612000000001,0.0114287855,0.013483607999999999,-0.025216580999999998,0.021889135000000001,-0.037595179999999999,-0.019654283000000002,-0.014501707000000001,-0.013421529,-0.032504680000000001,0.017071787000000001,-0.064810709999999994,0.0017537382,-0.058304809999999999,-0.0014231662000000001,-0.027910819999999999,0.0062265460000000003,-0.00046249022000000001,0.016724143,-0.018251291999999999,-0.0136946775,-0.025551809000000002,-0.035608639999999997,-0.048819102000000003,0.021802223999999999,-0.078021170000000001,0.0022814115999999999,0.023950164999999999,-0.021255927000000001,-0.013942994,0.06351946,-0.048918429999999999,0.010801786000000001,-0.024993095999999999,0.0083248239999999998,0.0013168555,0.012502756,0.031958385999999998,0.0066921404000000002,-0.027861156000000002,-0.036055613,0.004379689,0.010776954,-0.023950164999999999,-0.022447848999999999,-0.048843934999999998,0.031412086999999998,-0.019592202999999999,0.0013766068999999999,0.035211336000000003,0.011006647,0.012285478000000001,0.0045938626999999996,0.027414186,-0.038091812000000003,0.051053955999999998,-0.010702458,0.013011806000000001,-0.067095230000000006,-0.041692406000000001,-0.035236169999999997,0.014787272000000001,0.035112009999999999,-0.013645014,-0.024732362000000001,-0.028680601999999999,0.025775295,-0.00060760044,0.019294222999999999,0.030319492999999999,0.0049942737000000003,-0.0044107290000000004,0.017096618000000001,-0.00083962149999999998,0.018598936,-0.031511415000000001,-0.02674373,-0.025067592,0.012155111999999999,-0.0054443479999999999,-0.028481949999999999,0.057659183000000003,0.0069280415999999996,0.011012855,-0.025166918,-0.011484656500000001,0.022062957000000001,-0.033795930000000002,0.068982440000000006,-0.016364085,0.048595619999999999,-0.028233632000000002,0.081398280000000003,0.012751073,-0.040972290000000001,-0.0039575505000000004,0.02291965,0.0042306990000000001,-0.041220605,-0.027439017,0.01542048,0.0006921834,0.016910382000000002,-0.024732362000000001,-0.033100642,-0.016463410000000001,-0.0070149525999999999,-0.040525316999999998,-0.017071787000000001,0.018139550000000001,-0.017742243000000001,-0.022770660000000002,-0.038191139999999998,-0.013868498999999999,0.012825568000000001,0.0084427740000000001,-0.010497596999999999,0.0013890227,-0.0070646160000000001,0.031362425999999999,-0.031337592999999997,0.035236169999999997,-0.0023947059999999999,0.021106936,0.019915016000000001,0.022969313000000002,0.025874620000000001,0.026917553,-0.040699140000000002,0.040972290000000001,-0.019964678,-0.032703336,-0.033795930000000002,0.017692579,-0.054331740000000003,-0.040574983000000002,-0.0091132299999999999,-0.016860718,-0.011503279999999999,-0.012428260999999999,0.064314074999999998,-0.032703336,0.021442164,-0.0040475655999999997,0.035658307,-0.057559859999999997,0.074594400000000005,-0.016041270999999999,0.022112619999999999,-0.012726240999999999,-0.04248702,-0.0079647650000000004,0.023403868000000001,-0.017332520000000001,-0.012527587999999999,-0.015308736999999999,0.011497073,0.070770316,0.0032995108000000001,0.0061892989999999997,-0.00035889552000000001,0.028630938000000002,-0.048943263000000001,0.023465946000000001,0.030220167999999999,-0.034367059999999998,-0.026247097,0.0021060377000000001,0.017034540000000001,-0.022447848999999999,-0.030145670999999999,0.055126349999999998,0.036105279999999997,0.010752121999999999,0.030319492999999999,-0.0055405706999999997,0.0056647295,-0.032281197999999997,0.0025530079999999998,0.021578738,-0.02124351,-0.054530393000000003,0.038265634,-0.035012685000000002,0.0023605625999999998,0.086811589999999994,-0.0062731057,-0.0061365315000000004,-0.0084179430000000006,0.019877767000000001,-0.035856961999999999,0.016513073999999999,-0.041915894000000002,-0.010007171,0.044771537,-0.024210898000000002,-0.045243338000000001,0.021678065999999999,-0.011801260500000001,-0.069379739999999995,-0.010789369,0.024111569999999999,-0.06287384,-0.0070832394999999996,0.046162113999999997,0.0070335763000000003,-0.033572446999999998,-0.0025499042,-0.051202945,-0.00016218198,-0.0058602790000000004,-0.012887647,0.006400368,0.019964678,-0.001118202,-0.061483264000000003,0.027612839,0.059546391999999997,-0.039556883000000001,-0.044101080000000001,0.048620447999999997,0.019095569999999999,0.009318092,0.014303054000000001,-0.021988460000000001,0.0292269,-0.028953751999999999,0.027339690999999999,0.049315735999999999,-0.026942383,0.027662503000000001,-0.071167624999999998,0.031387255000000003,-0.038091812000000003,0.031461753000000002,-0.025253828999999998,-0.018412698000000002,-0.0069466652000000004,-0.0095167450000000001,0.0036005949999999998,-0.012260647,0.023453531999999999,0.057410865999999998,-0.013794004,0.055374670000000001,0.01556947,-0.00093739629999999995,-0.0081261700000000003,-0.023192799,-0.03866294,0.027588008000000001,-0.023639767999999999,-0.029475215999999999,0.0095415770000000007,-0.041468921999999998,-0.030046346000000002,-0.0028649562000000002,-0.032330860000000003,-0.0049601300000000001,-0.052593517999999999,-0.023379036999999998,-0.00059091660000000002,-0.034093909999999998,0.014315469000000001,-0.0088649130000000003,0.0031815602,-0.029003412999999999,-0.082093570000000005,0.055672649999999997,-0.0099575080000000003,0.019455627999999999,0.044721875000000001,0.0017521861,-0.022720996,0.033199970000000002,-0.05408342,0.010013378999999999,-0.055076689999999998,0.04966338,-0.0080206359999999994,0.028134304999999998,0.0063103530000000003,-0.012242022999999999,0.026545078,0.003845808,0.0039823823999999997,-0.0095229530000000007,-0.0056430018000000002,-0.015855035,-0.0057609519999999997,0.043231970000000002,0.025725630999999999,-0.041742069999999999,0.024210898000000002,0.04055015,-0.0022286442000000002,0.0026942384,0.045739975000000002,0.036328763,0.030096008,0.033994585000000001,0.015110083999999999,-0.025949117000000001,-0.029475215999999999,0.030716800999999998,-0.011751598,-0.029798029,-0.010243072000000001,-0.0026880304999999998,-0.031983215000000002,0.021442164,-0.017866401000000001,0.053835105000000001,0.020858620000000001,-0.04966338,-0.088102840000000002,0.026470581,0.033448286000000001,0.010491389,0.040128009999999999,-0.020933113999999999,-0.024471631000000001,-0.022820323999999999,-0.0037092335999999999,-0.012130281,0.053040490000000003,0.0094050030000000003,0.030989948999999999,0.053338469999999999,-0.0049849619999999999,-0.042313200000000002,-0.056864570000000003,-0.0141044,-0.011155636999999999,-0.015743291,0.020746877,-0.034689869999999998,0.018474778000000001,0.024782025999999999,-0.070968970000000006,-0.039979022000000003,-0.0028261568000000001,0.00065571179999999996,-0.0036626741999999999,0.025502145,-0.029673868999999999,0.060887302999999997,-0.0094360420000000004,-0.020088836999999998,0.036403257000000001,-0.0090201115000000005,-0.017655331999999999,0.025266245,0.015693627000000002,-0.015345984,-0.019952264000000001,-0.029971850000000001,0.0090821909999999999,-0.016438580000000001,-0.015668795999999999,-0.00081866980000000001,0.016537904999999999,0.0099326759999999997,0.01601644,-0.012688993500000001,-0.012912478999999999,-0.0056833530000000004,0.029450384999999999,-0.061234946999999998,0.011906795,-0.010317567,0.016773807000000002,0.015544638,-0.012862815499999999,0.037768999999999997,-0.047503023999999998,0.0041065407000000003,-0.011074934,0.0097836859999999998,-0.0054195164999999998,0.06595297,0.00099481960000000007,0.042611179999999999,-0.045293002999999998,-0.00044774642000000001,0.0090635669999999998,-0.037520683999999999,0.0063444966999999996,-0.018785172999999999,-0.00020447344,-0.035782464,0.021044858,0.0014805895,0.032728170000000001,0.016848301999999999,-0.010447933,0.020846203000000001,0.026843058,0.014588617999999999,-0.011211508,-0.0072073980000000003,0.045566152999999998,0.044746703999999998,0.0075798732999999997,-0.02011367,-0.029723532,0.017556004,0.0050253132000000001,0.065754320000000005,0.00530467,-0.019815689000000001,-0.03203288,-0.0012268406999999999,-0.022323688000000001,-0.0048608035000000001,-0.032827492999999999,0.040103180000000002,-0.027165870000000002,-0.0057392245999999996,0.057013559999999998,0.0094546660000000005,0.020895866999999999,0.011739181,0.043927260000000003,-0.024335057,-0.024868937000000001,-0.053338469999999999,-0.034044247,-0.0079771800000000004,-0.00023337909,0.0064810709999999997,0.0060868681999999997,0.0056740413,-0.032827492999999999,0.0043610656000000001,-0.011137012999999999,0.040773634000000003,0.028829591000000002,-0.0048359715999999999,-0.0034888523999999999,-0.024993095999999999,0.015631549000000002,0.031312760000000002,0.013272539,-0.062824174999999996,0.046956724999999998,0.0018794485000000001,0.025253828999999998,-0.0089890714999999993,-0.023043807999999999,0.065903305999999995,0.011981291,0.016972460000000002,-0.027364521999999999,-0.032504680000000001,-0.0097153989999999996,0.0049352985999999996,-0.019244560000000001,-0.0045907585999999997,-0.040922627000000003,0.041915894000000002,-0.024744779000000001,0.033522780000000002,-0.0058602790000000004,-0.0064189920000000001,-0.034242901999999999,0.016239925999999998,0.024881354000000001,0.025502145,0.0094360420000000004,-0.022584422,0.03143692,0.0081075469999999997,-0.0030775775999999999,-0.00019118461999999999,-0.01903349,-0.042611179999999999,0.0077102400000000001,-0.069330080000000002,0.035683140000000002,-0.026718898000000001,0.0069839129999999996,-0.0022022604999999999,-0.034143575000000002,0.0041034365999999996,-0.02542765,0.0042648427000000003,-0.028556444,0.0013494472,-0.024918599999999999,-0.0038240803000000002,-0.0059565017000000001,-0.015743291,-0.049290907000000002,0.021988460000000001,-0.013632597999999999,0.033994585000000001,-0.035434822999999997,0.059149085999999997,-0.0028416766000000002,0.018946579000000002,-0.011776429,-0.0068535465,-0.036552247000000003,-0.010267904,0.018176798000000001,-0.022671332999999998,-0.0051215360000000003,0.018462362,-0.011484656500000001,-0.0084055269999999994,0.0044666002999999999,0.057311542,-0.011174261,0.0035757634000000002,0.021752560000000001,-0.020523392000000001,0.015494974,0.0041375802999999997,0.009299468,0.0055964420000000001,-0.028159136000000001,-0.011621230999999999,0.0020749980000000002,-0.041617910000000001,6.7996150000000001e-5,0.025291075999999999,-0.020262658999999999,0.015271489500000001,0.0075612496999999997,0.02982286,-0.0021588050000000002,0.037992484999999999,0.023341787999999999,0.01873551,0.008945616,0.041841400000000001,0.027712166,0.0093677550000000002,-0.0031365528999999998,-0.035037513999999999,0.012924895,0.030393988,0.0041406843999999996,-0.031958385999999998,-0.00031932,-0.0055126350000000001,-0.0024241938000000001,-0.016662065,0.0067418039999999997,0.034044247,-0.0124034295,-0.039879695,-0.0053605409999999999,-0.021255927000000001,0.014663113,-0.018139550000000001,0.005847863,-0.044672209999999997,0.0041096450000000003,0.0013277193999999999,-0.034218070000000003,0.035782464,0.033771097999999999,0.033175137,-0.010826617,0.0020346467,0.015445311999999999,-0.0035354117999999999,-0.011074934,-0.036279100000000002,0.015134915000000001,-0.021926383000000001,0.023130719000000001,-0.010336191,0.033175137,-0.032057714000000001,0.019517709000000001,-0.0085669330000000005,0.020200579999999999,-0.0010111154000000001,0.010193408500000001,-0.011447409,0.0048235557,-0.023316956999999999,0.0010491389999999999,0.033969751999999999,0.017742243000000001,0.015681212999999999,0.022336104999999998,0.012316518,-0.0022814115999999999,0.012167528,-0.032877160000000002,0.0075364179999999996,0.010174785,-0.0044479766000000004,-0.0292269,-0.024049493000000002,-0.016587568,-0.0073253483999999999,0.040748804999999999,-0.00400411,0.0037309613999999998,0.013818835999999999,0.014886597999999999,0.0060868681999999997,-0.022820323999999999,0.017046954,-0.012496548,-0.0065493584000000001,-0.010547261,0.015544638,-0.0030418820000000001,-0.0011647615,0.013545687000000001,0.02540282,0.023403868000000001,0.0014107504,-0.0014565337999999999,-0.028084641,-0.036204603000000002,0.0086165960000000007,0.0036130110000000002,-0.0051743034,-0.021094522000000001,-0.010516220999999999,-0.015395648,-0.054778710000000001,-0.013433944,0.011975082999999999,-0.0083062009999999992,0.0028168450000000001,-0.021144183,-0.0035012683000000002,0.029351057,0.0060527246,-0.00080470194000000004,-0.011832301,-0.013371865,-0.015879865999999999,0.034069080000000002,-0.018487192999999999,0.017704995000000001,0.03116377,0.027439017,0.036229434999999997,0.027935651999999998,0.019182479999999998,0.0022705477000000002,0.024161234,0.020697213999999999,-0.025005512000000001,0.012502756,-0.027215533,-0.018648600000000001,0.049812370000000002,-0.0078716454999999998,-0.023863254,-0.034789197000000001,0.02704171,-0.0069466652000000004,-0.022646501999999999,0.0011391537,-0.059298075999999998,0.024347473000000001,0.039234072000000002,-0.014700361,-0.042909160000000002,-0.0035074762999999999,0.0075302099999999999,-0.0067542195000000003,0.0087097159999999993,0.031660404000000003,0.040997119999999998,0.046559419999999997,0.0082565369999999996,0.025191749999999999,-0.061830907999999997,0.010143746,-0.024335057,-0.036105279999999997,0.030691968,0.030120839999999999,-0.03143692,-0.014315469000000001,0.011776429,0.0022208841999999999,-0.0045473029999999999,0.019666698,-0.017233193000000001,-0.01072729,0.0078964770000000007,0.035757635000000003,-0.039258899999999999,0.044920526000000002,0.052295540000000001,-0.018437530000000001,0.031511415000000001,0.048918429999999999,-0.011192884,0.0092932599999999994,-0.026768561,-0.0061210114999999997,-0.0030279143000000001,0.0025607680000000002,-0.0055747143999999998,0.01822646,0.016984875999999999,-0.043430626,0.03866294,-0.0077412794000000004,0.022907235000000001,-0.018164381,-0.023602521000000001,0.0094546660000000005,-0.0041034365999999996,0.033572446999999998,0.020734460999999999,-0.028283294,0.0014542057999999999,0.013185628,0.018276122999999998,0.00029254835999999998,0.024794442999999999,0.0078840620000000007,0.0099140530000000008,-0.016239925999999998,-0.029897355,0.004181036,0.004742853,0.0072322297000000004,0.0090014880000000002,0.0022301962999999999,-0.029947017999999999,0.032604010000000003,0.027736996999999999,-0.00025219685999999997,-0.023503195000000001,-0.037048879999999999,-0.046733240000000002,0.023999829,-0.0027407978,-0.038836761999999997,-0.0076481607,-0.017481509999999999,0.0051029124000000004,-0.026818225000000001,-0.021094522000000001,0.014278222,0.012682785,-0.019654283000000002,-0.039457555999999998,-0.0060278926999999998,0.015755707000000001,-0.011379122,-0.0027609735000000001,0.0029503150999999998,-0.00075891849999999998,-0.027339690999999999,0.044796370000000002,-0.038687773000000002,-0.029996682,0.019753608999999998,-0.041518588000000002,-0.026594739999999999,0.0014673977000000001,0.0040630850000000001,0.042909160000000002,0.014116816000000001,-0.024335057,-0.036676407000000001,-0.04303332,-0.023279709999999999,0.020225410999999999,-0.017270440000000001,0.0029518672,-0.00062079227000000001,0.0030930974000000001,0.0095353690000000001,0.016984875999999999,-0.0038178724000000001,0.013421529,-0.0020765502,-0.022596838000000001,0.0026771666000000001,-0.0011507936,0.019381134000000001,0.00080082199999999997,0.028606106999999999,-0.022236780000000001,-0.015035588000000001,-0.014464459000000001,-0.022211946999999999,-0.016997293,0.0095353690000000001,0.034888524999999997,-0.010832825000000001,0.016302003999999998,-0.010180992999999999,0.0025607680000000002,0.036800563000000001,0.016935213000000001,-0.025924283999999999,0.0087904184999999992,0.021206263,0.030890622999999999,-0.020635133999999999,-0.044796370000000002,0.019145234000000001,0.034516048000000001,0.00067317159999999996,-0.022795490000000002,0.033895257999999998,0.035385158,-0.025055175999999998,-0.015470143,-0.0094608739999999993,-0.032157036999999999,-0.012713825,-0.0013432392,0.017680162999999999,0.010640379,0.036552247000000003,-0.024769612,0.017655331999999999,0.014563786,-0.0080765079999999996,0.00035617955,0.011434994,-0.010776954,-0.027339690999999999,0.0063134569999999997,0.023205215000000001,0.025998779,-0.020461312999999998,-0.043207143000000003,0.0037713130000000001,0.0099078445000000005,-0.02181464,0.0059937489999999996,-0.034590546,0.0054971150000000003,-0.009131854,-0.017357351,0.0088835370000000004,0.010615546999999999,0.017258024,0.040574983000000002,0.0040289415,-0.026445750000000001,0.033249630000000002,0.011453616999999999,-0.0092063490000000008,-0.019617036000000001,-0.026470581,0.014389964999999999,0.051004290000000001,-0.0090759824999999995,-0.0091442699999999995,0.040351495000000001,0.043604450000000003,-0.015333569,-0.014166478999999999,-0.0078033586,-0.0041344759999999998,-0.0018002974999999999,-0.049787539999999998,0.017034540000000001,0.036030779999999998,0.025452481999999998,0.0065555659999999997,0.032082542999999998,0.032877160000000002,0.018164381,-0.021578738,-0.013396697000000001,-0.024570957000000001,-0.002633711,-0.045640646999999999,0.0066114375000000001,0.025725630999999999,0.02512967,0.022373351999999999,-0.0075860815000000003,0.014601034000000001,-0.017183529999999999,0.035484485000000003,0.034118740000000002,-0.052841840000000001,-0.012136488000000001,0.034863690000000003,0.0075426260000000002,0.035310662999999999,-0.023292126,0.025824957999999999,-0.001839097,-0.0016311316000000001,-0.002408674,-0.0099885470000000004,0.053139816999999999,0.01476244,0.0076854080000000003,-0.014253389999999999,0.0046745655000000001,0.032157036999999999,0.039830033000000001,-0.001766154,-0.0096471109999999999,-0.0051339519999999998,0.011838507999999999,0.038315295999999999,0.026818225000000001,-0.013806419,0.044945359999999997,-0.00072787893999999998,-0.013545687000000001,-0.04998619,0.044995019999999997,0.0072322297000000004,-0.020275075,0.013570518,-0.029475215999999999,-0.042064882999999997,-0.002006711,-0.013843668,0.013297369999999999,-0.014414796000000001,0.0038116643999999999,-0.012297895,0.020473728,-0.016488243,0.009665735,-0.0072632692999999998,0.014340301,0.022236780000000001,-0.015730876000000001,-0.019915016000000001,-0.015010756,-0.0048328675,0.035136840000000003,0.0051991353000000002,-0.015842617999999999,-0.00064484790000000002,0.029698702,-0.017978142999999999,-0.0021029337999999998,0.029152404999999999,-0.00044076249999999999,0.019778442,-0.036055613,-0.0016776911000000001,0.014774855,-0.017816736999999999,0.0078778540000000001,-0.031635574999999999,0.018363034,-0.015805370999999999,0.021007609999999999,-0.042735339999999997,-0.0079399329999999997,0.010125122,0.016537904999999999,0.010298943,-0.014191311,-0.030145670999999999,-0.0027345898999999998,-8.9287390000000001e-5,0.016860718,-0.031635574999999999,-0.020275075,-0.030567810000000001,0.0095912399999999991,-0.0048328675,-0.0094546660000000005,0.024558541999999999,-0.0058230315,-0.072309880000000007,-0.010261696000000001,0.070422670000000007,-0.0094050030000000003,0.0068907940000000004,0.0073253483999999999,0.010832825000000001,0.0036099068999999998,0.011037686,-0.0107397055,-0.0041965559999999997,-0.022410599999999999,-0.0051215360000000003,0.00020932338999999999,-0.0036005949999999998,-0.0088524980000000003,0.019617036000000001,0.0030791296000000001,0.018810006000000001,-0.018363034,0.0076109130000000004,-0.069230749999999994,-0.031238265000000001,0.00063864,-0.00027858053,-0.016798638000000001,-0.009131854,0.0053419173000000004,0.047329202000000001,0.014961093999999999,0.0059751253999999997,0.014799687000000001,-0.015470143,0.019542540000000001,0.022596838000000001,-0.0091380620000000006,-0.0073998435000000003,0.0077723189999999998,0.014228558000000001,-0.014067152499999999,-0.013322202,0.07732588,-0.016264757000000001,-0.0079585565,-0.00093506834999999996,-0.015259073999999999,-0.0026942384,0.0095788239999999997,-0.0090076949999999996,-0.0022177803,-0.021727730000000001,-0.015507391000000001,-0.069777049999999993,0.020461312999999998,0.040053516999999997,0.010932150999999999,-0.014563786,0.0033833177000000001,0.012030954,-0.013297369999999999,-0.013446359999999999,0.0039575505000000004,0.0053295013,0.0087035075000000007,-0.0063258730000000001,-0.012304102000000001,-0.021827055000000001,-0.0041841400000000003,-0.0083744869999999999,0.024359887,0.0030496417999999999,0.024595789999999999,-0.0055933379999999998,-0.016066104000000001,0.027488680000000001,0.0092746359999999993,0.021851888,-0.04934057,-0.01682347,0.0048483876000000002,0.0049290903999999998,0.01075833,-0.016922796,-0.021640818999999999,-0.0036130110000000002,-0.012105449000000001,0.022435432000000002,-0.0030030825,-0.044721875000000001,-0.0083124080000000003,-0.010398271000000001,0.0071949824000000001,0.010174785,0.021963630000000001,0.032157036999999999,-0.0024288497000000001,0.0068038832000000002,0.010677626000000001,-0.031337592999999997,0.065505999999999995,-0.0039730705000000002,0.021678065999999999,-0.0045690310000000003,-0.0011026822000000001,0.0068349229999999997,-0.014961093999999999,0.0099637149999999997,0.016364085,-6.7511159999999996e-5,0.0021945005,0.010236864,-0.0102244485,-0.015159746999999999,0.011795053,-0.014402379999999999,0.016190263,-0.018896916999999999,-0.0025157606999999999,-0.021876718999999999,-0.013011806000000001,0.0019104880999999999,-0.022261610000000001,0.034814030000000003,0.057162549999999999,-0.019554955999999998,-0.014961093999999999,0.034342225999999997,-0.018884499999999999,0.0074991705000000001,0.01822646,0.051451261999999998,0.028034977999999999,-0.0048080359999999999,0.012273063000000001,0.00272683,-0.0092870520000000005,-0.005379165,0.015581885,-0.0060372044999999997,-0.0060930760000000002,-0.015917113,-0.0030279143000000001,-0.00052612139999999997,-0.01933147,-0.044001753999999997,0.0084862300000000009,-0.024037076000000001,-0.015470143,-0.01189438,0.043256804000000003,0.033249630000000002,0.018300956,-0.023416283999999999,-0.0083558629999999998,0.023167968000000001,-0.0064376154999999996,-0.010801786000000001,0.013905747,0.024012245000000002,-0.033249630000000002,-0.034367059999999998,0.022075371999999999,0.027836324999999999,0.00032630393999999999,-0.017419430999999999,0.00094050022999999999,0.017704995000000001,0.021082105,0.013781588000000001,0.012751073,0.0046838773,0.046609079999999997,0.014923845,0.012837984,-0.023230046000000001,0.02925173,0.0094670819999999999,0.012446885,0.023267293000000001,-0.026420920000000001,0.028407452999999999,0.020548224,-0.0088214580000000008,0.014154063999999999,-0.056715580000000002,0.031461753000000002,-0.037669674,0.022100205000000001,0.045590980000000003,-0.013880915000000001,0.019194896999999999,-0.045491660000000003,0.0051401596999999999,-0.020622720000000001,0.023652184999999999,-0.0029425554,-0.00054164125999999998,-0.018934164,-0.0076543684000000001,-0.0060372044999999997,-0.019170064000000001,-0.014365133,-0.018437530000000001,0.00094282825000000002,-0.035981119999999998,0.0148990145,0.0067418039999999997,0.024868937000000001,0.0067976750000000004,-0.027513513,0.0031520726999999999,0.018425113999999999,-0.0084179430000000006,-0.055076689999999998,0.0037092335999999999,-0.0029518672,-0.0102244485,-0.024198482,0.028730266000000001,-0.015408063,0.037247534999999998,9.1178860000000006e-5,-0.013545687000000001,-0.026967215999999999,0.010149953,0.02121868,-0.016612400999999999,-0.0014456699,0.0034205653000000001,0.0046528378000000002,-0.0098830129999999995,0.023366620000000001,-0.0074184669999999998,0.0091628934999999998,0.030170504000000001,0.00042912264999999999,0.025949117000000001,-0.021119352000000001,-0.039904527000000002,0.0085048539999999992,-0.039681040000000001,0.017407015000000001,0.0012423604,0.031859060000000002,0.016165430000000001,-0.017704995000000001,0.022149868,-0.017990559999999999,0.012813152,-0.014427212,0.0025048967999999998,0.0016311316000000001,-0.011875755999999999,0.020386817000000002,0.019828104999999999,-0.033721436,-0.013098716999999999,0.0054971150000000003,0.0067914673000000004,-0.0031365528999999998,0.023391452,-0.033175137,-0.029996682,0.030393988,-0.0050842889999999996,0.005286046,-0.027885988,-0.034590546,0.0061458433000000003,-3.8526704999999997e-6,-0.017034540000000001,0.039705873000000003,0.029996682,0.012018538,-0.0081882499999999993,-0.030642306000000001,0.00064019200000000004,0.0036968180000000002,-0.0063755362999999999,-0.011348083,0.022733413000000001,-0.014427212,0.017779489999999998,-0.010994230000000001,-0.0082813690000000002,0.0074681310000000002,0.015656380000000001,0.038712605999999997,-0.028804759999999999,0.026147770000000001,-0.0040289415,0.0018437530000000001,-0.025874620000000001,-0.013682260999999999,-0.0046745655000000001,0.040152844,0.017071787000000001,0.0044852240000000003,0.0011779532999999999,-0.0037961446000000002,0.0028059809999999999,-0.00084738140000000005,-0.020858620000000001,0.011844716,0.022472679999999998,-0.0052208630000000001,-0.0095850320000000003,-0.0063041453000000003,-0.012086825000000001,0.024099154000000001,0.019977095,-0.0052084469999999997,0.024918599999999999,0.025626303999999999,-0.00034027173999999998,-0.025328323,0.011962667,0.010994230000000001,-0.017258024,0.015308736999999999,-0.037421357000000002,0.0037216494999999998,-0.014377548,-0.013520855,0.022720996,-0.018797589999999999,0.0083869029999999994,-0.032231532,0.0050656646000000003,-0.021082105,0.016450995999999999,0.00023202109999999999,-0.0082006660000000006,-0.020064005999999999,-0.020212995000000001,0.0058602790000000004,-0.017431846000000001,-0.0059844369999999996,-0.020647550000000001,-0.030716800999999998,0.016066104000000001,-0.015768124000000001,0.052593517999999999,0.0048576994000000002,-0.0070646160000000001,-0.010752121999999999,0.022460263000000001,-0.025179334000000001,0.051401599999999999,-0.02432264,-0.0041375802999999997,0.017680162999999999,-0.0025731840000000001,-0.036403257000000001,0.00044541843999999997,-0.0022255399999999998,0.00075659057000000002,0.041170944000000001,-0.019157647999999999,-0.00062932812999999998,0.024111569999999999,0.019095569999999999,-0.016115766,0.0096719430000000006,0.017407015000000001,-0.018474778000000001,0.00049663379999999996,-0.021615986,-0.0077164475999999997,-0.0051215360000000003,-0.023130719000000001,0.0086290120000000001,-0.0028990997999999999,0.015507391000000001,-0.011782637,0.0044821202999999997,0.026247097,-0.017469093000000002,0.028779929999999999,0.032554346999999997,0.013918162,-0.018611351000000002,-0.010013378999999999,0.0016063,0.04303332,-0.036800563000000001,0.0086352200000000007,-0.0033181347000000002,-0.012962141999999999,-0.0087966260000000001,0.00067161960000000002,-0.011490864999999999,-0.023006559999999999,0.0034143572999999999,-0.024782025999999999,0.025315909000000001,-0.0031241369999999999,-0.023304539999999999,0.0038923675,0.022845154999999999,0.030220167999999999,0.019617036000000001,-0.036825396000000003,-0.0062420660000000001,-0.0088835370000000004,-0.0070459920000000001,0.018971412,0.0065245264999999998,-0.00065105589999999997,0.017320103999999999,0.0023559065999999998,-0.031089275999999999,0.010901111999999999,-0.025216580999999998,0.021082105,-0.030195335,-0.026445750000000001,-0.0082565369999999996,-0.05025934,0.01933147,-0.015619133,0.0064127840000000004,-0.0092622199999999998,-0.014948677,-0.028829591000000002,0.0039482387000000004,-0.024409551000000002,-0.012695201999999999,0.038240799999999998,0.0064251999999999998]; - + $vector = $aiclient->embed_query($data->userprompt); $search = \core_search\manager::instance(true, true); $settings = [ 'similarity' => true, @@ -102,10 +100,21 @@ ]; $limit = 0; $docs = $search->search((object)$settings); - exit(); + var_dump($docs); + // Perform "R" from RAG, finding documents from within the context that are similar to the user's prompt. // Add the retrieved documents to the context for this chat by generating some system messages with the content // returned + if (!empty($docs)) { + $context = []; + foreach ($docs as $doc) { + $context[] = $doc->content; + } + $_SESSION[$aicontextkey]['messages'][] = (object)[ + "role" => "system", + "content" => "Use the following context to answer questions:" . implode("\n",$context) + ]; + } // Add the user's new prompt to the messages. $progress->update(2, $totalsteps,'Attaching user prompt'); @@ -114,7 +123,9 @@ "content" => $data->userprompt ]; // Pass the whole context over the AI to summarise. + var_dump($_SESSION[$aicontextkey]['messages']); $progress->update(3, $totalsteps, 'Waiting for response'); + $airesults = $aiclient->chat($_SESSION[$aicontextkey]['messages']); $_SESSION[$aicontextkey]['messages'] = array_merge($_SESSION[$aicontextkey]['messages'],$airesults); $progress->update(4, $totalsteps, 'Got Response'); diff --git a/search/engine/solr/classes/engine.php b/search/engine/solr/classes/engine.php index 8da39b32021e..48080323cf96 100644 --- a/search/engine/solr/classes/engine.php +++ b/search/engine/solr/classes/engine.php @@ -837,7 +837,8 @@ protected function add_solr_document($doc) { debugging('Solr client error adding document with id ' . $doc['id'] . ': ' . $e->getMessage(), DEBUG_DEVELOPER); } catch (\SolrServerException $e) { // We only use the first line of the message, as it's a fully java stacktrace behind it. - $msg = strtok($e->getMessage(), "\n"); + // $msg = strtok($e->getMessage(), "\n"); + $msg = $e->getMessage(); debugging('Solr server error adding document with id ' . $doc['id'] . ': ' . $msg, DEBUG_DEVELOPER); } diff --git a/search/engine/solrrag/classes/ai/aiclient.php b/search/engine/solrrag/classes/ai/aiclient.php index 4b9c9b8bad8e..a9445e3614a5 100644 --- a/search/engine/solrrag/classes/ai/aiclient.php +++ b/search/engine/solrrag/classes/ai/aiclient.php @@ -84,7 +84,7 @@ public function embed_query($content): array { $rawresult = $this->post($this->get_embeddings_url(), $params); // var_dump($rawresult); $result = json_decode($rawresult, true); - var_dump($result); + // var_dump($result); $usage = $result['usage']; $this->provider->increment_prompt_usage($usage['prompt_tokens']); $this->provider->increment_total_tokens($usage['total_tokens']); diff --git a/search/engine/solrrag/classes/engine.php b/search/engine/solrrag/classes/engine.php index 57794cbdf381..e7ece7e996c4 100644 --- a/search/engine/solrrag/classes/engine.php +++ b/search/engine/solrrag/classes/engine.php @@ -339,7 +339,7 @@ public function execute_query($filters, $accessinfo, $limit = 0) { ) { // Do a vector similarity search. debugging("Running similarity search", DEBUG_DEVELOPER); - $this->execute_solr_knn_query($filters, $accessinfo, $limit); + return $this->execute_solr_knn_query($filters, $accessinfo, $limit); } else { debugging("Running regular search", DEBUG_DEVELOPER); print_r($filters); @@ -353,6 +353,7 @@ public function execute_solr_knn_query($filters, $accessinfo, $limit) { $topK = 3; // Nearest neighbours to retrieve. $field = "solr_vector_" . count($vector); $requestbody = "{!knn f={$field} topK={$topK}}[" . implode(",", $vector) . "]"; + $filters->mainquery = $requestbody; if (empty($limit)) { $limit = \core_search\manager::MAX_RESULTS; @@ -360,20 +361,18 @@ public function execute_solr_knn_query($filters, $accessinfo, $limit) { $curl = $this->get_curl_object(); $requesturl = $this->get_connection_url('/select'); - $requesturl->param('fl', 'id,areaid,score'); + $requesturl->param('fl', 'id,areaid,score,content'); $requesturl->param('wt', 'xml'); - - $body = [ - 'query' => $requestbody + // $requesturl->param('query', $requestbody) + $params = [ + "query" => $requestbody, ]; - echo $requesturl->out(false); - $result = $curl->post($requesturl->out(false), - json_encode($body) - ); + $curl->setHeader('Content-type: application/json'); + $result = $curl->post($requesturl->out(false), json_encode($params)); + // Probably have to duplicate error handling code from the add_stored_file() function. $code = $curl->get_errno(); $info = $curl->get_info(); - // Now error handling. It is just informational, since we aren't tracking per file/doc results. if ($code != 0) { // This means an internal cURL error occurred error is in result. @@ -410,10 +409,22 @@ public function execute_solr_knn_query($filters, $accessinfo, $limit) { // echo htmlentities($result); // debugging("Got SOLR update/extract response"); $xml = simplexml_load_string($result); - print_r($xml->result); - print_r($xml->result['numFound']); - print_r($xml->result['maxScore']); - + // echo "
";
+                    // var_dump($xml->result);
+                    // echo "
"; + $results = $xml->result->doc; + $docs = []; + foreach($results as $doc) { + $docs[] = (object)[ + 'id' => (string)$doc->str[0], + 'areaid' => (string)$doc->str[1], + 'content' => (string)$doc->str[2], + 'score' => (string)$doc->float, + ]; + } + return $docs; + // [0][1][2] as defined in the fl attribute above + } } else { // We received an unprocessable response. diff --git a/search/engine/solrrag/classes/schema.php b/search/engine/solrrag/classes/schema.php index 54f00cb74fa8..6892d49dd29f 100644 --- a/search/engine/solrrag/classes/schema.php +++ b/search/engine/solrrag/classes/schema.php @@ -128,6 +128,7 @@ protected function validate_add_field_result($result) { // It comes as error when fetching fields data. if (!empty($results->error)) { + var_dump($results); throw new \moodle_exception('errorcreatingschema', 'search_solrrag', '', $results->error); } From f299b2909620f43703502768d5e53e7fcbdaf05e Mon Sep 17 00:00:00 2001 From: Michael Hughes Date: Tue, 19 Mar 2024 23:33:51 +0000 Subject: [PATCH 12/21] Vector search working for content indexed by solr --- mod/xaichat/templates/conversation.mustache | 3 + mod/xaichat/view.php | 56 +++--- search/engine/solrrag/classes/ai/aiclient.php | 27 +-- .../engine/solrrag/classes/ai/aiexception.php | 7 + .../engine/solrrag/classes/ai/aiprovider.php | 93 +++++++++- search/engine/solrrag/classes/ai/api.php | 5 +- search/engine/solrrag/classes/engine.php | 162 ++++++++++++++++-- search/engine/solrrag/lib.php | 6 + search/engine/solrrag/settings.php | 2 +- 9 files changed, 305 insertions(+), 56 deletions(-) create mode 100644 search/engine/solrrag/classes/ai/aiexception.php create mode 100644 search/engine/solrrag/lib.php diff --git a/mod/xaichat/templates/conversation.mustache b/mod/xaichat/templates/conversation.mustache index 7dacee0a4d14..cf653d4951c9 100644 --- a/mod/xaichat/templates/conversation.mustache +++ b/mod/xaichat/templates/conversation.mustache @@ -3,3 +3,6 @@ {{> mod_xaichat/message}} {{/messages}}
+{{#rawmessages}} +{{{rawmessages}}} +{{/rawmessages}} diff --git a/mod/xaichat/view.php b/mod/xaichat/view.php index b519113eb513..cdb94b416f09 100644 --- a/mod/xaichat/view.php +++ b/mod/xaichat/view.php @@ -94,13 +94,12 @@ $progress->update(1, $totalsteps,'Looking for relevant context'); $vector = $aiclient->embed_query($data->userprompt); $search = \core_search\manager::instance(true, true); - $settings = [ - 'similarity' => true, - 'vector' => $vector, - ]; - $limit = 0; + // Some of these values can't be "trusted" to the end user to supply, via something + // like a form, nor can they be entirely left to the plugin developer. + $settings = $aiprovider->get_settings_for_user($USER); + $settings['vector'] = $vector; + $settings['userquery'] = $data->userprompt; $docs = $search->search((object)$settings); - var_dump($docs); // Perform "R" from RAG, finding documents from within the context that are similar to the user's prompt. // Add the retrieved documents to the context for this chat by generating some system messages with the content @@ -110,22 +109,22 @@ foreach ($docs as $doc) { $context[] = $doc->content; } - $_SESSION[$aicontextkey]['messages'][] = (object)[ + $prompt = (object)[ "role" => "system", - "content" => "Use the following context to answer questions:" . implode("\n",$context) + "content" => "Use the following context to answer following question:" . implode("\n",$context) ]; - } - - // Add the user's new prompt to the messages. + $_SESSION[$aicontextkey]['messages'][] = $prompt; + } $progress->update(2, $totalsteps,'Attaching user prompt'); - $_SESSION[$aicontextkey]['messages'][] = (object)[ + // $_SESSION[$aicontextkey]['messages'][] + $prompt = (object)[ "role" => "user", "content" => $data->userprompt ]; - // Pass the whole context over the AI to summarise. - var_dump($_SESSION[$aicontextkey]['messages']); + $_SESSION[$aicontextkey]['messages'][] = $prompt; + + // Pass the whole context over the AI to summarise. $progress->update(3, $totalsteps, 'Waiting for response'); - $airesults = $aiclient->chat($_SESSION[$aicontextkey]['messages']); $_SESSION[$aicontextkey]['messages'] = array_merge($_SESSION[$aicontextkey]['messages'],$airesults); $progress->update(4, $totalsteps, 'Got Response'); @@ -133,14 +132,19 @@ // We stash the data in the session temporarily (should go into an activity-user store in database) but this // is fast and dirty, and then we do a redirect so that we don't double up the request if the user hit's // refresh. - redirect(new \moodle_url('/mod/xaichat/view.php', ['id' => $cm->id])); + $next = new \moodle_url('/mod/xaichat/view.php', ['id' => $cm->id]); + redirect($next); } else if ($chatform->is_cancelled()) { $_SESSION[$aicontextkey] = [ 'messages'=>[] ]; + $prompt = (object)[ + "role" => "system", + "content" => "You are a helpful AI. You should only use information you know. Only use information that is relevant to the question." + ]; + $_SESSION[$aicontextkey]['messages'][] = $prompt; } else { // Clear session on first view of form. - $toform = [ 'id' => $id, 'aiproviderid' => $moduleinstance->aiproviderid, @@ -154,19 +158,27 @@ $displaymessages = []; foreach ($_SESSION[$aicontextkey]['messages'] as $message) { - $displaymessages[] = [ - "role" => $message->role == "user" ? $userpic : \html_writer::tag("strong", $aipic), - "content" => format_text($message->content, FORMAT_MARKDOWN) - ]; + if ($message->role != "system") { + $displaymessages[] = [ + "role" => $message->role == "user" ? $userpic : \html_writer::tag("strong", $aipic), + "content" => format_text($message->content, FORMAT_MARKDOWN) + ]; + } } +$displaymessages = array_reverse($displaymessages); $tcontext = [ "userpic" => new user_picture($USER), "messages" => $displaymessages ]; +$chatform->display(); echo $OUTPUT->render_from_template("mod_xaichat/conversation", $tcontext); -$chatform->display(); +if (false) { + echo html_writer::tag("pre", print_r($_SESSION[$aicontextkey]['messages'],1)); +} + + //echo \html_writer::tag('pre', print_r($displaymessages,1)); //echo \html_writer::tag('pre', print_r($_SESSION[$aicontextkey]['messages'],1)); diff --git a/search/engine/solrrag/classes/ai/aiclient.php b/search/engine/solrrag/classes/ai/aiclient.php index a9445e3614a5..086c8d3a9264 100644 --- a/search/engine/solrrag/classes/ai/aiclient.php +++ b/search/engine/solrrag/classes/ai/aiclient.php @@ -1,6 +1,7 @@ libdir.'/filelib.php'); +use core\ai\AiException; /** * Base client for AI providers that uses simple http request. */ @@ -40,16 +41,20 @@ public function chat($messages) { $params = json_encode($params); $rawresult = $this->post($this->get_chat_completions_url(), $params); $jsonresult = json_decode($rawresult); - if (!isset($jsonresult->choices)) { - exit(); - return []; + if (isset($jsonresult->error)) { + throw new AiException("Error: " . $jsonresult->error->message . ":". print_r($messages, true)); + //return "Error: " . $jsonresult->error->message . ":". print_r($messages, true); } - $result = $this->convert_chat_completion($jsonresult->choices); - if (isset($jsonresult->usage)) { - $this->provider->increment_prompt_usage($jsonresult->usage->prompt_tokens); - $this->provider->increment_completion_tokens($jsonresult->usage->completion_tokens); - $this->provider->increment_total_tokens($jsonresult->usage->total_tokens); + $result = []; + if (isset($jsonresult->choices)) { + $result = $this->convert_chat_completion($jsonresult->choices); + if (isset($jsonresult->usage)) { + $this->provider->increment_prompt_usage($jsonresult->usage->prompt_tokens); + $this->provider->increment_completion_tokens($jsonresult->usage->completion_tokens); + $this->provider->increment_total_tokens($jsonresult->usage->total_tokens); + } } + return $result; } @@ -73,7 +78,7 @@ public function embed_query($content): array { // Send document to back end and return the vector $usedptokens = $this->provider->get_usage('prompt_tokens'); $totaltokens = $this->provider->get_usage('total_tokens'); - mtrace("Prompt tokens: $usedptokens. Total tokens: $totaltokens"); + // mtrace("Prompt tokens: $usedptokens. Total tokens: $totaltokens"); $params = [ "input" => htmlentities($content), // TODO need to do some length checking here! "model" => $this->provider->get('embeddingmodel') @@ -88,7 +93,7 @@ public function embed_query($content): array { $usage = $result['usage']; $this->provider->increment_prompt_usage($usage['prompt_tokens']); $this->provider->increment_total_tokens($usage['total_tokens']); - mtrace("Used Prompt tokens: {$usage['prompt_tokens']}. Total tokens: {$usage['total_tokens']}"); + // mtrace("Used Prompt tokens: {$usage['prompt_tokens']}. Total tokens: {$usage['total_tokens']}"); $data = $result['data']; foreach($data as $d) { if ($d['object'] == "embedding") { @@ -97,7 +102,7 @@ public function embed_query($content): array { } $usedptokens = $this->provider->get_usage('prompt_tokens'); $totaltokens = $this->provider->get_usage('total_tokens'); - mtrace("Total Used: Prompt tokens: $usedptokens. Total tokens: $totaltokens"); + // mtrace("Total Used: Prompt tokens: $usedptokens. Total tokens: $totaltokens"); return []; } public function embed_documents(array $documents) { diff --git a/search/engine/solrrag/classes/ai/aiexception.php b/search/engine/solrrag/classes/ai/aiexception.php new file mode 100644 index 000000000000..ab006c952e12 --- /dev/null +++ b/search/engine/solrrag/classes/ai/aiexception.php @@ -0,0 +1,7 @@ +dirroot . "/search/engine/solrrag/lib.php"); + use \core\persistent; +use core_course_category; class AIProvider extends persistent { // Ultimately this would extend a persistent. - + const CONTEXT_ALL_MY_COURSES = -1; protected static function define_properties() { @@ -37,7 +39,17 @@ protected static function define_properties() ], 'completionmodel' => [ 'type' => PARAM_ALPHANUMEXT - ] + ], + // What context is this provider attached to. + // If null, it's a global provider. + // If -1 its limited to user's own courses. + 'context' => [ + 'type' => PARAM_INT + ], + // If true, only courses that the user is enrolled in will be searched. + 'onlyenrolledcourses' => [ + 'type' => PARAM_BOOL + ], ]; } @@ -95,6 +107,61 @@ public function increment_total_tokens($change) { set_config($key, $new, 'ai'); } + /** + * Returns appropriate search settings based on + * provider configuration. + */ + public function get_settings() { + // `userquery` and `vector` will be filled at run time. + $settings = [ + 'userquery'=> null, + 'vector' => null, + // `similarity` is a boolean that determines if the query should use vector similarity search. + 'similarity' => true, + 'areaids' => [], + // `excludeareaids` is an array of areaids that should be excluded from the search. + 'excludeareaids'=> ["core_user-user"], // <-- This may be should be in control of the AI Provider. + 'courseids' => [], // This of course IDs that result should be limited to. + ]; + return $settings; + } + + /** + * Gets user specific settings. + * + * This takes on some of the function that the manager code did. + */ + public function get_settings_for_user($user) { + $usersettings = $this->get_settings(); + + // This is basically manager::build_limitcourseids(). + $mycourseids = enrol_get_my_courses(array('id', 'cacherev'), 'id', 0, [], false); + $onlyenrolledcourses = $this->get('onlyenrolledcourses'); + $courseids = []; + if ($this->get('context') == self::CONTEXT_ALL_MY_COURSES) { + $courseids = array_keys($mycourseids); + } else { + $context = \context::instance_by_id($this->get('context')); + if ($context->contextlevel == CONTEXT_COURSE) { + // Check that the specific course is also in the user's list of courses. + $courseids = array_intersect([$context->instanceid], $mycourseids); + } else if ($context->contextlevel == CONTEXT_COURSECAT) { + // CourseIDs will be all courses in the category, + // optionally that the user is enrolled in + $category = core_course_category::get($context->instanceid); + $categorycourseids = $category->get_courses([ + 'recursive'=>true, + 'idonly' => true + ]); + } else if ($context->contextlevel == CONTEXT_SYSTEM) { + // No restrictions anywhere. + } + } + $usersettings['courseids'] = $courseids; + + return $usersettings; + } + //public function // TODO token counting. /** @@ -119,7 +186,25 @@ public static function get_records($filters = array(), $sort = '', $order = 'ASC 'embeddingmodel' => 'text-embedding-3-small', 'completions' => 'chat/completions', 'completionmodel' => 'gpt-4-turbo-preview', - 'apikey'=> $_ENV['OPENAIKEY'] + 'apikey'=> $_ENV['OPENAIKEY'], + 'context' => \context_system::instance()->id, + //null, // Global AI Provider + 'onlyenrolledcourses' => true + ]); + array_push($records, $fake); + $fake = new static(0, (object) [ + 'id' => 2, + 'name' => "Ollama AI Provider", + 'allowembeddings' => true, + 'allowquery' => true, + 'baseurl' => 'http://127.0.0.1:11434/api/', + 'embeddings' => 'embeddings', + 'embeddingmodel' => '', + 'completions' => 'chat', + 'completionmodel' => 'llama2', + 'context' => null, // Global AI Provider + 'onlyenrolledcourses' => true + // 'apikey'=> $_ENV['OPENAIKEY'] ]); array_push($records, $fake); return $records; diff --git a/search/engine/solrrag/classes/ai/api.php b/search/engine/solrrag/classes/ai/api.php index ad5939815746..be8c4d627b68 100644 --- a/search/engine/solrrag/classes/ai/api.php +++ b/search/engine/solrrag/classes/ai/api.php @@ -3,6 +3,8 @@ namespace core\ai; +require_once($CFG->dirroot . "/search/engine/solrrag/lib.php"); + class api { /** @@ -15,7 +17,8 @@ public static function get_all_providers($context = null) { } public static function get_provider(int $id): AIProvider { $fakes = AIProvider::get_records(); - return $fakes[0]; + return $fakes[0]; // Open AI + // return $fakes[1]; // Ollama } } diff --git a/search/engine/solrrag/classes/engine.php b/search/engine/solrrag/classes/engine.php index e7ece7e996c4..10a5cbcbf240 100644 --- a/search/engine/solrrag/classes/engine.php +++ b/search/engine/solrrag/classes/engine.php @@ -4,12 +4,14 @@ use search_solrrag\document; use search_solrrag\schema; -// Fudge autoloading! -require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/api.php"); -require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/aiprovider.php"); -require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/aiclient.php"); +require_once($CFG->dirroot . "/search/engine/solrrag/lib.php"); +// // Fudge autoloading! +// require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/api.php"); +// require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/aiprovider.php"); +// require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/aiclient.php"); use \core\ai\AIProvider; use \core\ai\AIClient; +use \core\ai\AiException; class engine extends \search_solr\engine { /** @@ -79,7 +81,7 @@ public function add_document($document, $fileindexing = false) { $vlength = count($vector); $vectorfield = "solr_vector_" . $vlength; $docdata[$vectorfield] = $vector; - var_dump($docdata); + // var_dump($docdata); } else { debugging("Err didn't do any vector stuff!"); } @@ -107,7 +109,7 @@ public function add_document_batch(array $documents, bool $fileindexing = false) $vlength = count($vector); $vectorfield = "solr_vector_" . $vlength; $doc[$vectorfield] = $vector; - var_dump($doc); + // var_dump($doc); } else { debugging("Err didn't do any vector stuff!"); } @@ -338,37 +340,163 @@ public function execute_query($filters, $accessinfo, $limit = 0) { $filters->similarity ) { // Do a vector similarity search. - debugging("Running similarity search", DEBUG_DEVELOPER); - return $this->execute_solr_knn_query($filters, $accessinfo, $limit); + // debugging("Running similarity search", DEBUG_DEVELOPER); + // We may get accessinfo, but we actually should determine our own ones to apply too + // But we can't access the "manager" class' get_areas_user_accesses function, and + // that's already been called based on the configuration / data from the user + return $this->execute_similarity_query($filters, $accessinfo, $limit); } else { - debugging("Running regular search", DEBUG_DEVELOPER); - print_r($filters); - print_r($accessinfo); + // debugging("Running regular search", DEBUG_DEVELOPER); + // print_r($filters); + // print_r($accessinfo); return parent::execute_query($filters, $accessinfo, $limit); } } + /** + * A logging function just to allow us to output all the things + * that the process is doing for verification / validation. + * + * Probably not the most efficient way to do this, but Moodle's lacking + * a good generic logging framework. + * + * @param mixed $message An object/array/string that will be turned into a string. + */ + protected function log($message) { + $logfiledir = make_temp_directory('search_solrrag'); + $file = $logfiledir . '/solr_knn_query.log'; + $log = fopen($file, 'a'); + if (is_object($message)) { + $message = print_r($message, true); + } else if (is_array($message)) { + $message = print_r($message, true); + } + fwrite($log, date('Y-m-d H:i:s') . " " . $message . "\n"); + fclose($log); + } - public function execute_solr_knn_query($filters, $accessinfo, $limit) { + /** + * Perform a similarity search against the backend. + * + * This should be an optional method that can be implemented if the engine supports + * a vector search capability. + * + * This function will broadly replicate the same functionality as execute_query, but optimised + * for similarity + * + * @param \stdClass filters The filters object that contains the query and any other parameters. Basically from the search form. + * @param \stdClass accessinfo The access information for the user. + * @param int limit The maximum number of results to return. + */ + public function execute_similarity_query(\stdClass $filters, \stdClass $accessinfo, int $limit = null) { + $data = clone($filters); + $this->log("Executing SOLR KNN QUery"); + $this->log("Filters"); + $this->log($filters); $vector = $filters->vector; - $topK = 3; // Nearest neighbours to retrieve. - $field = "solr_vector_" . count($vector); - $requestbody = "{!knn f={$field} topK={$topK}}[" . implode(",", $vector) . "]"; + $topK = $limit > 0 ? $limit: 1; // We'll make the number of neighbours the same as search result limit. - $filters->mainquery = $requestbody; if (empty($limit)) { $limit = \core_search\manager::MAX_RESULTS; + $topK = \core_search\manager::MAX_RESULTS; // Nearest neighbours to retrieve. } + $field = "solr_vector_" . count($vector); + $requestbody = "{!knn f={$field} topK={$topK}}[" . implode(",", $vector) . "]"; + $this->log($requestbody); + $filters->mainquery = $requestbody; + // Build filter restrictions. + $filterqueries = []; + if(!empty($data->areaids)) { + $filterqueries[] = '{!cache=false}areaid:(' . implode(' OR ', $data->areaids) . ')'; + } + + if(!empty($data->excludeareaids)) { + $filterqueries[] = '{!cache=false}-areaid:(' . implode(' OR ', $data->excludeareaids) . ')'; + } + // Build access restrictions. + + // And finally restrict it to the context where the user can access, we want this one cached. + // If the user can access all contexts $usercontexts value is just true, we don't need to filter + // in that case. + if (!$accessinfo->everything && is_array($accessinfo->usercontexts)) { + // Join all area contexts into a single array and implode. + $allcontexts = array(); + foreach ($accessinfo->usercontexts as $areaid => $areacontexts) { + if (!empty($data->areaids) && !in_array($areaid, $data->areaids)) { + // Skip unused areas. + continue; + } + foreach ($areacontexts as $contextid) { + // Ensure they are unique. + $allcontexts[$contextid] = $contextid; + } + } + if (empty($allcontexts)) { + // This means there are no valid contexts for them, so they get no results. + return null; + } + $filterqueries[] = 'contextid:(' . implode(' OR ', $allcontexts) . ')'; + } + + if (!$accessinfo->everything && $accessinfo->separategroupscontexts) { + // Add another restriction to handle group ids. If there are any contexts using separate + // groups, then results in that context will not show unless you belong to the group. + // (Note: Access all groups is taken care of earlier, when computing these arrays.) + + // This special exceptions list allows for particularly pig-headed developers to create + // multiple search areas within the same module, where one of them uses separate + // groups and the other uses visible groups. It is a little inefficient, but this should + // be rare. + $exceptions = ''; + if ($accessinfo->visiblegroupscontextsareas) { + foreach ($accessinfo->visiblegroupscontextsareas as $contextid => $areaids) { + $exceptions .= ' OR (contextid:' . $contextid . ' AND areaid:(' . + implode(' OR ', $areaids) . '))'; + } + } + + if ($accessinfo->usergroups) { + // Either the document has no groupid, or the groupid is one that the user + // belongs to, or the context is not one of the separate groups contexts. + $filterqueries[] = '(*:* -groupid:[* TO *]) OR ' . + 'groupid:(' . implode(' OR ', $accessinfo->usergroups) . ') OR ' . + '(*:* -contextid:(' . implode(' OR ', $accessinfo->separategroupscontexts) . '))' . + $exceptions; + } else { + // Either the document has no groupid, or the context is not a restricted one. + $filterqueries[] = '(*:* -groupid:[* TO *]) OR ' . + '(*:* -contextid:(' . implode(' OR ', $accessinfo->separategroupscontexts) . '))' . + $exceptions; + } + } + + if ($this->file_indexing_enabled()) { + // Now group records by solr_filegroupingid. Limit to 3 results per group. + // TODO work out how to convert the following into query / filter parameters. + // $query->setGroup(true); + // $query->setGroupLimit(3); + // $query->setGroupNGroups(true); + // $query->addGroupField('solr_filegroupingid'); + } else { + // Make sure we only get text files, in case the index has pre-existing files. + $filterqueries[] = 'type:'.\core_search\manager::TYPE_TEXT; + } + + // Finally perform the actaul search + $curl = $this->get_curl_object(); $requesturl = $this->get_connection_url('/select'); $requesturl->param('fl', 'id,areaid,score,content'); $requesturl->param('wt', 'xml'); - // $requesturl->param('query', $requestbody) + $requesturl->param('fq', implode("&", $filterqueries)); + $params = [ "query" => $requestbody, ]; + $curl->setHeader('Content-type: application/json'); $result = $curl->post($requesturl->out(false), json_encode($params)); + $this->log($result); // Probably have to duplicate error handling code from the add_stored_file() function. $code = $curl->get_errno(); diff --git a/search/engine/solrrag/lib.php b/search/engine/solrrag/lib.php new file mode 100644 index 000000000000..46f51c2d8e66 --- /dev/null +++ b/search/engine/solrrag/lib.php @@ -0,0 +1,6 @@ +dirroot ."/search/engine/solrrag/classes/ai/api.php"); +require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/aiprovider.php"); +require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/aiclient.php"); +require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/aiexception.php"); \ No newline at end of file diff --git a/search/engine/solrrag/settings.php b/search/engine/solrrag/settings.php index 85b427e5c75b..b4a837c5e0e4 100644 --- a/search/engine/solrrag/settings.php +++ b/search/engine/solrrag/settings.php @@ -23,7 +23,7 @@ */ defined('MOODLE_INTERNAL') || die(); - +require_once($CFG->dirroot . "/search/engine/solrrag/lib.php"); if ($ADMIN->fulltree) { if (!during_initial_install()) { From c9f18ec69a03d6c8dfbe8714c755cd95bed23da1 Mon Sep 17 00:00:00 2001 From: Michael hughes Date: Wed, 20 Mar 2024 15:51:49 +0000 Subject: [PATCH 13/21] solrrag_coreai: SolrRag but with AI coding moving to a core subsystem --- .phpcs.xml | 105 +++++++++ course/moodleform_mod.php | 21 ++ .../plusteams/classes/data_controller.php | 48 ++++ .../plusteams/classes/field_controller.php | 0 .../lang/en/customfield_plusteams.php | 0 customfield/field/plusteams/version.php | 6 + grade/grading/form/multigraders | 1 + grade/grading/form/workflow | 1 + .../solrrag => lib}/classes/ai/aiclient.php | 0 .../classes/ai/aiexception.php | 0 .../solrrag => lib}/classes/ai/aiprovider.php | 0 .../engine/solrrag => lib}/classes/ai/api.php | 0 lib/classes/event/grade_item_hidden.php | 43 ++++ lib/classes/event/grade_item_revealed.php | 43 ++++ lib/moodlelib.php | 2 + lib/phpunit/bootstrap.php | 2 +- lib/setup.php | 3 + local/ai | 1 + local/clientevents | 1 + local/codechecker | 1 + local/plusteams | 1 + local/recompletion | 1 + mod/xaichat/lib.php | 10 +- mod/xaichat/mod_form.php | 14 +- search/engine/solrrag/classes/ai/aiclient.phx | 138 ++++++++++++ .../engine/solrrag/classes/ai/aiexception.phx | 7 + .../engine/solrrag/classes/ai/aiprovider.phx | 213 ++++++++++++++++++ search/engine/solrrag/classes/ai/api.phx | 24 ++ search/engine/solrrag/classes/engine.php | 2 +- search/engine/solrrag/lib.php | 8 +- 30 files changed, 673 insertions(+), 23 deletions(-) create mode 100644 .phpcs.xml create mode 100644 customfield/field/plusteams/classes/data_controller.php create mode 100644 customfield/field/plusteams/classes/field_controller.php create mode 100644 customfield/field/plusteams/lang/en/customfield_plusteams.php create mode 100644 customfield/field/plusteams/version.php create mode 160000 grade/grading/form/multigraders create mode 160000 grade/grading/form/workflow rename {search/engine/solrrag => lib}/classes/ai/aiclient.php (100%) rename {search/engine/solrrag => lib}/classes/ai/aiexception.php (100%) rename {search/engine/solrrag => lib}/classes/ai/aiprovider.php (100%) rename {search/engine/solrrag => lib}/classes/ai/api.php (100%) create mode 100644 lib/classes/event/grade_item_hidden.php create mode 100644 lib/classes/event/grade_item_revealed.php create mode 160000 local/ai create mode 160000 local/clientevents create mode 160000 local/codechecker create mode 160000 local/plusteams create mode 160000 local/recompletion create mode 100644 search/engine/solrrag/classes/ai/aiclient.phx create mode 100644 search/engine/solrrag/classes/ai/aiexception.phx create mode 100644 search/engine/solrrag/classes/ai/aiprovider.phx create mode 100644 search/engine/solrrag/classes/ai/api.phx diff --git a/.phpcs.xml b/.phpcs.xml new file mode 100644 index 000000000000..7c5f2f2a8f86 --- /dev/null +++ b/.phpcs.xml @@ -0,0 +1,105 @@ + + + + node_modules/ + vendor/ + admin/tool/componentlibrary/amd/src/lunr.js + admin/tool/pluginskel/vendor/autoload.php + admin/tool/pluginskel/vendor/composer/ + admin/tool/pluginskel/vendor/symfony/ + admin/tool/pluginskel/vendor/monolog/monolog/ + admin/tool/pluginskel/vendor/psr/log/ + admin/tool/policy/amd/src/jquery-eu-cookie-law-popup.js + auth/cas/CAS/ + cache/stores/mongodb/MongoDB/ + enrol/lti/ims-blti/ + filter/algebra/AlgParser.pm + filter/tex/mimetex.* + h5p/h5plib/v124/joubel/core/ + h5p/h5plib/v124/joubel/editor/ + lib/editor/atto/plugins/html/yui/src/codemirror/ + lib/editor/atto/plugins/html/yui/src/beautify/ + lib/editor/atto/yui/src/rangy/js/*.* + lib/editor/tiny/js/tinymce/ + lib/editor/tinymce/plugins/pdw/tinymce/ + lib/editor/tinymce/plugins/spellchecker/rpc.php + lib/editor/tinymce/tiny_mce/ + lib/mlbackend/php/phpml/ + lib/adodb/ + lib/behat/axe/ + lib/bennu/ + lib/evalmath/ + lib/phpspreadsheet/ + lib/google/ + lib/htmlpurifier/ + lib/minify/matthiasmullie-minify/ + lib/minify/matthiasmullie-pathconverter/ + lib/pear/HTML/Common.php + lib/pear/HTML/QuickForm.php + lib/pear/HTML/QuickForm/ + lib/pear/PEAR.php + lib/phpmailer/ + lib/simplepie/ + lib/tcpdf/ + lib/yuilib/ + lib/yuilib/gallery/ + lib/jquery/ + lib/html2text/ + lib/markdown/ + lib/xhprof/ + lib/horde/ + lib/requirejs/ + lib/amd/src/loglevel.js + lib/mustache/ + lib/amd/src/mustache.js + lib/graphlib.php + lib/php-css-parser/ + lib/rtlcss/ + lib/scssphp/ + lib/spout/ + lib/amd/src/chartjs-lazy.js + lib/maxmind/GeoIp2/ + lib/maxmind/MaxMind/ + lib/ltiprovider/ + lib/lti1p3/ + lib/amd/src/truncate.js + lib/fonts/ + lib/amd/src/adapter.js + lib/validateurlsyntax.php + lib/amd/src/popper.js + lib/geopattern-php/ + lib/php-jwt/ + lib/polyfills/ + lib/emoji-data/ + lib/plist/ + lib/zipstream/ + lib/php-enum/ + lib/http-message/ + lib/phpxmlrpc/ + local/codechecker/phpcs/ + local/codechecker/PHPCompatibility/ + media/player/videojs/amd/src/video-lazy.js + media/player/videojs/amd/src/Youtube-lazy.js + media/player/videojs/videojs/ + media/player/videojs/amd/src/local/ogv/ogv.js + media/player/videojs/ogvjs/ + media/player/videojs/amd/src/videojs-ogvjs-lazy.js + mod/assign/feedback/editpdf/fpdi/ + repository/s3/S3.php + theme/boost/scss/bootstrap/ + theme/boost/amd/src/bootstrap/alert.js + theme/boost/amd/src/bootstrap/button.js + theme/boost/amd/src/bootstrap/carousel.js + theme/boost/amd/src/bootstrap/collapse.js + theme/boost/amd/src/bootstrap/dropdown.js + theme/boost/amd/src/bootstrap/modal.js + theme/boost/amd/src/bootstrap/popover.js + theme/boost/amd/src/bootstrap/tools/sanitizer.js + theme/boost/amd/src/bootstrap/scrollspy.js + theme/boost/amd/src/bootstrap/tab.js + theme/boost/amd/src/bootstrap/toast.js + theme/boost/amd/src/bootstrap/tooltip.js + theme/boost/amd/src/bootstrap/util.js + theme/boost/amd/src/index.js + theme/boost/scss/fontawesome/ + diff --git a/course/moodleform_mod.php b/course/moodleform_mod.php index 3357140484a1..e593eaec2b5b 100644 --- a/course/moodleform_mod.php +++ b/course/moodleform_mod.php @@ -1195,4 +1195,25 @@ public function get_data() { } return $data; } + + public function standard_aiprovider_coursemodule_elements() { + + if (!$this->_features->ai) { + return; + } + $mform = $this->_form; + // Adding the rest of mod_xaichat settings, spreading all them into this fieldset + // ... or adding more fieldsets ('header' elements) if needed for better logic. + $mform->addElement('header', 'aiprovider', get_string('aiprovider')); + + $providers = \core\ai\api::get_all_providers(); + $optproviders = []; + foreach($providers as $provider) { + $optproviders[$provider->get('id')] = $provider->get('name'); + } + $mform->addElement('select', 'aiproviderid', + 'Choose Provider', + $optproviders + ); + } } diff --git a/customfield/field/plusteams/classes/data_controller.php b/customfield/field/plusteams/classes/data_controller.php new file mode 100644 index 000000000000..d39514b5a7be --- /dev/null +++ b/customfield/field/plusteams/classes/data_controller.php @@ -0,0 +1,48 @@ +get_field()->get_configdata_property('defaultvalue'); + if ('' . $defaultvalue !== '') { + $key = array_search($defaultvalue, $this->get_field()->get_options()); + if ($key !== false) { + return $key; + } + } + return 0; + } + + public function instance_form_definition(\MoodleQuickForm $mform) { +// $field = $this->get_field(); +// $config = $field->get('configdata'); +// $options = $field->get_options(); +// $formattedoptions = array(); +// $context = $this->get_field()->get_handler()->get_configuration_context(); +// local_plusteams_linkform($mform); + } + public function instance_form_validation(array $data, array $files) : array { + + } + public function export_value() { + $value = $this->get_value(); + + if ($this->is_empty($value)) { + return null; + } + + $options = $this->get_field()->get_options(); + if (array_key_exists($value, $options)) { + return format_string($options[$value], true, + ['context' => $this->get_field()->get_handler()->get_configuration_context()]); + } + + return null; + } +} \ No newline at end of file diff --git a/customfield/field/plusteams/classes/field_controller.php b/customfield/field/plusteams/classes/field_controller.php new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/customfield/field/plusteams/lang/en/customfield_plusteams.php b/customfield/field/plusteams/lang/en/customfield_plusteams.php new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/customfield/field/plusteams/version.php b/customfield/field/plusteams/version.php new file mode 100644 index 000000000000..f4552b133d4c --- /dev/null +++ b/customfield/field/plusteams/version.php @@ -0,0 +1,6 @@ +component = 'customfield_plusteams'; +$plugin->version = 2022041900; +$plugin->requires = 2022041200; diff --git a/grade/grading/form/multigraders b/grade/grading/form/multigraders new file mode 160000 index 000000000000..aec0ab5d4aa5 --- /dev/null +++ b/grade/grading/form/multigraders @@ -0,0 +1 @@ +Subproject commit aec0ab5d4aa5dce02bdf9336784ebe14aa10b9e3 diff --git a/grade/grading/form/workflow b/grade/grading/form/workflow new file mode 160000 index 000000000000..eb0a4bd5c758 --- /dev/null +++ b/grade/grading/form/workflow @@ -0,0 +1 @@ +Subproject commit eb0a4bd5c75872357e85934f1eaa1035bfa12cd0 diff --git a/search/engine/solrrag/classes/ai/aiclient.php b/lib/classes/ai/aiclient.php similarity index 100% rename from search/engine/solrrag/classes/ai/aiclient.php rename to lib/classes/ai/aiclient.php diff --git a/search/engine/solrrag/classes/ai/aiexception.php b/lib/classes/ai/aiexception.php similarity index 100% rename from search/engine/solrrag/classes/ai/aiexception.php rename to lib/classes/ai/aiexception.php diff --git a/search/engine/solrrag/classes/ai/aiprovider.php b/lib/classes/ai/aiprovider.php similarity index 100% rename from search/engine/solrrag/classes/ai/aiprovider.php rename to lib/classes/ai/aiprovider.php diff --git a/search/engine/solrrag/classes/ai/api.php b/lib/classes/ai/api.php similarity index 100% rename from search/engine/solrrag/classes/ai/api.php rename to lib/classes/ai/api.php diff --git a/lib/classes/event/grade_item_hidden.php b/lib/classes/event/grade_item_hidden.php new file mode 100644 index 000000000000..443a422107f1 --- /dev/null +++ b/lib/classes/event/grade_item_hidden.php @@ -0,0 +1,43 @@ +. + +/** + * Grade item updated event. + * + * @package core + * @copyright 2019 Dmitrii Metelkin + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core\event; + +defined('MOODLE_INTERNAL') || die(); + +class grade_item_hidden extends grade_item_updated { + protected function init() { + $this->data['objecttable'] = 'grade_items'; + $this->data['crud'] = 'u'; + $this->data['edulevel'] = self::LEVEL_OTHER; + } + public static function get_name() { + return get_string('eventgradeitemhidden', 'core_grades'); + } + public function get_description() { + return "The user with id '" . $this->userid . "' hid a grade item with id '" . $this->objectid . "'" . + " of type '" . $this->other['itemtype'] . "' and name '" . $this->other['itemname'] . "'" . + " in the course with the id '" . $this->courseid . "'."; + } +} diff --git a/lib/classes/event/grade_item_revealed.php b/lib/classes/event/grade_item_revealed.php new file mode 100644 index 000000000000..1353fda69b2d --- /dev/null +++ b/lib/classes/event/grade_item_revealed.php @@ -0,0 +1,43 @@ +. + +/** + * Grade item updated event. + * + * @package core + * @copyright 2019 Dmitrii Metelkin + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core\event; + +defined('MOODLE_INTERNAL') || die(); + +class grade_item_revealed extends grade_item_updated { + protected function init() { + $this->data['objecttable'] = 'grade_items'; + $this->data['crud'] = 'u'; + $this->data['edulevel'] = self::LEVEL_OTHER; + } + public static function get_name() { + return get_string('eventgradeitemrevealed', 'core_grades'); + } + public function get_description() { + return "The user with id '" . $this->userid . "' revealed a grade item with id '" . $this->objectid . "'" . + " of type '" . $this->other['itemtype'] . "' and name '" . $this->other['itemname'] . "'" . + " in the course with the id '" . $this->courseid . "'."; + } +} diff --git a/lib/moodlelib.php b/lib/moodlelib.php index 79053882ceda..049c8574daa0 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -421,6 +421,8 @@ define('FEATURE_CONTROLS_GRADE_VISIBILITY', 'controlsgradevisbility'); /** True if module supports plagiarism plugins */ define('FEATURE_PLAGIARISM', 'plagiarism'); +/** True if module uses ai provider. */ +define('FEATURE_AI', 'ai'); /** True if module has code to track whether somebody viewed it */ define('FEATURE_COMPLETION_TRACKS_VIEWS', 'completion_tracks_views'); diff --git a/lib/phpunit/bootstrap.php b/lib/phpunit/bootstrap.php index 0ac815e3143f..f7ca43a4c0ef 100644 --- a/lib/phpunit/bootstrap.php +++ b/lib/phpunit/bootstrap.php @@ -251,10 +251,10 @@ $CFG->profilingenabled = true; $CFG->profilingincluded = '*'; } + require("$CFG->dirroot/lib/setup.php"); raise_memory_limit(MEMORY_HUGE); - if (PHPUNIT_UTIL) { // We are not going to do testing, this is 'true' in utility scripts that only init database. return; diff --git a/lib/setup.php b/lib/setup.php index cba63271ab66..c8af3b8f568c 100644 --- a/lib/setup.php +++ b/lib/setup.php @@ -653,6 +653,7 @@ setup_DB(); if (PHPUNIT_TEST and !PHPUNIT_UTIL) { + // Make sure tests do not run in parallel. $suffix = ''; if (phpunit_util::is_in_isolated_process()) { @@ -663,7 +664,9 @@ try { if ($dbhash = $DB->get_field('config', 'value', array('name'=>'phpunittest'))) { // reset DB tables + echo('1'); phpunit_util::reset_database(); + echo('2'); } } catch (Exception $e) { if ($dbhash) { diff --git a/local/ai b/local/ai new file mode 160000 index 000000000000..a41b8271f47d --- /dev/null +++ b/local/ai @@ -0,0 +1 @@ +Subproject commit a41b8271f47dbadfe33a95607c87acf5d0d6c588 diff --git a/local/clientevents b/local/clientevents new file mode 160000 index 000000000000..24ed0b779860 --- /dev/null +++ b/local/clientevents @@ -0,0 +1 @@ +Subproject commit 24ed0b779860770add6d51ca226fd2e943e2508f diff --git a/local/codechecker b/local/codechecker new file mode 160000 index 000000000000..9708c27f3a24 --- /dev/null +++ b/local/codechecker @@ -0,0 +1 @@ +Subproject commit 9708c27f3a24933863c2ca66a0ecbfcfb6cc80b9 diff --git a/local/plusteams b/local/plusteams new file mode 160000 index 000000000000..3e0a109ed6eb --- /dev/null +++ b/local/plusteams @@ -0,0 +1 @@ +Subproject commit 3e0a109ed6eb10770a230dbe9abb12ad90a10873 diff --git a/local/recompletion b/local/recompletion new file mode 160000 index 000000000000..2ed9fc6d3d85 --- /dev/null +++ b/local/recompletion @@ -0,0 +1 @@ +Subproject commit 2ed9fc6d3d853cece1bc92293294457f8ac890dc diff --git a/mod/xaichat/lib.php b/mod/xaichat/lib.php index 6c7e078f64d5..d3e0fdf4cb6f 100644 --- a/mod/xaichat/lib.php +++ b/mod/xaichat/lib.php @@ -23,10 +23,10 @@ */ defined('MOODLE_INTERNAL') || die(); - -require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/api.php"); -require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/aiprovider.php"); -require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/aiclient.php"); +// +//require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/api.php"); +//require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/aiprovider.php"); +//require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/aiclient.php"); /** * Return if the plugin supports $feature. @@ -36,6 +36,8 @@ */ function xaichat_supports($feature) { switch ($feature) { + case FEATURE_AI: + return true; case FEATURE_GRADE_HAS_GRADE: return true; case FEATURE_MOD_INTRO: diff --git a/mod/xaichat/mod_form.php b/mod/xaichat/mod_form.php index 1b88e0a319ea..4de0909adf93 100644 --- a/mod/xaichat/mod_form.php +++ b/mod/xaichat/mod_form.php @@ -69,19 +69,7 @@ public function definition() { $this->add_intro_editor(); } - // Adding the rest of mod_xaichat settings, spreading all them into this fieldset - // ... or adding more fieldsets ('header' elements) if needed for better logic. - $mform->addElement('header', 'xaichatfieldset', get_string('xaichatfieldset', 'mod_xaichat')); - - $providers = \core\ai\api::get_all_providers(); - $optproviders = []; - foreach($providers as $provider) { - $optproviders[$provider->get('id')] = $provider->get('name'); - } - $mform->addElement('select', 'aiproviderid', - 'Choose Provider', - $optproviders - ); + $this->standard_aiprovider_coursemodule_elements(); // Add standard grading elements. $this->standard_grading_coursemodule_elements(); diff --git a/search/engine/solrrag/classes/ai/aiclient.phx b/search/engine/solrrag/classes/ai/aiclient.phx new file mode 100644 index 000000000000..086c8d3a9264 --- /dev/null +++ b/search/engine/solrrag/classes/ai/aiclient.phx @@ -0,0 +1,138 @@ +libdir.'/filelib.php'); +use core\ai\AiException; +/** + * Base client for AI providers that uses simple http request. + */ +class AIClient extends \curl { + /** + * @var AIProvider + */ + private $provider; + public function __construct( + \core\ai\AIProvider $provider + ) { + $this->provider = $provider; + $settings = []; + parent::__construct($settings); + $this->setHeader('Authorization: Bearer ' . $this->provider->get('apikey')); + $this->setHeader('Content-Type: application/json'); + } + + public function get_embeddings_url(): string { + return $this->provider->get('baseurl') . $this->provider->get('embeddings'); + } + + public function get_chat_completions_url(): string { + return $this->provider->get('baseurl') . $this->provider->get('completions'); + } + + /** + * @param $messages + * @return array String array of each line of the AI's Response. + * @throws \coding_exception + */ + public function chat($messages) { + $params = [ + "model" => $this->provider->get('completionmodel'), + "messages" => $messages + ]; + $params = json_encode($params); + $rawresult = $this->post($this->get_chat_completions_url(), $params); + $jsonresult = json_decode($rawresult); + if (isset($jsonresult->error)) { + throw new AiException("Error: " . $jsonresult->error->message . ":". print_r($messages, true)); + //return "Error: " . $jsonresult->error->message . ":". print_r($messages, true); + } + $result = []; + if (isset($jsonresult->choices)) { + $result = $this->convert_chat_completion($jsonresult->choices); + if (isset($jsonresult->usage)) { + $this->provider->increment_prompt_usage($jsonresult->usage->prompt_tokens); + $this->provider->increment_completion_tokens($jsonresult->usage->completion_tokens); + $this->provider->increment_total_tokens($jsonresult->usage->total_tokens); + } + } + + return $result; + } + + /** + * Converts an OpenAI Type of response to an array of sentences + * @param $completion + * @return array + */ + protected function convert_chat_completion($choices) { + $responses = []; + foreach($choices as $choice) { + array_push($responses, $choice->message); + } + return $responses; + } + /** + * @param $document + * @return array + */ + public function embed_query($content): array { + // Send document to back end and return the vector + $usedptokens = $this->provider->get_usage('prompt_tokens'); + $totaltokens = $this->provider->get_usage('total_tokens'); + // mtrace("Prompt tokens: $usedptokens. Total tokens: $totaltokens"); + $params = [ + "input" => htmlentities($content), // TODO need to do some length checking here! + "model" => $this->provider->get('embeddingmodel') + ]; + $params = json_encode($params); +// var_dump($this->get_embeddings_url()); + + $rawresult = $this->post($this->get_embeddings_url(), $params); +// var_dump($rawresult); + $result = json_decode($rawresult, true); + // var_dump($result); + $usage = $result['usage']; + $this->provider->increment_prompt_usage($usage['prompt_tokens']); + $this->provider->increment_total_tokens($usage['total_tokens']); + // mtrace("Used Prompt tokens: {$usage['prompt_tokens']}. Total tokens: {$usage['total_tokens']}"); + $data = $result['data']; + foreach($data as $d) { + if ($d['object'] == "embedding") { + return $d['embedding']; + } + } + $usedptokens = $this->provider->get_usage('prompt_tokens'); + $totaltokens = $this->provider->get_usage('total_tokens'); + // mtrace("Total Used: Prompt tokens: $usedptokens. Total tokens: $totaltokens"); + return []; + } + public function embed_documents(array $documents) { + // Go send the documents off to a back end and then return array of each document's vectors. + // But for the minute generate an array of fake vectors of a specific length. + $embeddings = []; + foreach($documents as $doc) { + $embeddings[] = $this->embed_query($doc); + } + return $embeddings; + } + public function fake_embed(array $documents) { + $vectors = []; + foreach ($documents as $document) { + $vectors[] = $this->fake_vector(1356); + } + return $vectors; + } + public function complete($query) { + + + } + private function fake_vector($length) { + $vector = []; + for ($i = 0; $i < $length; $i++) { + $vector[] = rand(0, 1); + } + return $vector; + } + + + +} diff --git a/search/engine/solrrag/classes/ai/aiexception.phx b/search/engine/solrrag/classes/ai/aiexception.phx new file mode 100644 index 000000000000..ab006c952e12 --- /dev/null +++ b/search/engine/solrrag/classes/ai/aiexception.phx @@ -0,0 +1,7 @@ +dirroot . "/search/engine/solrrag/lib.php"); + +use \core\persistent; +use core_course_category; + +class AIProvider extends persistent { +// Ultimately this would extend a persistent. + const CONTEXT_ALL_MY_COURSES = -1; + + protected static function define_properties() + { + return [ + 'name' => [ + 'type' => PARAM_TEXT + ], + 'apikey' =>[ + 'type' => PARAM_ALPHANUMEXT + ], + 'allowembeddings' => [ + 'type' => PARAM_BOOL + ], + 'allowquery' => [ + 'type' => PARAM_BOOL + ], + 'baseurl' => [ + 'type' => PARAM_URL + ], + 'embeddings' => [ + 'type' => PARAM_URL + ], + 'embeddingmodel' => [ + 'type' => PARAM_ALPHANUMEXT + ], + 'completions' => [ + 'type' => PARAM_URL + ], + 'completionmodel' => [ + 'type' => PARAM_ALPHANUMEXT + ], + // What context is this provider attached to. + // If null, it's a global provider. + // If -1 its limited to user's own courses. + 'context' => [ + 'type' => PARAM_INT + ], + // If true, only courses that the user is enrolled in will be searched. + 'onlyenrolledcourses' => [ + 'type' => PARAM_BOOL + ], + ]; + } + + public function use_for_embeddings(): bool { + return $this->get('allowembeddings'); + } + + public function use_for_query():bool { + return $this->get('allowquery'); + } + public function get_usage($type) { + return "-"; + $key = [ + '$type', + $this->get('id'), + $this->get('apikey'), + ]; + $current = get_config('ai', $key); + return $current; + } + public function increment_prompt_usage($change) { + return; + $key = [ + 'prompttokens', + $this->get('id'), + $this->get('apikey'), + ]; + $key = implode("_", $key); + $current = get_config('ai', $key); + $new = $current + $change; + set_config($key, $new, 'ai'); + } + public function increment_completion_tokens($change) { + return; + $key = [ + 'completiontokens', + $this->get('id'), + $this->get('apikey'), + ]; + $key = implode("_", $key); + $current = get_config('ai', $key); + $new = $current + $change; + set_config($key, $new, 'ai'); + } + public function increment_total_tokens($change) { + return; + $key = [ + 'totaltokens', + $this->get('id'), + $this->get('apikey'), + ]; + $key = implode("_", $key); + $current = get_config('ai', $key); + $new = $current + $change; + set_config($key, $new, 'ai'); + } + + /** + * Returns appropriate search settings based on + * provider configuration. + */ + public function get_settings() { + // `userquery` and `vector` will be filled at run time. + $settings = [ + 'userquery'=> null, + 'vector' => null, + // `similarity` is a boolean that determines if the query should use vector similarity search. + 'similarity' => true, + 'areaids' => [], + // `excludeareaids` is an array of areaids that should be excluded from the search. + 'excludeareaids'=> ["core_user-user"], // <-- This may be should be in control of the AI Provider. + 'courseids' => [], // This of course IDs that result should be limited to. + ]; + return $settings; + } + + /** + * Gets user specific settings. + * + * This takes on some of the function that the manager code did. + */ + public function get_settings_for_user($user) { + $usersettings = $this->get_settings(); + + // This is basically manager::build_limitcourseids(). + $mycourseids = enrol_get_my_courses(array('id', 'cacherev'), 'id', 0, [], false); + $onlyenrolledcourses = $this->get('onlyenrolledcourses'); + $courseids = []; + if ($this->get('context') == self::CONTEXT_ALL_MY_COURSES) { + $courseids = array_keys($mycourseids); + } else { + $context = \context::instance_by_id($this->get('context')); + if ($context->contextlevel == CONTEXT_COURSE) { + // Check that the specific course is also in the user's list of courses. + $courseids = array_intersect([$context->instanceid], $mycourseids); + } else if ($context->contextlevel == CONTEXT_COURSECAT) { + // CourseIDs will be all courses in the category, + // optionally that the user is enrolled in + $category = core_course_category::get($context->instanceid); + $categorycourseids = $category->get_courses([ + 'recursive'=>true, + 'idonly' => true + ]); + } else if ($context->contextlevel == CONTEXT_SYSTEM) { + // No restrictions anywhere. + } + } + $usersettings['courseids'] = $courseids; + + return $usersettings; + } + + //public function + // TODO token counting. + /** + * We're overriding this whilst we don't have a real DB table. + * @param $filters + * @param $sort + * @param $order + * @param $skip + * @param $limit + * @return array + */ + public static function get_records($filters = array(), $sort = '', $order = 'ASC', $skip = 0, $limit = 0) { + global $_ENV; + $records = []; + $fake = new static(0, (object) [ + 'id' => 1, + 'name' => "Fake Open AI Provider", + 'allowembeddings' => true, + 'allowquery' => true, + 'baseurl' => 'https://api.openai.com/v1/', + 'embeddings' => 'embeddings', + 'embeddingmodel' => 'text-embedding-3-small', + 'completions' => 'chat/completions', + 'completionmodel' => 'gpt-4-turbo-preview', + 'apikey'=> $_ENV['OPENAIKEY'], + 'context' => \context_system::instance()->id, + //null, // Global AI Provider + 'onlyenrolledcourses' => true + ]); + array_push($records, $fake); + $fake = new static(0, (object) [ + 'id' => 2, + 'name' => "Ollama AI Provider", + 'allowembeddings' => true, + 'allowquery' => true, + 'baseurl' => 'http://127.0.0.1:11434/api/', + 'embeddings' => 'embeddings', + 'embeddingmodel' => '', + 'completions' => 'chat', + 'completionmodel' => 'llama2', + 'context' => null, // Global AI Provider + 'onlyenrolledcourses' => true + // 'apikey'=> $_ENV['OPENAIKEY'] + ]); + array_push($records, $fake); + return $records; + } + +} diff --git a/search/engine/solrrag/classes/ai/api.phx b/search/engine/solrrag/classes/ai/api.phx new file mode 100644 index 000000000000..be8c4d627b68 --- /dev/null +++ b/search/engine/solrrag/classes/ai/api.phx @@ -0,0 +1,24 @@ +dirroot . "/search/engine/solrrag/lib.php"); + +class api { + + /** + * Return a list of AIProviders that are available for specified context. + * @param $context + * @return array + */ + public static function get_all_providers($context = null) { + return array_values(AIProvider::get_records()); + } + public static function get_provider(int $id): AIProvider { + $fakes = AIProvider::get_records(); + return $fakes[0]; // Open AI + // return $fakes[1]; // Ollama + + } +} diff --git a/search/engine/solrrag/classes/engine.php b/search/engine/solrrag/classes/engine.php index 10a5cbcbf240..043102731605 100644 --- a/search/engine/solrrag/classes/engine.php +++ b/search/engine/solrrag/classes/engine.php @@ -4,7 +4,7 @@ use search_solrrag\document; use search_solrrag\schema; -require_once($CFG->dirroot . "/search/engine/solrrag/lib.php"); +//require_once($CFG->dirroot . "/search/engine/solrrag/lib.php"); // // Fudge autoloading! // require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/api.php"); // require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/aiprovider.php"); diff --git a/search/engine/solrrag/lib.php b/search/engine/solrrag/lib.php index 46f51c2d8e66..128c498db238 100644 --- a/search/engine/solrrag/lib.php +++ b/search/engine/solrrag/lib.php @@ -1,6 +1,6 @@ dirroot ."/search/engine/solrrag/classes/ai/api.php"); -require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/aiprovider.php"); -require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/aiclient.php"); -require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/aiexception.php"); \ No newline at end of file +//require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/api.php"); +//require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/aiprovider.php"); +//require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/aiclient.php"); +//require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/aiexception.php"); From 60221ec99984faabdb539507520ecd092b138f5e Mon Sep 17 00:00:00 2001 From: Michael hughes Date: Thu, 21 Mar 2024 23:14:40 +0000 Subject: [PATCH 14/21] core_ai providers management page --- {lib/classes/ai => ai/classes}/aiclient.php | 6 +- .../classes/ai => ai/classes}/aiexception.php | 5 +- {lib/classes/ai => ai/classes}/aiprovider.php | 94 +++++++++++++-- ai/classes/api.php | 55 +++++++++ ai/classes/output/index_page.php | 108 ++++++++++++++++++ ai/classes/renderer.php | 18 +++ ai/index.php | 68 +++++++++++ ai/lang/en/ai.php | 0 ai/lib.php | 49 ++++++++ ai/templates/index_page.mustache | 6 + course/moodleform_mod.php | 44 ++++++- lang/en/ai.php | 44 +++++++ lib/classes/ai/api.php | 24 ---- lib/components.json | 1 + lib/db/access.php | 24 ++++ lib/navigationlib.php | 28 ++++- lib/outputfactories.php | 1 - mod/xaichat/mod_form.php | 5 +- pix/no.svg | 3 + pix/yes.svg | 3 + version.php | 1 + 21 files changed, 537 insertions(+), 50 deletions(-) rename {lib/classes/ai => ai/classes}/aiclient.php (98%) rename {lib/classes/ai => ai/classes}/aiexception.php (72%) rename {lib/classes/ai => ai/classes}/aiprovider.php (64%) create mode 100644 ai/classes/api.php create mode 100644 ai/classes/output/index_page.php create mode 100644 ai/classes/renderer.php create mode 100644 ai/index.php create mode 100644 ai/lang/en/ai.php create mode 100644 ai/lib.php create mode 100644 ai/templates/index_page.mustache create mode 100644 lang/en/ai.php delete mode 100644 lib/classes/ai/api.php create mode 100644 pix/no.svg create mode 100644 pix/yes.svg diff --git a/lib/classes/ai/aiclient.php b/ai/classes/aiclient.php similarity index 98% rename from lib/classes/ai/aiclient.php rename to ai/classes/aiclient.php index 086c8d3a9264..21cb025a6809 100644 --- a/lib/classes/ai/aiclient.php +++ b/ai/classes/aiclient.php @@ -1,7 +1,7 @@ libdir.'/filelib.php'); -use core\ai\AiException; +use core_ai\AiException; /** * Base client for AI providers that uses simple http request. */ @@ -11,7 +11,7 @@ class AIClient extends \curl { */ private $provider; public function __construct( - \core\ai\AIProvider $provider + AIProvider $provider ) { $this->provider = $provider; $settings = []; diff --git a/lib/classes/ai/aiexception.php b/ai/classes/aiexception.php similarity index 72% rename from lib/classes/ai/aiexception.php rename to ai/classes/aiexception.php index ab006c952e12..43bf00cb3b82 100644 --- a/lib/classes/ai/aiexception.php +++ b/ai/classes/aiexception.php @@ -1,7 +1,6 @@ dirroot . "/search/engine/solrrag/lib.php"); +namespace core_ai; use \core\persistent; use core_course_category; @@ -16,13 +15,16 @@ protected static function define_properties() 'name' => [ 'type' => PARAM_TEXT ], + 'enabled' => [ + 'type' => PARAM_BOOL + ], 'apikey' =>[ 'type' => PARAM_ALPHANUMEXT ], 'allowembeddings' => [ 'type' => PARAM_BOOL ], - 'allowquery' => [ + 'allowchat' => [ 'type' => PARAM_BOOL ], 'baseurl' => [ @@ -43,7 +45,7 @@ protected static function define_properties() // What context is this provider attached to. // If null, it's a global provider. // If -1 its limited to user's own courses. - 'context' => [ + 'contextid' => [ 'type' => PARAM_INT ], // If true, only courses that the user is enrolled in will be searched. @@ -53,6 +55,14 @@ protected static function define_properties() ]; } + /** + * Work out the context path from the site to this AI Provider's context + * @return void + */ + public function get_context_path() { + $context = \context::instance_by_id($this->get('contextid')); + var_dump($context); + } public function use_for_embeddings(): bool { return $this->get('allowembeddings'); } @@ -173,40 +183,102 @@ public function get_settings_for_user($user) { * @param $limit * @return array */ - public static function get_records($filters = array(), $sort = '', $order = 'ASC', $skip = 0, $limit = 0) { + public static function get_records($filters = [], $sort = '', $order = 'ASC', $skip = 0, $limit = 0) { global $_ENV; + $records = []; $fake = new static(0, (object) [ 'id' => 1, - 'name' => "Fake Open AI Provider", + 'name' => "Open AI Provider (hardcoded)", + 'enabled' => true, 'allowembeddings' => true, - 'allowquery' => true, + 'allowchat' => true, 'baseurl' => 'https://api.openai.com/v1/', 'embeddings' => 'embeddings', 'embeddingmodel' => 'text-embedding-3-small', 'completions' => 'chat/completions', 'completionmodel' => 'gpt-4-turbo-preview', 'apikey'=> $_ENV['OPENAIKEY'], - 'context' => \context_system::instance()->id, + 'contextid' => \context_system::instance()->id, //null, // Global AI Provider 'onlyenrolledcourses' => true ]); array_push($records, $fake); $fake = new static(0, (object) [ 'id' => 2, - 'name' => "Ollama AI Provider", + 'name' => "Ollama AI Provider (hard coded)", + 'enabled' => true, 'allowembeddings' => true, - 'allowquery' => true, + 'allowchat' => true, 'baseurl' => 'http://127.0.0.1:11434/api/', 'embeddings' => 'embeddings', 'embeddingmodel' => '', 'completions' => 'chat', 'completionmodel' => 'llama2', - 'context' => null, // Global AI Provider + 'contextid' => null, // Global AI Provider 'onlyenrolledcourses' => true // 'apikey'=> $_ENV['OPENAIKEY'] ]); array_push($records, $fake); + $fake = new static(0, (object) [ + 'id' => 3, + 'name' => "Ollama AI Provider (hard coded) Misc Category only", + 'enabled' => true, + 'allowembeddings' => true, + 'allowchat' => true, + 'baseurl' => 'http://127.0.0.1:11434/api/', + 'embeddings' => 'embeddings', + 'embeddingmodel' => '', + 'completions' => 'chat', + 'completionmodel' => 'llama2', + 'contextid' => 111, // Global AI Provider + 'onlyenrolledcourses' => true, + // 'apikey'=> $_ENV['OPENAIKEY'] + ]); + array_push($records, $fake); + + $targetcontextid = $filters['contextid'] ?? null; + $targetcontext = null; + if (is_null($targetcontextid)) { +// debugging("No Context Restriction", DEBUG_DEVELOPER); + unset($filters['contextid']); // Because we need special handling. + } else { +// debugging("Context Restriction: {$targetcontextid}", DEBUG_DEVELOPER); + $targetcontext = \context::instance_by_id($targetcontextid); + } +// debugging(print_r($filters,1), DEBUG_DEVELOPER); + $records = array_filter($records, function($record) use ($filters, $targetcontext) { + $result = true; + foreach($filters as $key => $value) { + if ($key == "contextid") { + $providercontextid = $record->get('contextid'); + if ($providercontextid == self::CONTEXT_ALL_MY_COURSES) { + // More problematic. + debugging('Provider needs to be in one of user\'s courses', DEBUG_DEVELOPER); + $result = $result & true; + } else if ($providercontextid == null) { + // System provider so always matches. + debugging("System AI provider", DEBUG_DEVELOPER); + $result = $result & true; + } else { + debugging("Context linked AI provider", DEBUG_DEVELOPER); + $providercontext = \context::instance_by_id( + $providercontextid + ); + $ischild = $targetcontext->is_child_of($providercontext, true); + debugging("IS child ". (int)$ischild, DEBUG_DEVELOPER); + $result = $result & $ischild; + } + }else { + debugging('Filtering on '.$key. "' = {$value}", DEBUG_DEVELOPER); + if ($record->get($key) != $value) { + return false; + } + } + } + return $result; + }); + return $records; } diff --git a/ai/classes/api.php b/ai/classes/api.php new file mode 100644 index 000000000000..242320d64480 --- /dev/null +++ b/ai/classes/api.php @@ -0,0 +1,55 @@ +providers = $providers; + } + + public function providers_table($providers) { + global $CFG; + $table = new html_table(); + $table->head = [ + 'name', + 'context', + 'completion', + 'embeddings', + 'status', + 'edit' + ]; + + $table->attributes['class'] = 'admintable generaltable'; + $data = []; + $contextcache = []; + $index = 0; + foreach ($providers as $provider) { + $first = false; + if ($index == 0) { + $first = true; + } + $last = false; + if ($index == count($providers) - 1) { + $last = true; + } + + $name = $provider->get('name'); + $contextid = $provider->get('contextid'); + $context = ""; + if($contextid >0) { + if ( + !isset($contextcache[$contextid]) + ) { + $contextcache[$contextid] = context::instance_by_id($contextid); + } + $contextinstance = $contextcache[$contextid]; + $context = $contextinstance->get_context_name(); + } else if ($contextid < 0){ + $context = "User's own courses"; + } else { + $context = "System"; + } + $completion = $provider->get('allowchat'); + $embeddings = $provider->get('embeddings'); + $status = $provider->get('enabled'); + + // Set up cells. + $namecell = new html_table_cell($name); + $namecell->header = true; + + $contextcell = new html_table_cell($context); + $contextcell->header = true; + + $completioncell = new html_table_cell( + $completion + ?"yes"//$this->pix_icon('yes', get_string('enabled','ai')) + :"no"//$this->pix_icon('no', get_string('disabled','ai')) + ); + $completioncell->header = true; + + $embeddingscell = new html_table_cell( + $embeddings + ?"yes"//$this->pix_icon('yes', get_string('enabled','ai'), 'ai') + :"no"//$this->pix_icon('no', get_string('disabled','ai'), 'ai') + ); + $embeddingscell->header = true; + + $statuscell = new html_table_cell($status); + $statuscell->header = true; + + $links = ""; + // Action links. + $editurl = new moodle_url($CFG->wwwroot . '/ai/index.php', + [ + 'action' => api::ACTION_EDIT_PROVIDER, + 'pid' => $provider->get('id') + ]); + $links .= html_writer::link($editurl, 'Edit'); + $editcell = new html_table_cell($links); + $row = new html_table_row([ + $namecell, + $contextcell, + $completioncell, + $embeddingscell, + $statuscell, + $editcell + ]); + $data[] = $row; + } + $table->data = $data; + return html_writer::table($table); + } + public function export_for_template(renderer_base $output) : stdClass{ + $data = (object)[ + 'providers' => array_values($this->providers_table($this->providers)) + ]; + return $data; + } +} diff --git a/ai/classes/renderer.php b/ai/classes/renderer.php new file mode 100644 index 000000000000..f1635a088b3b --- /dev/null +++ b/ai/classes/renderer.php @@ -0,0 +1,18 @@ +export_for_template($this); + return parent::render_from_template('core_ai/index_page', $data); + } + +} diff --git a/ai/index.php b/ai/index.php new file mode 100644 index 000000000000..1fbd7deca4e2 --- /dev/null +++ b/ai/index.php @@ -0,0 +1,68 @@ +libdir.'/adminlib.php'); +require_once($CFG->libdir.'/tablelib.php'); + +use core_ai\api; + +$PAGE->set_url('/ai/index.php'); +$PAGE->set_context(context_system::instance()); +$PAGE->set_pagelayout("admin"); + +$strheading = get_string('aiprovider', 'ai'); +$PAGE->set_heading($strheading); +$PAGE->set_title($strheading); + +$renderer = $PAGE->get_renderer('core', 'ai'); + +$action = optional_param('action', '', PARAM_ALPHAEXT); +// We're using pid as "id" is used to specify contextids. +$providerid = optional_param('pid', '', PARAM_RAW); + +$provider = null; +$mform = null; + +if ($providerid) { + $provider = core\ai\api::get_provider($providerid); + if (!$provider) { + throw new moodle_exception('invaliddata'); + } +} + +if ($action == api::ACTION_EDIT_PROVIDER) { + if ($provider) { + // Edit + } else { + // Create new + } + $mform = new \core\ai\form\provider(null, ['persistent' => $provider]); +} + + +if ($mform && $mform->is_cancelled()) { + redirect(new moodle_url('/admin/tool/oauth2/issuers.php')); +} else if ($action == api::ACTION_EDIT_PROVIDER) { + // Handle edit. + if ($data = $mform->get_data()) { + + } else { + echo $OUTPUT->header(); + $mform->display(); + echo $OUTPUT->footer(); + } +} else if ($action == api::ACTION_REMOVE_PROVIDER) { + // Handle remove. +} else { + // Display list of providers. + $providers = api::get_providers(); + echo $OUTPUT->header(); + echo $OUTPUT->heading(get_string('pluginname', 'ai')); + echo $renderer->providers_table($providers); + echo $OUTPUT->footer(); +} diff --git a/ai/lang/en/ai.php b/ai/lang/en/ai.php new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/ai/lib.php b/ai/lib.php new file mode 100644 index 000000000000..9b618d17a0af --- /dev/null +++ b/ai/lib.php @@ -0,0 +1,49 @@ +add( + get_string('addprovider', 'ai'), + new moodle_url( + '/ai/index.php', + [ + 'id' => $catcontext->id, + 'action' => api::ACTION_ADD_PROVIDER + ]), + navigation_node::TYPE_SETTING + ); + } +} + +/** + * Extend Course Navigation with option to create AI providers within a + * course. + * @param $coursenode + * @param $coursecontext + * @return void + * @throws coding_exception + * @throws moodle_exception + */ +function extend_navigation_course_settings($coursenode, $coursecontext) { + if (has_capability('moodle/addprovider', $coursecontext)) { + $coursenode->add( + get_string('addaiprovider', 'core_ai'), + new moodle_url( + '/ai/index.php', + [ + 'id' => $coursecontext->id, + 'action' => api::ACTION_ADD_PROVIDER + ]), + navigation_node::TYPE_SETTING + ); + } +} diff --git a/ai/templates/index_page.mustache b/ai/templates/index_page.mustache new file mode 100644 index 000000000000..6952ed21f691 --- /dev/null +++ b/ai/templates/index_page.mustache @@ -0,0 +1,6 @@ +
+ {{{issuers_table}}} +
+
+ {{#str}}createnewprovider, ai{{/str}} +
diff --git a/course/moodleform_mod.php b/course/moodleform_mod.php index e593eaec2b5b..c0de9974226e 100644 --- a/course/moodleform_mod.php +++ b/course/moodleform_mod.php @@ -210,6 +210,7 @@ protected function init_features() { $this->_features->advancedgrading = plugin_supports('mod', $this->_modname, FEATURE_ADVANCED_GRADING, false); $this->_features->hasnoview = plugin_supports('mod', $this->_modname, FEATURE_NO_VIEW_LINK, false); $this->_features->canrescale = (component_callback_exists('mod_' . $this->_modname, 'rescale_activity_grades') !== false); + $this->_features->ai = plugin_supports('mod', $this->_modname, FEATURE_AI, false); } /** @@ -1196,7 +1197,17 @@ public function get_data() { return $data; } - public function standard_aiprovider_coursemodule_elements() { + /** + * Display AI Provider configuration section. + * @param $allowdisable If the plugin can function without AI, then it's edit form can offer a "disabled" option. + * @return void + * @throws coding_exception + */ + public function standard_aiprovider_coursemodule_elements( + $allowchat = false, + $allowembedding = false, + $allowdisable = false + ) { if (!$this->_features->ai) { return; @@ -1204,10 +1215,34 @@ public function standard_aiprovider_coursemodule_elements() { $mform = $this->_form; // Adding the rest of mod_xaichat settings, spreading all them into this fieldset // ... or adding more fieldsets ('header' elements) if needed for better logic. - $mform->addElement('header', 'aiprovider', get_string('aiprovider')); + $mform->addElement('header', 'aiprovider', get_string('aiprovider', 'ai')); + + $mform->addElement('static', "aiproviderfeatures", + get_string("aiproviderfeatures", 'ai'), + get_string("aiproviderfeatures_desc", 'ai') + ); + $contextconstraint = "Context id {$this->context->id} or above"; + $mform->addElement('static', 'aiprovider_contextconstraint', get_string('context'), + $contextconstraint + ); + $mform->addElement( + 'static', 'aiprovider_chat', get_string('chat', 'ai'), + $allowchat ? get_string("yes") : get_string("no") + ); + + $mform->addElement('static', 'aiprovider_embedding', get_string('embedding', 'ai'), + $allowembedding ? get_string("yes") : get_string("no") + ); - $providers = \core\ai\api::get_all_providers(); + $providers = \core_ai\api::get_providers( + $this->context->id, + $allowchat, + $allowembedding + ); $optproviders = []; + if ($allowdisable) { + $optproviders[''] = get_string('disable', 'ai'); + } foreach($providers as $provider) { $optproviders[$provider->get('id')] = $provider->get('name'); } @@ -1215,5 +1250,8 @@ public function standard_aiprovider_coursemodule_elements() { 'Choose Provider', $optproviders ); + + // TODO Display provider settings that can be set at this context. + } } diff --git a/lang/en/ai.php b/lang/en/ai.php new file mode 100644 index 000000000000..5f639e153994 --- /dev/null +++ b/lang/en/ai.php @@ -0,0 +1,44 @@ +. + +/** +* Strings for component 'ai', language 'en', branch 'MOODLE_0_STABLE' +* +* @package core_ai +* @copyright 2024 onwards Michael Hughes {@link http://moodle.com} +* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later +*/ +$string['pluginname'] = 'AI Providers'; +$string['aiprovider'] = 'AI Provider'; +$string['addprovider'] = 'Add AI Provider'; +$string['enabled'] = 'Enabled'; +$string['disabled'] = 'Disabled'; +$string['removeprovider'] = 'Remove AI Provider'; +$string['manageproviders'] = 'Manage AI Providers'; +$string['availableproviders'] = 'Available Providers'; +$string['availableproviders_help'] = 'This is a list of configured AI Providers that meet the requirements of the activity. + +Activity developers specify which AI features their plugin requires, and an administrator must configure +an AI Provider instance, and make it available at a context level that the plugin can access. + +System level AI Providers that meet the required features will always be listed. +'; +$string['chat'] = 'Chat Completion'; +$string['chat_help'] = 'Chat Completion allows the AIProvider to be used to generate text.'; +$string['embedding'] = 'Embedding'; +$string['embedding_help'] = 'Embedding allows the AI to generate vector representations of text.'; +$string['aiproviderfeatures'] = ''; +$string['aiproviderfeatures_desc'] = 'This plugin needs the following AI features'; diff --git a/lib/classes/ai/api.php b/lib/classes/ai/api.php deleted file mode 100644 index be8c4d627b68..000000000000 --- a/lib/classes/ai/api.php +++ /dev/null @@ -1,24 +0,0 @@ -dirroot . "/search/engine/solrrag/lib.php"); - -class api { - - /** - * Return a list of AIProviders that are available for specified context. - * @param $context - * @return array - */ - public static function get_all_providers($context = null) { - return array_values(AIProvider::get_records()); - } - public static function get_provider(int $id): AIProvider { - $fakes = AIProvider::get_records(); - return $fakes[0]; // Open AI - // return $fakes[1]; // Ollama - - } -} diff --git a/lib/components.json b/lib/components.json index 651654cc583b..eb035657512b 100644 --- a/lib/components.json +++ b/lib/components.json @@ -44,6 +44,7 @@ "paygw": "payment\/gateway" }, "subsystems": { + "ai": "ai", "access": null, "admin": "admin", "adminpresets": "admin/presets", diff --git a/lib/db/access.php b/lib/db/access.php index 69b687aef83a..96f981434419 100644 --- a/lib/db/access.php +++ b/lib/db/access.php @@ -2748,4 +2748,28 @@ 'manager' => CAP_ALLOW, ] ], + // Allow user to add AI Provider to contexts. + 'moodle/ai:addprovider' => [ + 'captype' => 'write', + 'contextlevel' => CONTEXT_COURSECAT, + 'archetypes' => [ + 'manager' => CAP_ALLOW, + ] + ], + // Allow user to remove AI Provider from contexts. + 'moodle/ai:removeprovider' => [ + 'captype' => 'write', + 'contextlevel' => CONTEXT_COURSECAT, + 'archetypes' => [ + 'manager' => CAP_ALLOW, + ] + ], + // Allow user to modify AI Providers in contexts. + 'moodle/ai:manageproviders' => [ + 'captype' => 'write', + 'contextlevel' => CONTEXT_COURSECAT, + 'archetypes' => [ + 'manager' => CAP_ALLOW, + ] + ], ); diff --git a/lib/navigationlib.php b/lib/navigationlib.php index f56a6ef65c73..45e63bfb6d94 100644 --- a/lib/navigationlib.php +++ b/lib/navigationlib.php @@ -25,6 +25,7 @@ use core\moodlenet\utilities; use core_contentbank\contentbank; +use core\ai\api; defined('MOODLE_INTERNAL') || die(); @@ -4847,7 +4848,19 @@ protected function load_course_settings($forceopen = false) { $coursereusenav->add(get_string('reset'), $url, self::TYPE_SETTING, null, 'reset', new pix_icon('i/return', '')); } } - + // Core AI Function + if (has_capability('moodle/ai:manageproviders', $coursecontext)) { + $coursenode->add( + get_string('manageproviders', 'ai'), + new moodle_url( + '/ai/index.php', + [ + 'id' => $coursecontext->id, + 'action' => \core_ai\api::ACTION_MANAGE_PROVIDERS + ]), + navigation_node::TYPE_SETTING + ); + } // Return we are done return $coursenode; } @@ -5618,6 +5631,19 @@ protected function load_category_settings() { $categorynode->add(get_string('restorecourse', 'admin'), $url, self::TYPE_SETTING, null, 'restorecourse', new pix_icon('i/restore', '')); } + // AI + if (has_capability('moodle/ai:manageproviders', $catcontext)) { + $categorynode->add( + get_string('manageproviders', 'core_ai'), + new moodle_url( + '/ai/index.php', + [ + 'id' => $catcontext->id, + 'action' => api::ACTION_MANAGE_PROVIDERS + ]), + navigation_node::TYPE_SETTING + ); + } // Let plugins hook into category settings navigation. $pluginsfunction = get_plugins_with_function('extend_navigation_category_settings', 'lib.php'); foreach ($pluginsfunction as $plugintype => $plugins) { diff --git a/lib/outputfactories.php b/lib/outputfactories.php index aa28a9e69019..03b2c889e0f0 100644 --- a/lib/outputfactories.php +++ b/lib/outputfactories.php @@ -387,7 +387,6 @@ public function __construct(theme_config $theme) { */ public function get_renderer(moodle_page $page, $component, $subtype = null, $target = null) { $classnames = $this->standard_renderer_classnames($component, $subtype); - list($target, $suffix) = $this->get_target_suffix($target); // Theme lib.php and renderers.php files are loaded automatically diff --git a/mod/xaichat/mod_form.php b/mod/xaichat/mod_form.php index 4de0909adf93..c4fce3e8e1e9 100644 --- a/mod/xaichat/mod_form.php +++ b/mod/xaichat/mod_form.php @@ -25,9 +25,6 @@ defined('MOODLE_INTERNAL') || die(); require_once($CFG->dirroot.'/course/moodleform_mod.php'); -require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/api.php"); -require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/aiprovider.php"); -require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/aiclient.php"); /** * Module instance settings form. @@ -69,7 +66,7 @@ public function definition() { $this->add_intro_editor(); } - $this->standard_aiprovider_coursemodule_elements(); + $this->standard_aiprovider_coursemodule_elements(true, true); // Add standard grading elements. $this->standard_grading_coursemodule_elements(); diff --git a/pix/no.svg b/pix/no.svg new file mode 100644 index 000000000000..0185d868b95a --- /dev/null +++ b/pix/no.svg @@ -0,0 +1,3 @@ + +]> \ No newline at end of file diff --git a/pix/yes.svg b/pix/yes.svg new file mode 100644 index 000000000000..714d4c73c796 --- /dev/null +++ b/pix/yes.svg @@ -0,0 +1,3 @@ + +]> \ No newline at end of file diff --git a/version.php b/version.php index 8ccfeb3a1d71..ea35f11db131 100644 --- a/version.php +++ b/version.php @@ -29,6 +29,7 @@ defined('MOODLE_INTERNAL') || die(); + $version = 2024032000.00; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes. From 5f4e6b94be379e3a96ec9f578d1a9ef82f1721f7 Mon Sep 17 00:00:00 2001 From: Michael hughes Date: Thu, 21 Mar 2024 23:51:09 +0000 Subject: [PATCH 15/21] edit form --- ai/classes/aiprovider.php | 4 ++- ai/classes/form/aiprovider.php | 41 +++++++++++++++++++++ ai/classes/output/index_page.php | 62 +++++++++++++++++++++----------- ai/classes/renderer.php | 3 +- ai/index.php | 11 ++++-- ai/templates/index_page.mustache | 7 ++-- 6 files changed, 101 insertions(+), 27 deletions(-) create mode 100644 ai/classes/form/aiprovider.php diff --git a/ai/classes/aiprovider.php b/ai/classes/aiprovider.php index af0c9dca23e6..7dc88f908a9f 100644 --- a/ai/classes/aiprovider.php +++ b/ai/classes/aiprovider.php @@ -2,11 +2,12 @@ // We're mocking a core Moodle "AI" Subsystem a la Oauth 2 namespace core_ai; -use \core\persistent; +use core\persistent; use core_course_category; class AIProvider extends persistent { // Ultimately this would extend a persistent. + const CONTEXT_ALL_MY_COURSES = -1; protected static function define_properties() @@ -55,6 +56,7 @@ protected static function define_properties() ]; } + /** * Work out the context path from the site to this AI Provider's context * @return void diff --git a/ai/classes/form/aiprovider.php b/ai/classes/form/aiprovider.php new file mode 100644 index 000000000000..fc98cba0a2a9 --- /dev/null +++ b/ai/classes/form/aiprovider.php @@ -0,0 +1,41 @@ +type = $customdata['type']; + } + + parent::__construct($action, $customdata, $method, $target, $attributes, $editable, $ajaxformdata); + } + public function definition() { + global $PAGE, $OUTPUT; + + $mform = $this->_form; + $provider = $this->get_persistent(); + $mform->addElement('html','intro', 'hello'); + + // Name. + $mform->addElement('text', 'name', get_string('providername', 'ai')); + $mform->addRule('name', null, 'required', null, 'client'); + $mform->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'client'); + $mform->addHelpButton('name', 'providername', 'ai'); + + // Client Secret. + $mform->addElement('text', 'apikey', get_string('apikey', 'ai')); + $mform->addRule('apikey', get_string('maximumchars', '', 255), 'maxlength', 255, 'client'); + $mform->addHelpButton('apikey', 'apikey', 'ai'); + + $mform->addElement('header', 'features', get_string('features', 'ai')); + $mform->addElement('advcheckbox', 'allowchat', get_string('allowchat', 'ai')); + $mform->addHelpButton('allowchat', 'allowchat', 'ai'); + + $this->add_action_buttons(true, get_string('savechanges', 'ai')); + } +} diff --git a/ai/classes/output/index_page.php b/ai/classes/output/index_page.php index 110ea56d1149..6c3caf61d989 100644 --- a/ai/classes/output/index_page.php +++ b/ai/classes/output/index_page.php @@ -1,6 +1,8 @@ head = [ 'name', 'context', @@ -40,7 +42,7 @@ public function providers_table($providers) { if ( !isset($contextcache[$contextid]) ) { - $contextcache[$contextid] = context::instance_by_id($contextid); + $contextcache[$contextid] = \context::instance_by_id($contextid); } $contextinstance = $contextcache[$contextid]; $context = $contextinstance->get_context_name(); @@ -54,39 +56,36 @@ public function providers_table($providers) { $status = $provider->get('enabled'); // Set up cells. - $namecell = new html_table_cell($name); + $namecell = new \html_table_cell($name); $namecell->header = true; - $contextcell = new html_table_cell($context); - $contextcell->header = true; + $contextcell = new \html_table_cell($context); - $completioncell = new html_table_cell( + $completioncell = new \html_table_cell( $completion ?"yes"//$this->pix_icon('yes', get_string('enabled','ai')) :"no"//$this->pix_icon('no', get_string('disabled','ai')) ); - $completioncell->header = true; - $embeddingscell = new html_table_cell( + $embeddingscell = new \html_table_cell( $embeddings ?"yes"//$this->pix_icon('yes', get_string('enabled','ai'), 'ai') :"no"//$this->pix_icon('no', get_string('disabled','ai'), 'ai') ); - $embeddingscell->header = true; - $statuscell = new html_table_cell($status); + $statuscell = new \html_table_cell($status); $statuscell->header = true; $links = ""; // Action links. - $editurl = new moodle_url($CFG->wwwroot . '/ai/index.php', + $editurl = new \moodle_url($CFG->wwwroot . '/ai/index.php', [ 'action' => api::ACTION_EDIT_PROVIDER, 'pid' => $provider->get('id') ]); - $links .= html_writer::link($editurl, 'Edit'); - $editcell = new html_table_cell($links); - $row = new html_table_row([ + $links .= \html_writer::link($editurl, 'Edit'); + $editcell = new \html_table_cell($links); + $row = new \html_table_row([ $namecell, $contextcell, $completioncell, @@ -97,12 +96,35 @@ public function providers_table($providers) { $data[] = $row; } $table->data = $data; - return html_writer::table($table); + return \html_writer::table($table); } - public function export_for_template(renderer_base $output) : stdClass{ - $data = (object)[ - 'providers' => array_values($this->providers_table($this->providers)) + + private $providertypes = ['OpenAI API']; + protected function template_buttons() { + global $CFG; + $buttons = []; + foreach($this->providertypes as $type) { + $addurl = new \moodle_url($CFG->wwwroot . '/ai/index.php', + [ + 'action' => api::ACTION_EDIT_PROVIDER, + 'type' => $type, + 'pid' => null + ] + ); + $buttontext = get_string('newprovider', 'ai', $type); + $buttons[] = [$addurl, $buttontext]; + } + return $buttons; + } + public function export_for_template(\renderer_base $output) : object{ + $buttondefs = $this->template_buttons(); + $buttons = array_map(function($button) use ($output) { + return $output->single_button($button[0], $button[1]); + }, $buttondefs); + $data = [ + 'providers_table' => $this->providers_table($this->providers), + 'templates' => $buttons ]; - return $data; + return (object)$data; } } diff --git a/ai/classes/renderer.php b/ai/classes/renderer.php index f1635a088b3b..dc818cf1668c 100644 --- a/ai/classes/renderer.php +++ b/ai/classes/renderer.php @@ -8,9 +8,10 @@ //use \html_table_row; //use html_writer; //use moodle_url; +use core_ai\output\index_page; class core_ai_renderer extends \plugin_renderer_base { - public function render_index_page($indexpage) { + public function render_index_page(index_page $indexpage) { $data = $indexpage->export_for_template($this); return parent::render_from_template('core_ai/index_page', $data); } diff --git a/ai/index.php b/ai/index.php index 1fbd7deca4e2..3f131419b7d4 100644 --- a/ai/index.php +++ b/ai/index.php @@ -41,7 +41,10 @@ } else { // Create new } - $mform = new \core\ai\form\provider(null, ['persistent' => $provider]); + $mform = new \core_ai\form\aiprovider(null, [ + 'persistent' => $provider, + 'type' => required_param('type', PARAM_RAW) + ]); } @@ -60,9 +63,11 @@ // Handle remove. } else { // Display list of providers. - $providers = api::get_providers(); + $indexpage = new \core_ai\output\index_page( + api::get_providers() + ); echo $OUTPUT->header(); echo $OUTPUT->heading(get_string('pluginname', 'ai')); - echo $renderer->providers_table($providers); + echo $renderer->render_index_page($indexpage); echo $OUTPUT->footer(); } diff --git a/ai/templates/index_page.mustache b/ai/templates/index_page.mustache index 6952ed21f691..a3172ca490a4 100644 --- a/ai/templates/index_page.mustache +++ b/ai/templates/index_page.mustache @@ -1,6 +1,9 @@
- {{{issuers_table}}} + {{{providers_table}}}
- {{#str}}createnewprovider, ai{{/str}} + {{#str}}addprovider, ai{{/str}} + {{#templates}} + {{{.}}} + {{/templates}}
From 2617ff30283aaf24180541cfd838e83904802391 Mon Sep 17 00:00:00 2001 From: Michael hughes Date: Fri, 22 Mar 2024 17:56:26 +0000 Subject: [PATCH 16/21] openaiapiproviderr --- ai/classes/form/aiprovider.php | 41 ---------------- ai/classes/form/openaiapiprovider.php | 68 +++++++++++++++++++++++++++ ai/classes/output/index_page.php | 8 ++-- ai/index.php | 2 +- lang/en/ai.php | 28 +++++++++++ 5 files changed, 102 insertions(+), 45 deletions(-) delete mode 100644 ai/classes/form/aiprovider.php create mode 100644 ai/classes/form/openaiapiprovider.php diff --git a/ai/classes/form/aiprovider.php b/ai/classes/form/aiprovider.php deleted file mode 100644 index fc98cba0a2a9..000000000000 --- a/ai/classes/form/aiprovider.php +++ /dev/null @@ -1,41 +0,0 @@ -type = $customdata['type']; - } - - parent::__construct($action, $customdata, $method, $target, $attributes, $editable, $ajaxformdata); - } - public function definition() { - global $PAGE, $OUTPUT; - - $mform = $this->_form; - $provider = $this->get_persistent(); - $mform->addElement('html','intro', 'hello'); - - // Name. - $mform->addElement('text', 'name', get_string('providername', 'ai')); - $mform->addRule('name', null, 'required', null, 'client'); - $mform->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'client'); - $mform->addHelpButton('name', 'providername', 'ai'); - - // Client Secret. - $mform->addElement('text', 'apikey', get_string('apikey', 'ai')); - $mform->addRule('apikey', get_string('maximumchars', '', 255), 'maxlength', 255, 'client'); - $mform->addHelpButton('apikey', 'apikey', 'ai'); - - $mform->addElement('header', 'features', get_string('features', 'ai')); - $mform->addElement('advcheckbox', 'allowchat', get_string('allowchat', 'ai')); - $mform->addHelpButton('allowchat', 'allowchat', 'ai'); - - $this->add_action_buttons(true, get_string('savechanges', 'ai')); - } -} diff --git a/ai/classes/form/openaiapiprovider.php b/ai/classes/form/openaiapiprovider.php new file mode 100644 index 000000000000..0f6058249e90 --- /dev/null +++ b/ai/classes/form/openaiapiprovider.php @@ -0,0 +1,68 @@ +type = $customdata['type']; + } + + parent::__construct($action, $customdata, $method, $target, $attributes, $editable, $ajaxformdata); + } + public function definition() { + global $PAGE, $OUTPUT; + + $mform = $this->_form; + $provider = $this->get_persistent(); + $mform->addElement('html','intro', 'hello'); + + // Name. + $mform->addElement('text', 'name', get_string('providername', 'ai')); + $mform->addRule('name', null, 'required', null, 'client'); + $mform->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'client'); + $mform->addHelpButton('name', 'providername', 'ai'); + + // Client Secret. + $mform->addElement('text','baseurl', get_string('baseurl', 'ai')); + $mform->setType('baseurl', PARAM_URL); + $mform->addElement('text', 'apikey', get_string('apikey', 'ai')); + $mform->addRule('apikey', get_string('maximumchars', '', 255), 'maxlength', 255, 'client'); + $mform->addHelpButton('apikey', 'apikey', 'ai'); + + $mform->addElement('header', 'features', get_string('features', 'ai')); + + $mform->addElement('advcheckbox', 'allowchat', get_string('allowchat', 'ai')); + $mform->addHelpButton('allowchat', 'allowchat', 'ai'); + $mform->addElement('text','completions', get_string('completionspath', 'ai')); + $mform->addElement('text','completionmodel', get_string('completionmodel', 'ai')); + $mform->disabledIf('completions', 'allowchat', 'notchecked'); + $mform->disabledIf('completionmodel', 'allowchat', 'notchecked'); + + $mform->addElement('advcheckbox', 'allowembeddings', get_string('allowembeddings', 'ai')); + $mform->addHelpButton('allowembeddings', 'allowembeddings', 'ai'); + $mform->addElement('text','embeddings', get_string('embeddingspath', 'ai')); + $mform->addElement('text','embeddingmodel', get_string('embeddingmodel', 'ai')); + $mform->disabledIf('embeddings', 'allowembeddings', 'notchecked'); + $mform->disabledIf('embeddingmodel', 'allowembeddings', 'notchecked'); + + $mform->addElement('header','constraints', get_string('constraints', 'ai')); + $mform->addElement('checkbox', 'systemwide', get_string('allowsystemwide', 'ai')); + $displaylist = \core_course_category::make_categories_list('moodle/course:changecategory'); +// if (!isset($displaylist[$course->category])) { +// //always keep current +// $displaylist[$course->category] = core_course_category::get($course->category, MUST_EXIST, true) +// ->get_formatted_name(); +// } + $mform->addElement('autocomplete', 'contextid', get_string('coursecategory'), $displaylist); +// $mform->addRule('contextid', null, 'required', null, 'client'); + $mform->addHelpButton('contextid', 'coursecategory'); + $mform->disabledIf('contextid', 'systemwide', 'checked'); + + $this->add_action_buttons(true, get_string('savechanges', 'ai')); + } +} diff --git a/ai/classes/output/index_page.php b/ai/classes/output/index_page.php index 6c3caf61d989..c07ee04881f2 100644 --- a/ai/classes/output/index_page.php +++ b/ai/classes/output/index_page.php @@ -99,11 +99,13 @@ public function providers_table($providers) { return \html_writer::table($table); } - private $providertypes = ['OpenAI API']; + private $providertypes = [ + 'openaiapi' => 'OpenAI API' + ]; protected function template_buttons() { global $CFG; $buttons = []; - foreach($this->providertypes as $type) { + foreach($this->providertypes as $type => $name) { $addurl = new \moodle_url($CFG->wwwroot . '/ai/index.php', [ 'action' => api::ACTION_EDIT_PROVIDER, @@ -111,7 +113,7 @@ protected function template_buttons() { 'pid' => null ] ); - $buttontext = get_string('newprovider', 'ai', $type); + $buttontext = get_string('newprovider', 'ai', $name); $buttons[] = [$addurl, $buttontext]; } return $buttons; diff --git a/ai/index.php b/ai/index.php index 3f131419b7d4..23975fcce675 100644 --- a/ai/index.php +++ b/ai/index.php @@ -41,7 +41,7 @@ } else { // Create new } - $mform = new \core_ai\form\aiprovider(null, [ + $mform = new \core_ai\form\openaiapiprovider(null, [ 'persistent' => $provider, 'type' => required_param('type', PARAM_RAW) ]); diff --git a/lang/en/ai.php b/lang/en/ai.php index 5f639e153994..557e026a368b 100644 --- a/lang/en/ai.php +++ b/lang/en/ai.php @@ -42,3 +42,31 @@ $string['embedding_help'] = 'Embedding allows the AI to generate vector representations of text.'; $string['aiproviderfeatures'] = ''; $string['aiproviderfeatures_desc'] = 'This plugin needs the following AI features'; + +// providers +$string['newprovider'] = '{$a} Based Provider'; + +// Provider instance form +$string['providername'] = "Name"; +$string['providername_help'] = "Name"; +$string['baseurl'] = 'Base URL'; +$string['baseurl_help'] = 'Base URL'; +$string['apikey'] = 'API Key'; +$string['apikey_help'] = 'API KEY '; + +$string['features'] = 'Features'; +$string['allowchat'] = 'Allow Chat'; +$string['allowchat_help'] = 'Allow this provider to provide chat completion.'; +$string['completionspath'] = 'Completions path'; +$string['completionspath_help'] = 'Completions path'; +$string['completionmodel'] = 'Completion Model'; +$string['completionmodel_help'] = 'Completion Model'; +$string['allowembeddings'] = 'Allow Emebddings'; +$string['allowembeddings_help'] = 'Allow this provider to be used to create embeddings.'; +$string['embeddingspath'] = 'Embeddings path'; +$string['embeddingspath_help'] = 'Embeddings path'; +$string['embeddingmodel'] = 'Embedding Model'; +$string['embeddingmodel_help'] = 'Embedding Model'; + +$string['constraints'] = 'Constraints'; +$string['savechanges'] = 'Save changes'; From 1bdbabcb605f4168e21865baab070a8a209015c2 Mon Sep 17 00:00:00 2001 From: Michael hughes Date: Fri, 22 Mar 2024 22:45:46 +0000 Subject: [PATCH 17/21] Got basic AIProvider form working --- ai/classes/api.php | 19 ++++++ ai/classes/form/openaiapiprovider.php | 85 +++++++++++++++++++++++---- ai/index.php | 16 ++++- lang/en/ai.php | 15 +++++ lib/db/access.php | 7 +++ 5 files changed, 129 insertions(+), 13 deletions(-) diff --git a/ai/classes/api.php b/ai/classes/api.php index 242320d64480..c4352199e6fe 100644 --- a/ai/classes/api.php +++ b/ai/classes/api.php @@ -13,6 +13,8 @@ class api { const ACTION_REMOVE_PROVIDER = "remove"; const ACTION_EDIT_PROVIDER = "edit"; const ACTION_MANAGE_PROVIDERS = "manage"; + + const ACTION_SAVE_PROVIDER = "save"; /** * Return a list of AIProviders that are available for specified context. * @param $context @@ -52,4 +54,21 @@ public static function get_providers($contextid = null, $allowchat = null, $allo $providers = aiprovider::get_records($filters); return array_values($providers); } + public static function create_provider($data) { + return self::create_or_update_provider($data, true); + } + public static function update_provider($data) { + return self::create_or_update_provider($data, false); + } + protected static function create_or_update_provider($data, bool $create) { + //TODO Capability check. + $provider = new aiprovider($data->id ?? 0, $data); + + if ($create) { + $provider->create(); + } else { + $provider->update(); + } + return $provider; + } } diff --git a/ai/classes/form/openaiapiprovider.php b/ai/classes/form/openaiapiprovider.php index 0f6058249e90..9c827b4e5fec 100644 --- a/ai/classes/form/openaiapiprovider.php +++ b/ai/classes/form/openaiapiprovider.php @@ -1,11 +1,17 @@ disabledIf('embeddingmodel', 'allowembeddings', 'notchecked'); $mform->addElement('header','constraints', get_string('constraints', 'ai')); - $mform->addElement('checkbox', 'systemwide', get_string('allowsystemwide', 'ai')); - $displaylist = \core_course_category::make_categories_list('moodle/course:changecategory'); -// if (!isset($displaylist[$course->category])) { -// //always keep current -// $displaylist[$course->category] = core_course_category::get($course->category, MUST_EXIST, true) -// ->get_formatted_name(); -// } - $mform->addElement('autocomplete', 'contextid', get_string('coursecategory'), $displaylist); -// $mform->addRule('contextid', null, 'required', null, 'client'); - $mform->addHelpButton('contextid', 'coursecategory'); - $mform->disabledIf('contextid', 'systemwide', 'checked'); + $displaylist = [ + "" => get_string('anywhere', 'ai'), + "-1" => get_string('anyusercourse', 'ai') + ]; + $displaylist = + $displaylist + + \core_course_category::make_categories_list('moodle/ai:selectcategory') + ; + + $mform->addElement('autocomplete', 'categoryid', get_string('scopecoursecategory','ai'), $displaylist); + $mform->addHelpButton('categoryid', 'scopecoursecategory', 'ai'); + $mform->setDefault('categoryid', null); // a null category is technical "whole" site + + $coursedisplaylist = \get_courses("all", "shortname"); + $coursedisplaylist = array_map(function($course) { + return $course->shortname; + }, $coursedisplaylist); + $coursedisplaylist = ["" => "No Restriction"] + $coursedisplaylist; + $mform->addElement('autocomplete', 'courseid', get_string('course'), $coursedisplaylist); + $mform->addHelpButton('courseid', 'scopecourse', 'ai'); + $mform->setDefault('courseid', null); // a null category is technical "whole" site +// $mform->disabledIf('courseid', 'categoryid', 'neq', ""); + + $mform->addElement('hidden', 'contextid', ); + $mform->setType('contextid', PARAM_RAW); + + $mform->addElement('hidden', 'onlyenrolledcourses', ); + $mform->setType('onlyenrolledcourses', PARAM_RAW); + + $mform->addElement('hidden', 'enabled', true); + $mform->setType('enabled', PARAM_ALPHA); + + $mform->addElement('hidden', 'type', $this->_customdata['type']); + $mform->setType('type', PARAM_ALPHANUM); + + $mform->addElement('hidden', 'action', api::ACTION_EDIT_PROVIDER); + $mform->setType('action', PARAM_ALPHA); + + $mform->addElement('hidden', 'id', $provider->get('id')); + $mform->setType('id', PARAM_INT); $this->add_action_buttons(true, get_string('savechanges', 'ai')); } + + protected function filter_data_for_persistent($data) { + if (!empty($data->categoryid)) { + $data->onlyenrolledcourses = false; + if ($data->categoryid >0) { + $data->contextid = \core_course_category::get($data->categoryid)->get_context()->id; + } else{ + $data->contextid = $data->categoryid; + if ($data->contextid == -1) { + $data->onlyenrolledcourses = true; + } + } + } else if (!empty($data->courseid)) { + $data->contextid = \core\context\course::instance($data->courseid)->id; + } + return (object) array_diff_key((array) $data, array_flip((array) static::$foreignfields)); + } + function extra_validation($data, $files, array &$errors) { + parent::extra_validation($data, $files, $errors); + if(!empty($data->categoryid) && !empty($data->courseid)) { + // $data->category is not allowed to be set. + $errors['courseid'] = "Course constraint cannot be set whilst a category one is set"; + } + var_dump($errors); + return $errors; + } } diff --git a/ai/index.php b/ai/index.php index 23975fcce675..f7d312e78efd 100644 --- a/ai/index.php +++ b/ai/index.php @@ -52,8 +52,22 @@ redirect(new moodle_url('/admin/tool/oauth2/issuers.php')); } else if ($action == api::ACTION_EDIT_PROVIDER) { // Handle edit. - if ($data = $mform->get_data()) { + if ($mform->is_cancelled()) { + echo 'cancelled'; + } + if ($mform->is_submitted()) { + echo 'submitted'; + } + echo 'validated '. (int)$mform->is_validated(); + if ($data = $mform->get_data()) { + var_dump($data); + if (!empty($data->id)) { + core_ai\api::update_provider($data); + } else { + core_ai\api::create_provider($data); + } + exit(); } else { echo $OUTPUT->header(); $mform->display(); diff --git a/lang/en/ai.php b/lang/en/ai.php index 557e026a368b..a3773be72441 100644 --- a/lang/en/ai.php +++ b/lang/en/ai.php @@ -24,6 +24,8 @@ $string['pluginname'] = 'AI Providers'; $string['aiprovider'] = 'AI Provider'; $string['addprovider'] = 'Add AI Provider'; +$string['anyusercourse'] = 'Any course user is enrolled in'; +$string['anywhere'] = 'Anywhere in site'; $string['enabled'] = 'Enabled'; $string['disabled'] = 'Disabled'; $string['removeprovider'] = 'Remove AI Provider'; @@ -68,5 +70,18 @@ $string['embeddingmodel'] = 'Embedding Model'; $string['embeddingmodel_help'] = 'Embedding Model'; +$string['scopecoursecategory'] = 'Category'; +$string['scopecoursecategory_help'] = 'Limit AI scope to courses and sub-categories. + +This can be limited to work only against the user\'s enrolled courses. + +Users must hold the `moodle/ai:selectcategory` capability on a category to choose it.'; +$string['scopecourse'] = 'Course(s)'; +$string['scopecourse_help'] = 'Limit AI scope to specific courses. + +Not available if a category scope constraint has been chosen. + +Users must hold the `moodle/ai:selectcourse` capability on a course to choose it.'; + $string['constraints'] = 'Constraints'; $string['savechanges'] = 'Save changes'; diff --git a/lib/db/access.php b/lib/db/access.php index 96f981434419..88c32ba11967 100644 --- a/lib/db/access.php +++ b/lib/db/access.php @@ -2772,4 +2772,11 @@ 'manager' => CAP_ALLOW, ] ], + 'moodle/ai:selectcategory' => [ + 'captype' => 'read', + 'contextlevel' => CONTEXT_COURSECAT, + 'archetypes' => [ + 'manager' => CAP_ALLOW, + ] + ] ); From 77da1654c89661e481e878e8b6b480f572d64d58 Mon Sep 17 00:00:00 2001 From: Michael hughes Date: Mon, 25 Mar 2024 11:48:33 +0000 Subject: [PATCH 18/21] Latest checkin --- ai/classes/aiprovider.php | 8 ++++---- lang/en/ai.php | 2 ++ mod/xaichat/view.php | 4 +++- search/engine/solrrag/classes/engine.php | 11 ++++++----- search/engine/solrrag/settings.php | 22 ++++++++++++++++++++++ 5 files changed, 37 insertions(+), 10 deletions(-) diff --git a/ai/classes/aiprovider.php b/ai/classes/aiprovider.php index 7dc88f908a9f..a4bdb4151aa7 100644 --- a/ai/classes/aiprovider.php +++ b/ai/classes/aiprovider.php @@ -256,14 +256,14 @@ public static function get_records($filters = [], $sort = '', $order = 'ASC', $s $providercontextid = $record->get('contextid'); if ($providercontextid == self::CONTEXT_ALL_MY_COURSES) { // More problematic. - debugging('Provider needs to be in one of user\'s courses', DEBUG_DEVELOPER); +// debugging('Provider needs to be in one of user\'s courses', DEBUG_DEVELOPER); $result = $result & true; } else if ($providercontextid == null) { // System provider so always matches. - debugging("System AI provider", DEBUG_DEVELOPER); +// debugging("System AI provider", DEBUG_DEVELOPER); $result = $result & true; } else { - debugging("Context linked AI provider", DEBUG_DEVELOPER); +// debugging("Context linked AI provider", DEBUG_DEVELOPER); $providercontext = \context::instance_by_id( $providercontextid ); @@ -272,7 +272,7 @@ public static function get_records($filters = [], $sort = '', $order = 'ASC', $s $result = $result & $ischild; } }else { - debugging('Filtering on '.$key. "' = {$value}", DEBUG_DEVELOPER); +// debugging('Filtering on '.$key. "' = {$value}", DEBUG_DEVELOPER); if ($record->get($key) != $value) { return false; } diff --git a/lang/en/ai.php b/lang/en/ai.php index a3773be72441..a4b76a272e38 100644 --- a/lang/en/ai.php +++ b/lang/en/ai.php @@ -40,6 +40,7 @@ '; $string['chat'] = 'Chat Completion'; $string['chat_help'] = 'Chat Completion allows the AIProvider to be used to generate text.'; +$string['disable'] = 'Disable'; $string['embedding'] = 'Embedding'; $string['embedding_help'] = 'Embedding allows the AI to generate vector representations of text.'; $string['aiproviderfeatures'] = ''; @@ -85,3 +86,4 @@ $string['constraints'] = 'Constraints'; $string['savechanges'] = 'Save changes'; +$string['aisettings'] = 'AI Provider Settings'; diff --git a/mod/xaichat/view.php b/mod/xaichat/view.php index cdb94b416f09..825f2c1c3aca 100644 --- a/mod/xaichat/view.php +++ b/mod/xaichat/view.php @@ -25,6 +25,8 @@ require(__DIR__.'/../../config.php'); require_once(__DIR__.'/lib.php'); + +use core_ai\api; use mod_xaichat\aichatform; // Course module id. @@ -57,7 +59,7 @@ } //$aicontext = $_SESSION[$aicontextkey]; -if (!($aiprovider = \core\ai\api::get_provider($moduleinstance->aiproviderid))){ +if (!($aiprovider = api::get_provider($moduleinstance->aiproviderid))){ throw new moodle_exception("noaiproviderfound", 'xaichat'); } diff --git a/search/engine/solrrag/classes/engine.php b/search/engine/solrrag/classes/engine.php index 043102731605..5fb1850ac0fd 100644 --- a/search/engine/solrrag/classes/engine.php +++ b/search/engine/solrrag/classes/engine.php @@ -2,6 +2,7 @@ namespace search_solrrag; +use core_ai\api; use search_solrrag\document; use search_solrrag\schema; //require_once($CFG->dirroot . "/search/engine/solrrag/lib.php"); @@ -9,9 +10,9 @@ // require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/api.php"); // require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/aiprovider.php"); // require_once($CFG->dirroot ."/search/engine/solrrag/classes/ai/aiclient.php"); -use \core\ai\AIProvider; -use \core\ai\AIClient; -use \core\ai\AiException; +use \core_ai\AIProvider; +use \core_ai\aiclient; +use \core_ai\AiException; class engine extends \search_solr\engine { /** @@ -28,8 +29,8 @@ public function __construct(bool $alternateconfiguration = false) // Ideally we'd be using a Moodle AI provider to tell us which LLM to use for generating embeddings, and // then simply calling the API and get some results back...but we don't have that yet. // So we'll fudge this for the moment and leverage an OpenAI Web Service API via a simple HTTP request. - $aiproviderid = 1; - $aiprovider = \core\ai\api::get_provider($aiproviderid); + $aiproviderid = get_config('search_solrrag', 'aiprovider'); + $aiprovider = api::get_provider($aiproviderid); $this->aiprovider = $aiprovider; $this->aiclient = !is_null($aiprovider)? new AIClient($aiprovider) : null; } diff --git a/search/engine/solrrag/settings.php b/search/engine/solrrag/settings.php index b4a837c5e0e4..a17b7227b24b 100644 --- a/search/engine/solrrag/settings.php +++ b/search/engine/solrrag/settings.php @@ -31,6 +31,28 @@ $settings->add(new admin_setting_heading('search_solrrag_settings', '', get_string('extensionerror', 'search_solrrag'))); } else { + // Which AI Provider to use: + $settings->add(new admin_setting_heading('search_solrrag_aiprovider', + new lang_string('aisettings', 'ai'), '')); + $providers = \core_ai\api::get_providers( + null, + true, + true + ); + $optproviders = [ + '' => get_string('disable', 'ai') + ]; + + foreach($providers as $provider) { + $optproviders[$provider->get('id')] = $provider->get('name'); + } + $settings->add(new admin_setting_configselect( + 'search_solrrag/aiprovider', + 'Choose Provider', + 'List of available AI services', + "", + $optproviders + )); $settings->add(new admin_setting_heading('search_solrrag_connection', new lang_string('connectionsettings', 'search_solrrag'), '')); $settings->add(new admin_setting_configtext('search_solrrag/server_hostname', new lang_string('solrserverhostname', 'search_solrrag'), new lang_string('solrserverhostname_desc', 'search_solrrag'), '127.0.0.1', PARAM_HOST)); From 26213eeea75b7ecf7d197a47056043b999169dba Mon Sep 17 00:00:00 2001 From: Michael hughes Date: Wed, 3 Apr 2024 14:17:01 +0100 Subject: [PATCH 19/21] recent changes --- ai/classes/aiprovider.php | 8 -------- ai/index.php | 22 +++++++++++++++------- lang/en/ai.php | 1 + lib/navigationlib.php | 6 +++--- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/ai/classes/aiprovider.php b/ai/classes/aiprovider.php index a4bdb4151aa7..56dda3dc1fb4 100644 --- a/ai/classes/aiprovider.php +++ b/ai/classes/aiprovider.php @@ -219,7 +219,6 @@ public static function get_records($filters = [], $sort = '', $order = 'ASC', $s 'completionmodel' => 'llama2', 'contextid' => null, // Global AI Provider 'onlyenrolledcourses' => true - // 'apikey'=> $_ENV['OPENAIKEY'] ]); array_push($records, $fake); $fake = new static(0, (object) [ @@ -235,20 +234,16 @@ public static function get_records($filters = [], $sort = '', $order = 'ASC', $s 'completionmodel' => 'llama2', 'contextid' => 111, // Global AI Provider 'onlyenrolledcourses' => true, - // 'apikey'=> $_ENV['OPENAIKEY'] ]); array_push($records, $fake); $targetcontextid = $filters['contextid'] ?? null; $targetcontext = null; if (is_null($targetcontextid)) { -// debugging("No Context Restriction", DEBUG_DEVELOPER); unset($filters['contextid']); // Because we need special handling. } else { -// debugging("Context Restriction: {$targetcontextid}", DEBUG_DEVELOPER); $targetcontext = \context::instance_by_id($targetcontextid); } -// debugging(print_r($filters,1), DEBUG_DEVELOPER); $records = array_filter($records, function($record) use ($filters, $targetcontext) { $result = true; foreach($filters as $key => $value) { @@ -256,14 +251,11 @@ public static function get_records($filters = [], $sort = '', $order = 'ASC', $s $providercontextid = $record->get('contextid'); if ($providercontextid == self::CONTEXT_ALL_MY_COURSES) { // More problematic. -// debugging('Provider needs to be in one of user\'s courses', DEBUG_DEVELOPER); $result = $result & true; } else if ($providercontextid == null) { // System provider so always matches. -// debugging("System AI provider", DEBUG_DEVELOPER); $result = $result & true; } else { -// debugging("Context linked AI provider", DEBUG_DEVELOPER); $providercontext = \context::instance_by_id( $providercontextid ); diff --git a/ai/index.php b/ai/index.php index f7d312e78efd..f480f05d19d0 100644 --- a/ai/index.php +++ b/ai/index.php @@ -15,15 +15,22 @@ $PAGE->set_context(context_system::instance()); $PAGE->set_pagelayout("admin"); -$strheading = get_string('aiprovider', 'ai'); -$PAGE->set_heading($strheading); -$PAGE->set_title($strheading); - $renderer = $PAGE->get_renderer('core', 'ai'); $action = optional_param('action', '', PARAM_ALPHAEXT); // We're using pid as "id" is used to specify contextids. $providerid = optional_param('pid', '', PARAM_RAW); +$incontextid = optional_param('contextid', null, PARAM_RAW); + +$context = !is_null($incontextid) ? \context::instance_by_id($incontextid) : null; + +if (empty($context)) { + $strheading = get_string(get_string('pluginname', 'ai')); +} else { + $strheading = get_string('aiprovidersin', 'ai', $context->get_context_name()); +} +$PAGE->set_heading($strheading); +$PAGE->set_title($strheading); $provider = null; $mform = null; @@ -43,7 +50,8 @@ } $mform = new \core_ai\form\openaiapiprovider(null, [ 'persistent' => $provider, - 'type' => required_param('type', PARAM_RAW) + 'type' => required_param('type', PARAM_RAW), + 'contextid' => $incontextid ]); } @@ -78,10 +86,10 @@ } else { // Display list of providers. $indexpage = new \core_ai\output\index_page( - api::get_providers() + api::get_providers($incontextid) ); echo $OUTPUT->header(); - echo $OUTPUT->heading(get_string('pluginname', 'ai')); + echo $renderer->render_index_page($indexpage); echo $OUTPUT->footer(); } diff --git a/lang/en/ai.php b/lang/en/ai.php index a4b76a272e38..9139ce95919f 100644 --- a/lang/en/ai.php +++ b/lang/en/ai.php @@ -23,6 +23,7 @@ */ $string['pluginname'] = 'AI Providers'; $string['aiprovider'] = 'AI Provider'; +$string['aiprovidersin'] = 'AI Providers in {$a}'; $string['addprovider'] = 'Add AI Provider'; $string['anyusercourse'] = 'Any course user is enrolled in'; $string['anywhere'] = 'Anywhere in site'; diff --git a/lib/navigationlib.php b/lib/navigationlib.php index 3b03454d5acd..08bdb3192ab7 100644 --- a/lib/navigationlib.php +++ b/lib/navigationlib.php @@ -25,7 +25,7 @@ use core\moodlenet\utilities; use core_contentbank\contentbank; -use core\ai\api; +use core_ai\api; defined('MOODLE_INTERNAL') || die(); @@ -5651,8 +5651,8 @@ protected function load_category_settings() { new moodle_url( '/ai/index.php', [ - 'id' => $catcontext->id, - 'action' => api::ACTION_MANAGE_PROVIDERS + 'contextid' => $catcontext->id, + 'action' => api::ACTION_MANAGE_PROVIDERS, ]), navigation_node::TYPE_SETTING ); From 9164361fd5b7a347c0630a50b6295b7cc82a83c1 Mon Sep 17 00:00:00 2001 From: Michael hughes Date: Mon, 22 Apr 2024 09:18:57 +0100 Subject: [PATCH 20/21] AI persistent tables added to lib/db/install.xml --- ai/classes/aiprovider.php | 80 ++++++++++++++------------- ai/classes/form/openaiapiprovider.php | 13 +++-- ai/classes/output/index_page.php | 2 +- ai/index.php | 14 +++-- lib/db/install.xml | 26 ++++++++- lib/db/upgrade.php | 37 +++++++++++++ mod/xaichat/view.php | 3 +- 7 files changed, 122 insertions(+), 53 deletions(-) diff --git a/ai/classes/aiprovider.php b/ai/classes/aiprovider.php index 56dda3dc1fb4..b6bf2c450986 100644 --- a/ai/classes/aiprovider.php +++ b/ai/classes/aiprovider.php @@ -9,6 +9,7 @@ class AIProvider extends persistent { // Ultimately this would extend a persistent. const CONTEXT_ALL_MY_COURSES = -1; + const TABLE = "aiprovider"; protected static function define_properties() { @@ -31,13 +32,13 @@ protected static function define_properties() 'baseurl' => [ 'type' => PARAM_URL ], - 'embeddings' => [ + 'embeddingsurl' => [ 'type' => PARAM_URL ], 'embeddingmodel' => [ 'type' => PARAM_ALPHANUMEXT ], - 'completions' => [ + 'completionsurl' => [ 'type' => PARAM_URL ], 'completionmodel' => [ @@ -150,10 +151,10 @@ public function get_settings_for_user($user) { $mycourseids = enrol_get_my_courses(array('id', 'cacherev'), 'id', 0, [], false); $onlyenrolledcourses = $this->get('onlyenrolledcourses'); $courseids = []; - if ($this->get('context') == self::CONTEXT_ALL_MY_COURSES) { + if ($this->get('contextid') == self::CONTEXT_ALL_MY_COURSES) { $courseids = array_keys($mycourseids); } else { - $context = \context::instance_by_id($this->get('context')); + $context = \context::instance_by_id($this->get('contextid')); if ($context->contextlevel == CONTEXT_COURSE) { // Check that the specific course is also in the user's list of courses. $courseids = array_intersect([$context->instanceid], $mycourseids); @@ -187,40 +188,41 @@ public function get_settings_for_user($user) { */ public static function get_records($filters = [], $sort = '', $order = 'ASC', $skip = 0, $limit = 0) { global $_ENV; - - $records = []; - $fake = new static(0, (object) [ - 'id' => 1, - 'name' => "Open AI Provider (hardcoded)", - 'enabled' => true, - 'allowembeddings' => true, - 'allowchat' => true, - 'baseurl' => 'https://api.openai.com/v1/', - 'embeddings' => 'embeddings', - 'embeddingmodel' => 'text-embedding-3-small', - 'completions' => 'chat/completions', - 'completionmodel' => 'gpt-4-turbo-preview', - 'apikey'=> $_ENV['OPENAIKEY'], - 'contextid' => \context_system::instance()->id, - //null, // Global AI Provider - 'onlyenrolledcourses' => true - ]); - array_push($records, $fake); - $fake = new static(0, (object) [ - 'id' => 2, - 'name' => "Ollama AI Provider (hard coded)", - 'enabled' => true, - 'allowembeddings' => true, - 'allowchat' => true, - 'baseurl' => 'http://127.0.0.1:11434/api/', - 'embeddings' => 'embeddings', - 'embeddingmodel' => '', - 'completions' => 'chat', - 'completionmodel' => 'llama2', - 'contextid' => null, // Global AI Provider - 'onlyenrolledcourses' => true - ]); - array_push($records, $fake); + $records = parent::get_records($filters, $sort, $order, $skip, $limit); +// $records = []; +// $fake = new static(0, (object) [ +// 'id' => 1, +// 'name' => "Open AI Provider (hardcoded)", +// 'enabled' => true, +// 'allowembeddings' => true, +// 'allowchat' => true, +// 'baseurl' => 'https://api.openai.com/v1/', +// 'embeddings' => 'embeddings', +// 'embeddingmodel' => 'text-embedding-3-small', +// 'completions' => 'chat/completions', +// 'completionmodel' => 'gpt-4-turbo-preview', +// 'apikey'=> $_ENV['OPENAIKEY'], +// 'contextid' => \context_system::instance()->id, +// //null, // Global AI Provider +// 'onlyenrolledcourses' => true +// ]); +// array_push($records, $fake); +// $fake = new static(0, (object) [ +// 'id' => 2, +// 'name' => "Ollama AI Provider (hard coded)", +// 'enabled' => true, +// 'allowembeddings' => true, +// 'allowchat' => true, +// 'baseurl' => 'http://127.0.0.1:11434/api/', +// 'embeddings' => 'embeddings', +// 'embeddingmodel' => '', +// 'completions' => 'chat', +// 'completionmodel' => 'llama2', +// 'contextid' => null, // Global AI Provider +// 'onlyenrolledcourses' => true +// ]); +// array_push($records, $fake); +/* $fake = new static(0, (object) [ 'id' => 3, 'name' => "Ollama AI Provider (hard coded) Misc Category only", @@ -236,7 +238,7 @@ public static function get_records($filters = [], $sort = '', $order = 'ASC', $s 'onlyenrolledcourses' => true, ]); array_push($records, $fake); - +*/ $targetcontextid = $filters['contextid'] ?? null; $targetcontext = null; if (is_null($targetcontextid)) { diff --git a/ai/classes/form/openaiapiprovider.php b/ai/classes/form/openaiapiprovider.php index 9c827b4e5fec..3ce2bd34346a 100644 --- a/ai/classes/form/openaiapiprovider.php +++ b/ai/classes/form/openaiapiprovider.php @@ -32,7 +32,8 @@ public function definition() { $mform->addRule('name', null, 'required', null, 'client'); $mform->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'client'); $mform->addHelpButton('name', 'providername', 'ai'); - + $mform->addElement('advcheckbox', 'enabled', get_string('enabled', 'ai')); + $mform->addHelpButton('enabled', 'enabled', 'ai'); // Client Secret. $mform->addElement('text','baseurl', get_string('baseurl', 'ai')); $mform->setType('baseurl', PARAM_URL); @@ -44,14 +45,14 @@ public function definition() { $mform->addElement('advcheckbox', 'allowchat', get_string('allowchat', 'ai')); $mform->addHelpButton('allowchat', 'allowchat', 'ai'); - $mform->addElement('text','completions', get_string('completionspath', 'ai')); + $mform->addElement('text','completionsurl', get_string('completionspath', 'ai')); $mform->addElement('text','completionmodel', get_string('completionmodel', 'ai')); - $mform->disabledIf('completions', 'allowchat', 'notchecked'); + $mform->disabledIf('completionsurl', 'allowchat', 'notchecked'); $mform->disabledIf('completionmodel', 'allowchat', 'notchecked'); $mform->addElement('advcheckbox', 'allowembeddings', get_string('allowembeddings', 'ai')); $mform->addHelpButton('allowembeddings', 'allowembeddings', 'ai'); - $mform->addElement('text','embeddings', get_string('embeddingspath', 'ai')); + $mform->addElement('text','embeddingsurl', get_string('embeddingspath', 'ai')); $mform->addElement('text','embeddingmodel', get_string('embeddingmodel', 'ai')); $mform->disabledIf('embeddings', 'allowembeddings', 'notchecked'); $mform->disabledIf('embeddingmodel', 'allowembeddings', 'notchecked'); @@ -87,8 +88,8 @@ public function definition() { $mform->addElement('hidden', 'onlyenrolledcourses', ); $mform->setType('onlyenrolledcourses', PARAM_RAW); - $mform->addElement('hidden', 'enabled', true); - $mform->setType('enabled', PARAM_ALPHA); +// $mform->addElement('hidden', 'enabled', true); +// $mform->setType('enabled', PARAM_ALPHA); $mform->addElement('hidden', 'type', $this->_customdata['type']); $mform->setType('type', PARAM_ALPHANUM); diff --git a/ai/classes/output/index_page.php b/ai/classes/output/index_page.php index c07ee04881f2..faa106facb63 100644 --- a/ai/classes/output/index_page.php +++ b/ai/classes/output/index_page.php @@ -52,7 +52,7 @@ public function providers_table($providers) { $context = "System"; } $completion = $provider->get('allowchat'); - $embeddings = $provider->get('embeddings'); + $embeddings = $provider->get('allowembeddings'); $status = $provider->get('enabled'); // Set up cells. diff --git a/ai/index.php b/ai/index.php index f480f05d19d0..d64cb6bf9029 100644 --- a/ai/index.php +++ b/ai/index.php @@ -21,11 +21,11 @@ // We're using pid as "id" is used to specify contextids. $providerid = optional_param('pid', '', PARAM_RAW); $incontextid = optional_param('contextid', null, PARAM_RAW); - -$context = !is_null($incontextid) ? \context::instance_by_id($incontextid) : null; +//var_dump($incontextid); +$context = !empty($incontextid) ? \context::instance_by_id($incontextid) : null; if (empty($context)) { - $strheading = get_string(get_string('pluginname', 'ai')); + $strheading = get_string('pluginname', 'ai'); } else { $strheading = get_string('aiprovidersin', 'ai', $context->get_context_name()); } @@ -36,21 +36,24 @@ $mform = null; if ($providerid) { - $provider = core\ai\api::get_provider($providerid); + $provider = api::get_provider($providerid); if (!$provider) { throw new moodle_exception('invaliddata'); } } if ($action == api::ACTION_EDIT_PROVIDER) { + if ($provider) { // Edit + $type = "openaipi";// Should store in and read from provider. } else { // Create new + $type = required_param('type', PARAM_RAW); } $mform = new \core_ai\form\openaiapiprovider(null, [ 'persistent' => $provider, - 'type' => required_param('type', PARAM_RAW), + 'type' => $type, 'contextid' => $incontextid ]); } @@ -81,6 +84,7 @@ $mform->display(); echo $OUTPUT->footer(); } + exit; } else if ($action == api::ACTION_REMOVE_PROVIDER) { // Handle remove. } else { diff --git a/lib/db/install.xml b/lib/db/install.xml index 802a5f592b0a..b6bf503793b7 100644 --- a/lib/db/install.xml +++ b/lib/db/install.xml @@ -1,5 +1,5 @@ - @@ -4844,5 +4844,29 @@ + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index 13bd8b7e6dd9..d5abfa87efbb 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -1129,5 +1129,42 @@ function xmldb_main_upgrade($oldversion) { upgrade_main_savepoint(true, 2024030500.02); } + if ($oldversion < 2024032600.01) { + + // Define table aiprovider to be created. + $table = new xmldb_table('aiprovider'); + + // Adding fields to table aiprovider. + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); + $table->add_field('usermodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); + $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); + $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); + $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null); + $table->add_field('enabled', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '1'); + $table->add_field('apikey', XMLDB_TYPE_TEXT, null, null, null, null, null); + $table->add_field('allowembeddings', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '1'); + $table->add_field('allowchat', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '1'); + $table->add_field('baseurl', XMLDB_TYPE_TEXT, null, null, null, null, null); + $table->add_field('embeddingsurl', XMLDB_TYPE_TEXT, null, null, null, null, null); + $table->add_field('completionsurl', XMLDB_TYPE_TEXT, null, null, null, null, null); + $table->add_field('embeddingmodel', XMLDB_TYPE_TEXT, null, null, null, null, null); + $table->add_field('completionmodel', XMLDB_TYPE_TEXT, null, null, null, null, null); + $table->add_field('contextid', XMLDB_TYPE_INTEGER, '10', null, null, null, '0'); + $table->add_field('onlyenrolledcourses', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '1'); + + // Adding keys to table aiprovider. + $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); + $table->add_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); + + // Conditionally launch create table for aiprovider. + if (!$dbman->table_exists($table)) { + $dbman->create_table($table); + } + + // Main savepoint reached. + upgrade_main_savepoint(true, 2024032600.02); + } + + return true; } diff --git a/mod/xaichat/view.php b/mod/xaichat/view.php index 825f2c1c3aca..c91e4ad1572a 100644 --- a/mod/xaichat/view.php +++ b/mod/xaichat/view.php @@ -27,6 +27,7 @@ require_once(__DIR__.'/lib.php'); use core_ai\api; +use core_ai\aiclient; use mod_xaichat\aichatform; // Course module id. @@ -83,7 +84,7 @@ } $stepnow = 0; $totalsteps = 4; - $aiclient = new \core\ai\AIClient($aiprovider); + $aiclient = new AIClient($aiprovider); $progress = new \progress_bar(); $progress->create(); From a853741b2667f264f21e4797dd96f154b50e2de0 Mon Sep 17 00:00:00 2001 From: Michael Hughes Date: Mon, 22 Apr 2024 14:37:46 +0100 Subject: [PATCH 21/21] Hard code system context id, rather than an id --- ai/classes/aiprovider.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ai/classes/aiprovider.php b/ai/classes/aiprovider.php index b6bf2c450986..8496d55309f8 100644 --- a/ai/classes/aiprovider.php +++ b/ai/classes/aiprovider.php @@ -234,7 +234,8 @@ public static function get_records($filters = [], $sort = '', $order = 'ASC', $s 'embeddingmodel' => '', 'completions' => 'chat', 'completionmodel' => 'llama2', - 'contextid' => 111, // Global AI Provider + 'contextid' => \context_system::instance()->id, + // 111, // Global AI Provider 'onlyenrolledcourses' => true, ]); array_push($records, $fake);