diff --git a/ai/classes/aiactions/base.php b/ai/classes/aiactions/base.php
index c59ce918e5b..3a764df9ac1 100644
--- a/ai/classes/aiactions/base.php
+++ b/ai/classes/aiactions/base.php
@@ -89,8 +89,8 @@ abstract class base {
*
* @return string The system instruction for the action.
*/
- public function get_system_instruction(): string {
- $stringid = 'action_' . $this->get_basename() . '_instruction';
+ public static function get_system_instruction(): string {
+ $stringid = 'action_' . self::get_basename() . '_instruction';
// If the string doesn't exist, return an empty string.
if (!get_string_manager()->string_exists($stringid, 'core_ai')) {
diff --git a/ai/classes/provider.php b/ai/classes/provider.php
index b2b1054848a..1dc1afc5d61 100644
--- a/ai/classes/provider.php
+++ b/ai/classes/provider.php
@@ -54,4 +54,21 @@ abstract class provider {
return get_string('pluginname', $component);
}
+ /**
+ * Get any action settings for this provider.
+ *
+ * @param string $action The action class name.
+ * @param \admin_root $ADMIN The admin root object.
+ * @param string $section The section name.
+ * @param bool $hassiteconfig Whether the current user has moodle/site:config capability.
+ * @return array An array of settings.
+ */
+ public function get_action_settings(
+ string $action,
+ \admin_root $ADMIN,
+ string $section,
+ bool $hassiteconfig
+ ): array {
+ return [];
+ }
}
diff --git a/ai/classes/table/aiprovider_action_management_table.php b/ai/classes/table/aiprovider_action_management_table.php
index 38e5473c33d..51dd3d28790 100644
--- a/ai/classes/table/aiprovider_action_management_table.php
+++ b/ai/classes/table/aiprovider_action_management_table.php
@@ -175,7 +175,14 @@ class aiprovider_action_management_table extends flexible_table implements dynam
* @return string
*/
protected function col_settings(stdClass $row): string {
- // TODO: MDL-82609 - Add settings link.
+ global $CFG;
+ require_once($CFG->libdir . '/adminlib.php'); // Needed for the AJAX calls.
+ $tree = \admin_get_root();
+ $sectionname = $this->pluginname . '_' . $row->action::get_basename();
+ $section = $tree->locate($sectionname);
+ if ($section) {
+ return \html_writer::link($section->get_settings_page_url(), get_string('settings'));
+ }
return '';
}
diff --git a/ai/provider/openai/classes/abstract_processor.php b/ai/provider/openai/classes/abstract_processor.php
index e7f2a697841..7f2839190c6 100644
--- a/ai/provider/openai/classes/abstract_processor.php
+++ b/ai/provider/openai/classes/abstract_processor.php
@@ -47,6 +47,15 @@ abstract class abstract_processor extends process_base {
*/
abstract protected function get_model(): string;
+ /**
+ * Get the system instructions.
+ *
+ * @return string
+ */
+ protected function get_system_instruction(): string {
+ return $this->action::get_system_instruction();
+ }
+
/**
* Create the request object to send to the OpenAI API.
*
diff --git a/ai/provider/openai/classes/process_generate_image.php b/ai/provider/openai/classes/process_generate_image.php
index 94ffc204ee3..d43da6048f7 100644
--- a/ai/provider/openai/classes/process_generate_image.php
+++ b/ai/provider/openai/classes/process_generate_image.php
@@ -22,6 +22,7 @@ use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Uri;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\UriInterface;
/**
* Class process image generation.
@@ -38,13 +39,13 @@ class process_generate_image extends abstract_processor {
private string $responseformat = 'url';
#[\Override]
- protected function get_endpoint(): \Psr\Http\Message\UriInterface {
- return new Uri('https://api.openai.com/v1/images/generations');
+ protected function get_endpoint(): UriInterface {
+ return new Uri(get_config('aiprovider_openai', 'action_generate_image_endpoint'));
}
#[\Override]
protected function get_model(): string {
- return 'dall-e-3';
+ return get_config('aiprovider_openai', 'action_generate_image_model');
}
#[\Override]
diff --git a/ai/provider/openai/classes/process_generate_text.php b/ai/provider/openai/classes/process_generate_text.php
index 592662b5505..eed7be703e2 100644
--- a/ai/provider/openai/classes/process_generate_text.php
+++ b/ai/provider/openai/classes/process_generate_text.php
@@ -32,12 +32,17 @@ use Psr\Http\Message\UriInterface;
class process_generate_text extends abstract_processor {
#[\Override]
protected function get_endpoint(): UriInterface {
- return new Uri('https://api.openai.com/v1/chat/completions');
+ return new Uri(get_config('aiprovider_openai', 'action_generate_text_endpoint'));
}
#[\Override]
protected function get_model(): string {
- return 'gpt-4o';
+ return get_config('aiprovider_openai', 'action_generate_text_model');
+ }
+
+ #[\Override]
+ protected function get_system_instruction(): string {
+ return get_config('aiprovider_openai', 'action_generate_text_systeminstruction');
}
#[\Override]
@@ -53,7 +58,7 @@ class process_generate_text extends abstract_processor {
$requestobj->user = $userid;
// If there is a system string available, use it.
- $systeminstruction = $this->action->get_system_instruction();
+ $systeminstruction = $this->get_system_instruction();
if (!empty($systeminstruction)) {
$systemobj = new \stdClass();
$systemobj->role = 'system';
diff --git a/ai/provider/openai/classes/process_summarise_text.php b/ai/provider/openai/classes/process_summarise_text.php
index cf36f53c621..c16530f11e9 100644
--- a/ai/provider/openai/classes/process_summarise_text.php
+++ b/ai/provider/openai/classes/process_summarise_text.php
@@ -16,6 +16,9 @@
namespace aiprovider_openai;
+use GuzzleHttp\Psr7\Uri;
+use Psr\Http\Message\UriInterface;
+
/**
* Class process text summarisation.
*
@@ -24,4 +27,18 @@ namespace aiprovider_openai;
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class process_summarise_text extends process_generate_text {
+ #[\Override]
+ protected function get_endpoint(): UriInterface {
+ return new Uri(get_config('aiprovider_openai', 'action_summarise_text_endpoint'));
+ }
+
+ #[\Override]
+ protected function get_model(): string {
+ return get_config('aiprovider_openai', 'action_summarise_text_model');
+ }
+
+ #[\Override]
+ protected function get_system_instruction(): string {
+ return get_config('aiprovider_openai', 'action_summarise_text_systeminstruction');
+ }
}
diff --git a/ai/provider/openai/classes/provider.php b/ai/provider/openai/classes/provider.php
index 29795dd45e4..5a7c0f89025 100644
--- a/ai/provider/openai/classes/provider.php
+++ b/ai/provider/openai/classes/provider.php
@@ -143,4 +143,68 @@ class provider extends \core_ai\provider {
return true;
}
+
+ /**
+ * Get any action settings for this provider.
+ *
+ * @param string $action The action class name.
+ * @param \admin_root $ADMIN The admin root object.
+ * @param string $section The section name.
+ * @param bool $hassiteconfig Whether the current user has moodle/site:config capability.
+ * @return array An array of settings.
+ */
+ public function get_action_settings(
+ string $action,
+ \admin_root $ADMIN,
+ string $section,
+ bool $hassiteconfig
+ ): array {
+ $actionname = substr($action, (strrpos($action, '\\') + 1));
+ $settings = [];
+ if ($actionname === 'generate_text' || $actionname === 'summarise_text') {
+ // Add the model setting.
+ $settings[] = new \admin_setting_configtext(
+ "aiprovider_openai/action_{$actionname}_model",
+ new \lang_string("action:{$actionname}:model", 'aiprovider_openai'),
+ new \lang_string("action:{$actionname}:model_desc", 'aiprovider_openai'),
+ 'gpt-4o',
+ PARAM_TEXT,
+ );
+ // Add API endpoint.
+ $settings[] = new \admin_setting_configtext(
+ "aiprovider_openai/action_{$actionname}_endpoint",
+ new \lang_string("action:{$actionname}:endpoint", 'aiprovider_openai'),
+ new \lang_string("action:{$actionname}:endpoint_desc", 'aiprovider_openai'),
+ 'https://api.openai.com/v1/chat/completions',
+ PARAM_URL,
+ );
+ // Add system instruction settings.
+ $settings[] = new \admin_setting_configtextarea(
+ "aiprovider_openai/action_{$actionname}_systeminstruction",
+ new \lang_string("action:{$actionname}:systeminstruction", 'aiprovider_openai'),
+ new \lang_string("action:{$actionname}:systeminstruction_desc", 'aiprovider_openai'),
+ $action::get_system_instruction(),
+ PARAM_TEXT
+ );
+ } else if ($actionname === 'generate_image') {
+ // Add the model setting.
+ $settings[] = new \admin_setting_configtext(
+ "aiprovider_openai/action_{$actionname}_model",
+ new \lang_string("action:{$actionname}:model", 'aiprovider_openai'),
+ new \lang_string("action:{$actionname}:model_desc", 'aiprovider_openai'),
+ 'dall-e-3',
+ PARAM_TEXT,
+ );
+ // Add API endpoint.
+ $settings[] = new \admin_setting_configtext(
+ "aiprovider_openai/action_{$actionname}_endpoint",
+ new \lang_string("action:{$actionname}:endpoint", 'aiprovider_openai'),
+ new \lang_string("action:{$actionname}:endpoint_desc", 'aiprovider_openai'),
+ 'https://api.openai.com/v1/images/generations',
+ PARAM_URL,
+ );
+ }
+
+ return $settings;
+ }
}
diff --git a/ai/provider/openai/lang/en/aiprovider_openai.php b/ai/provider/openai/lang/en/aiprovider_openai.php
index e935248f44f..9e4937003de 100644
--- a/ai/provider/openai/lang/en/aiprovider_openai.php
+++ b/ai/provider/openai/lang/en/aiprovider_openai.php
@@ -22,6 +22,22 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
+$string['action:generate_image:endpoint'] = 'API endpoint';
+$string['action:generate_image:endpoint_desc'] = 'The API endpoint the provider uses for this action.';
+$string['action:generate_image:model'] = 'Image generation model';
+$string['action:generate_image:model_desc'] = 'The model used to generate images from user prompts.';
+$string['action:generate_text:endpoint'] = 'API endpoint';
+$string['action:generate_text:endpoint_desc'] = 'The API endpoint for the provider uses for this action.';
+$string['action:generate_text:model'] = 'Text generation model';
+$string['action:generate_text:model_desc'] = 'The model used to generate the text response.';
+$string['action:generate_text:systeminstruction'] = 'System instruction';
+$string['action:generate_text:systeminstruction_desc'] = 'This instruction is provided together with the user prompt for this action. It provides information to the AI model on how to generate the response.';
+$string['action:summarise_text:endpoint'] = 'API endpoint';
+$string['action:summarise_text:endpoint_desc'] = 'The API endpoint the provider uses for this action.';
+$string['action:summarise_text:model'] = 'Text summarisation model';
+$string['action:summarise_text:model_desc'] = 'The model used to summarise the provided text.';
+$string['action:summarise_text:systeminstruction'] = 'System instruction';
+$string['action:summarise_text:systeminstruction_desc'] = 'This instruction is provided together with the user prompt for this action. It provides information to the AI model on how to generate the response.';
$string['apikey'] = 'OpenAI API key';
$string['apikey_desc'] = 'Enter your OpenAI API key. You can get one from here';
$string['enableglobalratelimit'] = 'Enable global rate limiting';
diff --git a/ai/provider/openai/tests/process_generate_text_test.php b/ai/provider/openai/tests/process_generate_text_test.php
index bcacf60c6ee..910683fd77c 100644
--- a/ai/provider/openai/tests/process_generate_text_test.php
+++ b/ai/provider/openai/tests/process_generate_text_test.php
@@ -83,8 +83,8 @@ final class process_generate_text_test extends \advanced_testcase {
$body = (object) json_decode($request->getBody()->getContents());
- $this->assertEquals('This is a test prompt', $body->messages[0]->content);
- $this->assertEquals('user', $body->messages[0]->role);
+ $this->assertEquals('This is a test prompt', $body->messages[1]->content);
+ $this->assertEquals('user', $body->messages[1]->role);
}
/**
diff --git a/ai/provider/openai/version.php b/ai/provider/openai/version.php
index 4722e9e0870..3d8e25da439 100644
--- a/ai/provider/openai/version.php
+++ b/ai/provider/openai/version.php
@@ -25,6 +25,6 @@
defined('MOODLE_INTERNAL') || die();
$plugin->component = 'aiprovider_openai';
-$plugin->version = 2024060900;
+$plugin->version = 2024091300;
$plugin->requires = 2024041600;
$plugin->maturity = MATURITY_STABLE;
diff --git a/lang/en/ai.php b/lang/en/ai.php
index 7089d37eb21..56328c2625c 100644
--- a/lang/en/ai.php
+++ b/lang/en/ai.php
@@ -27,6 +27,9 @@ $string['action_generate_image'] = 'Generate image';
$string['action_generate_image_desc'] = 'Generates an image based on a text prompt.';
$string['action_generate_text'] = 'Generate text';
$string['action_generate_text_desc'] = 'Generates text based on a text prompt.';
+$string['action_generate_text_instruction'] = 'You will receive a text input from the user. Your task is to generate text based on their request. Follow these important instructions:
+ 1. Return the summary in plain text only.
+ 2. Do not include any markdown formatting, greetings, or platitudes.';
$string['action_summarise_text'] = 'Summarise text';
$string['action_summarise_text_desc'] = 'Summarises text based on provided input text.';
$string['action_summarise_text_instruction'] = 'You will receive a text input from the user. Your task is to summarize the provided text. Follow these guidelines:
@@ -41,6 +44,9 @@ Important Instructions:
Ensure the summary is easy to read and effectively conveys the main points of the original text.';
$string['action_translate_text'] = 'Translate text';
$string['action_translate_text_desc'] = 'Translate provided text from one language to another.';
+$string['actionsettingprovider'] = '{$a} action settings';
+$string['actionsettingprovider_desc'] = 'These settings are specifc to this action for this provider.
+They are used to control how the action is processed by the provider.';
$string['ai'] = 'AI';
$string['aiplacementsettings'] = 'Manage settings for AI placements';
$string['aiprovidersettings'] = 'Manage settings for AI providers';
diff --git a/lib/classes/plugininfo/aiprovider.php b/lib/classes/plugininfo/aiprovider.php
index 01bf9e9b43c..1f443358b67 100644
--- a/lib/classes/plugininfo/aiprovider.php
+++ b/lib/classes/plugininfo/aiprovider.php
@@ -18,7 +18,6 @@ namespace core\plugininfo;
use core_plugin_manager;
use moodle_url;
-use core\lang_string;
/**
* AI placement plugin info class.
@@ -103,18 +102,36 @@ class aiprovider extends base {
} else {
// Provider action settings heading.
$settings->add(new \admin_setting_heading("{$section}/generals",
- new lang_string('provideractionsettings', 'core_ai'),
- new lang_string('provideractionsettings_desc', 'core_ai')));
+ new \lang_string('provideractionsettings', 'core_ai'),
+ new \lang_string('provideractionsettings_desc', 'core_ai')));
// Load the setting table of actions that this provider supports.
$settings->add(new \core_ai\admin\admin_setting_action_manager(
$section,
\core_ai\table\aiprovider_action_management_table::class,
'manageaiproviders',
- new lang_string('manageaiproviders', 'core_ai'),
+ new \lang_string('manageaiproviders', 'core_ai'),
));
}
-
$ADMIN->add($parentnodename, $settings);
+ // Load any action settings for this provider.
+ $providerclass = "\\{$section}\\provider";
+ $provider = new $providerclass();
+ $actionlist = $provider->get_action_list();
+ foreach ($actionlist as $action) {
+ $actionsettings = $provider->get_action_settings($action, $ADMIN, $section, $hassiteconfig);
+ if (!empty($actionsettings)) {
+ $actionname = substr($action, (strrpos($action, '\\') + 1));
+ $settings = new \admin_settingpage($section . '_' . $actionname, $action::get_name(), 'moodle/site:config', true);
+ $setting = new \admin_setting_heading("{$section}_actions/heading",
+ new \lang_string('actionsettingprovider', 'core_ai', $provider->get_name()),
+ new \lang_string('actionsettingprovider_desc', 'core_ai'));
+ $settings->add($setting);
+ foreach ($actionsettings as $setting) {
+ $settings->add($setting);
+ }
+ $ADMIN->add('root', $settings);
+ }
+ }
}
/**