diff --git a/ai/classes/manager.php b/ai/classes/manager.php index 99290b63cb9..acf2fcabf98 100644 --- a/ai/classes/manager.php +++ b/ai/classes/manager.php @@ -73,13 +73,15 @@ class manager { foreach ($actions as $action) { $providers[$action] = []; foreach ($plugins as $plugin) { - if ($enabledonly && (!$plugin->is_enabled() || !static::is_action_enabled($plugin->component, $action))) { + $pluginclassname = static::get_ai_plugin_classname($plugin->component); + $provider = new $pluginclassname(); + // Check the plugin is enabled and the provider is configured before making the action available. + if ($enabledonly && (!$plugin->is_enabled() || !static::is_action_enabled($plugin->component, $action)) || + $enabledonly && !$provider->is_provider_configured()) { continue; } - $pluginclassname = static::get_ai_plugin_classname($plugin->component); - $plugin = new $pluginclassname(); - if (in_array($action, $plugin->get_action_list())) { - $providers[$action][] = $plugin; + if (in_array($action, $provider->get_action_list())) { + $providers[$action][] = $provider; } } } diff --git a/ai/classes/provider.php b/ai/classes/provider.php index 1dc1afc5d61..b53b3687e17 100644 --- a/ai/classes/provider.php +++ b/ai/classes/provider.php @@ -71,4 +71,13 @@ abstract class provider { ): array { return []; } + + /** + * Check if a provider has the minimal configuration to work. + * + * @return bool Return true if configured. + */ + public function is_provider_configured(): bool { + return false; + } } diff --git a/ai/placement/courseassist/tests/behat/course_assist_summarise.feature b/ai/placement/courseassist/tests/behat/course_assist_summarise.feature index fff39485a70..52e2b6f5711 100644 --- a/ai/placement/courseassist/tests/behat/course_assist_summarise.feature +++ b/ai/placement/courseassist/tests/behat/course_assist_summarise.feature @@ -26,6 +26,9 @@ Feature: AI Course assist summarise | aiplacement/courseassist:summarise_text | Prohibit | custom2 | Course | C1 | And I log in as "admin" And I enable "openai" "aiprovider" plugin + And the following config values are set as admin: + | apikey | 123 | aiprovider_openai | + | orgid | abc | aiprovider_openai | And I enable "courseassist" "aiplacement" plugin @javascript diff --git a/ai/placement/courseassist/tests/utils_test.php b/ai/placement/courseassist/tests/utils_test.php index c3111fe4e14..056c49546da 100644 --- a/ai/placement/courseassist/tests/utils_test.php +++ b/ai/placement/courseassist/tests/utils_test.php @@ -46,6 +46,8 @@ final class utils_test extends \advanced_testcase { $this->assertFalse(utils::is_course_assist_available($context)); set_config('enabled', 1, 'aiprovider_openai'); + set_config('apikey', '123', 'aiprovider_openai'); + set_config('orgid', 'abc', 'aiprovider_openai'); // Plugin is not enabled. $this->setUser($user1); @@ -55,7 +57,6 @@ final class utils_test extends \advanced_testcase { // Plugin is enabled but user does not have capability. assign_capability('aiplacement/courseassist:summarise_text', CAP_PROHIBIT, $teacherrole->id, $context); $this->setUser($user2); - set_config('enabled', 1, 'aiprovider_openai'); set_config('enabled', 1, 'aiplacement_courseassist'); $this->assertFalse(utils::is_course_assist_available($context)); diff --git a/ai/placement/editor/tests/utils_test.php b/ai/placement/editor/tests/utils_test.php index 685254cf3c1..d3fb3e75ef8 100644 --- a/ai/placement/editor/tests/utils_test.php +++ b/ai/placement/editor/tests/utils_test.php @@ -72,6 +72,8 @@ final class utils_test extends \advanced_testcase { )); set_config('enabled', 1, 'aiprovider_openai'); + set_config('apikey', '123', 'aiprovider_openai'); + set_config('orgid', 'abc', 'aiprovider_openai'); // Plugin is not enabled. $this->setUser($this->users[1]); @@ -85,7 +87,6 @@ final class utils_test extends \advanced_testcase { // Plugin is enabled but user does not have capability. assign_capability("aiplacement/editor:{$actionname}", CAP_PROHIBIT, $this->teacherrole->id, $this->context); $this->setUser($this->users[2]); - set_config('enabled', 1, 'aiprovider_openai'); set_config('enabled', 1, 'aiplacement_editor'); $this->assertFalse(utils::is_html_editor_placement_action_available( context: $this->context, diff --git a/ai/provider/azureai/classes/provider.php b/ai/provider/azureai/classes/provider.php index 4d05d825bb4..e689070da23 100644 --- a/ai/provider/azureai/classes/provider.php +++ b/ai/provider/azureai/classes/provider.php @@ -190,4 +190,12 @@ class provider extends \core_ai\provider { return $settings; } + /** + * Check this provider has the minimal configuration to work. + * + * @return bool Return true if configured. + */ + public function is_provider_configured(): bool { + return !empty($this->apikey) && !empty($this->apiendpoint); + } } diff --git a/ai/provider/azureai/tests/provider_test.php b/ai/provider/azureai/tests/provider_test.php index d6140da2c10..6bc549074f1 100644 --- a/ai/provider/azureai/tests/provider_test.php +++ b/ai/provider/azureai/tests/provider_test.php @@ -110,4 +110,27 @@ final class provider_test extends \advanced_testcase { $this->assertFalse($result['success']); $this->assertEquals('Global rate limit exceeded', $result['errormessage']); } + + /** + * Test is_provider_configured. + */ + public function test_is_provider_configured(): void { + $this->resetAfterTest(); + + // No configured values. + $provider = new \aiprovider_azureai\provider(); + $this->assertFalse($provider->is_provider_configured()); + + // Partially configured values. + set_config('apikey', '123', 'aiprovider_azureai'); + set_config('endpoint', '', 'aiprovider_azureai'); + $provider = new \aiprovider_azureai\provider(); + $this->assertFalse($provider->is_provider_configured()); + + // Properly configured values. + set_config('apikey', '123', 'aiprovider_azureai'); + set_config('endpoint', 'abc', 'aiprovider_azureai'); + $provider = new \aiprovider_azureai\provider(); + $this->assertTrue($provider->is_provider_configured()); + } } diff --git a/ai/provider/openai/classes/provider.php b/ai/provider/openai/classes/provider.php index 07847687136..5573187fa6f 100644 --- a/ai/provider/openai/classes/provider.php +++ b/ai/provider/openai/classes/provider.php @@ -207,4 +207,13 @@ class provider extends \core_ai\provider { return $settings; } + + /** + * Check this provider has the minimal configuration to work. + * + * @return bool Return true if configured. + */ + public function is_provider_configured(): bool { + return !empty($this->apikey) && !empty($this->orgid); + } } diff --git a/ai/provider/openai/tests/provider_test.php b/ai/provider/openai/tests/provider_test.php index d1a9ae44ae6..be888d3f9b2 100644 --- a/ai/provider/openai/tests/provider_test.php +++ b/ai/provider/openai/tests/provider_test.php @@ -110,4 +110,27 @@ final class provider_test extends \advanced_testcase { $this->assertFalse($result['success']); $this->assertEquals('Global rate limit exceeded', $result['errormessage']); } + + /** + * Test is_provider_configured. + */ + public function test_is_provider_configured(): void { + $this->resetAfterTest(); + + // No configured values. + $provider = new \aiprovider_openai\provider(); + $this->assertFalse($provider->is_provider_configured()); + + // Partially configured values. + set_config('apikey', '123', 'aiprovider_openai'); + set_config('orgid', '', 'aiprovider_openai'); + $provider = new \aiprovider_openai\provider(); + $this->assertFalse($provider->is_provider_configured()); + + // Properly configured values. + set_config('apikey', '123', 'aiprovider_openai'); + set_config('orgid', 'abc', 'aiprovider_openai'); + $provider = new \aiprovider_openai\provider(); + $this->assertTrue($provider->is_provider_configured()); + } } diff --git a/ai/tests/behat/admin.feature b/ai/tests/behat/admin.feature index acc657a3e7f..666ae989da8 100644 --- a/ai/tests/behat/admin.feature +++ b/ai/tests/behat/admin.feature @@ -32,6 +32,9 @@ Feature: An administrator can manage AI subsystem settings And I navigate to "AI > AI providers" in site administration When I toggle the "Enable OpenAI API Provider" admin switch "on" And I should see "OpenAI API Provider enabled." + And the following config values are set as admin: + | apikey | 123 | aiprovider_openai | + | orgid | abc | aiprovider_openai | And I navigate to "AI > AI placements" in site administration And I click on the "Settings" link in the table row containing "HTML Text Editor Placement" Then I should not see "This action is unavailable." @@ -52,6 +55,9 @@ Feature: An administrator can manage AI subsystem settings And I navigate to "AI > AI providers" in site administration When I toggle the "Enable OpenAI API Provider" admin switch "on" And I should see "OpenAI API Provider enabled." + And the following config values are set as admin: + | apikey | 123 | aiprovider_openai | + | orgid | abc | aiprovider_openai | And I click on the "Settings" link in the table row containing "OpenAI API Provider" And I toggle the "Generate text" admin switch "off" And I navigate to "AI > AI placements" in site administration diff --git a/ai/tests/manager_test.php b/ai/tests/manager_test.php index 3b5de6d292a..d2e33ab9d68 100644 --- a/ai/tests/manager_test.php +++ b/ai/tests/manager_test.php @@ -74,6 +74,8 @@ final class manager_test extends \advanced_testcase { public function test_get_providers_for_actions(): void { $this->resetAfterTest(); set_config('enabled', 1, 'aiprovider_openai'); + set_config('apikey', '123', 'aiprovider_openai'); + set_config('orgid', 'abc', 'aiprovider_openai'); $manager = \core\di::get(manager::class); $actions = [ @@ -141,7 +143,11 @@ final class manager_test extends \advanced_testcase { // Enable the providers. set_config('enabled', 1, 'aiprovider_openai'); + set_config('apikey', '123', 'aiprovider_openai'); + set_config('orgid', 'abc', 'aiprovider_openai'); set_config('enabled', 1, 'aiprovider_azureai'); + set_config('apikey', '123', 'aiprovider_azureai'); + set_config('endpoint', 'abc', 'aiprovider_azureai'); $managermock = $this->getMockBuilder(manager::class) ->onlyMethods(['call_action_provider']) @@ -413,6 +419,8 @@ final class manager_test extends \advanced_testcase { // Enable the plugin, actions will be enabled by default when the plugin is enabled. $manager = \core_plugin_manager::resolve_plugininfo_class('aiprovider'); $manager::enable_plugin('openai', 1); + set_config('apikey', '123', 'aiprovider_openai'); + set_config('orgid', 'abc', 'aiprovider_openai'); // Should now be available. $result = manager::is_action_available($action); diff --git a/lib/editor/tiny/plugins/aiplacement/tests/behat/image.feature b/lib/editor/tiny/plugins/aiplacement/tests/behat/image.feature index e23c5f15b4a..a1ff074ba3d 100644 --- a/lib/editor/tiny/plugins/aiplacement/tests/behat/image.feature +++ b/lib/editor/tiny/plugins/aiplacement/tests/behat/image.feature @@ -29,6 +29,9 @@ Feature: Generate image using AI | aiplacement/editor:generate_text | Prohibit | user | System | | | aiplacement/editor:generate_image | Prohibit | custom2 | Course | C1 | And I enable "openai" "aiprovider" plugin + And the following config values are set as admin: + | apikey | 123 | aiprovider_openai | + | orgid | abc | aiprovider_openai | And I enable "editor" "aiplacement" plugin @javascript diff --git a/lib/editor/tiny/plugins/aiplacement/tests/behat/text.feature b/lib/editor/tiny/plugins/aiplacement/tests/behat/text.feature index e968667d4fd..3ab7ade0480 100644 --- a/lib/editor/tiny/plugins/aiplacement/tests/behat/text.feature +++ b/lib/editor/tiny/plugins/aiplacement/tests/behat/text.feature @@ -30,6 +30,9 @@ Feature: Generate text using AI | aiplacement/editor:generate_text | Prohibit | custom2 | Course | C1 | And I log in as "admin" And I enable "openai" "aiprovider" plugin + And the following config values are set as admin: + | apikey | 123 | aiprovider_openai | + | orgid | abc | aiprovider_openai | And I enable "editor" "aiplacement" plugin @javascript