-
Notifications
You must be signed in to change notification settings - Fork 305
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1117 from mattporritt/AI_subsystem
Docs for AI Subsystem for: MDL-82627
- Loading branch information
Showing
4 changed files
with
663 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
--- | ||
title: AI Plugins | ||
tags: | ||
- AI | ||
- LLM | ||
- Provider | ||
- Placement | ||
--- | ||
|
||
The AI subsystem in the LMS is designed to be extensible, allowing for the integration of external AI services. | ||
This is achieved through the use of AI plugins, which are divided into two types: Providers and Placements. | ||
|
||
### Placements | ||
|
||
The aim of Placements is to provide a consistent UX and UI for users when they use AI backed functionality. | ||
|
||
Placement plugins leverage the functionality of the other components of the AI subsystem. | ||
This means plugin authors can focus on how users interact with the AI functionality, without needing to | ||
implement the AI functionality itself. | ||
|
||
Because Placements are LMS plugins in their own right and are not "other" types of LMS plugins, | ||
it gives great flexibility in how AI functionality is presented to users. | ||
|
||
See the [Placements](/apis/plugintypes/ai/placement.md) documentation for more information | ||
on developing Placement plugins. | ||
|
||
### Providers | ||
|
||
Provider plugins are the interface between the LMS AI subsystem and external AI systems. | ||
Their focus is on converting the data requested by an Action into the format needed by the | ||
external AI services API, and then correctly providing the response back from the AI | ||
in an Action Response object. | ||
|
||
Because of this design the Providers that provide the AI Actions can be swapped out, mix and matched | ||
or upgraded; both without the need to update the Placement code and without the need to change the | ||
way users interact with the functionality. | ||
|
||
See the [Providers](/apis/plugintypes/ai/provider.md) documentation for more information | ||
on developing Provider plugins. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
--- | ||
title: Placements | ||
tags: | ||
- AI | ||
- LLM | ||
- Placement | ||
--- | ||
|
||
The aim of Placements is to provide a consistent UX and UI for users when they use AI backed functionality. | ||
|
||
Placement plugins leverage the functionality of the other components of the AI subsystem. | ||
This means plugin authors can focus on how users interact with the AI functionality, without needing to | ||
implement the AI functionality itself. | ||
|
||
Because Placements are LMS plugins in their own right and are not "other" types of LMS plugins, | ||
it gives great flexibility in how AI functionality is presented to users. | ||
|
||
:::warning The Golden Rule: | ||
|
||
Placements DO NOT know about Providers, and Providers DO NOT know about Placements. | ||
Everything should go via the Manager. | ||
|
||
::: | ||
|
||
Placements are defined as classes in their own namespace according to their plugin name. | ||
The naming convention for Action classes is `aiplacement_<plugin name>`, | ||
for example: `aiplacement_editor`. With corresponding namespaces. | ||
|
||
Each Placement MUST inherit from the `\core_ai\placement` abstract class. | ||
They must also implement the following methods: | ||
|
||
- `get_action_list(): array` This is the list of Actions that are supported by this Placement, for example the `aiplacement_editor` plugin defines this as: | ||
|
||
```php | ||
public function get_action_list(): array { | ||
return [ | ||
\core_ai\aiactions\generate_text::class, | ||
\core_ai\aiactions\generate_image::class, | ||
]; | ||
} | ||
``` | ||
|
||
## Capabilities and Permissions | ||
|
||
Placements are responsible for determining who and where a Placement (and by extension an Action can be used). | ||
It is not the job of Actions or Providers to determine access. | ||
|
||
## Action Processing | ||
|
||
The following is the basic workflow in order for a placement to have an action processed for a user request: | ||
|
||
- The Placement instantiates a new action object of type they wish to use. | ||
- The action must be instantiated and passing it the required data. Each action will define what configuration it needs. As an example: | ||
|
||
```php | ||
// Prepare the action. | ||
$action = new \core_ai\aiactions\generate_image( | ||
contextid: $contextid, | ||
userid: $USER->id, | ||
prompttext: $prompttext, | ||
quality: $quality, | ||
aspectratio: $aspectratio, | ||
numimages: $numimages, | ||
style: $style, | ||
); | ||
``` | ||
|
||
- The Placement then instantiates the Manager class and calls `process_action()` | ||
- passing in the configured action object: | ||
|
||
```php | ||
// Send the action to the AI manager. | ||
$manager = \core\di::get(\core_ai\manager::class); | ||
$response = $manager->process_action($action); | ||
```` | ||
|
||
- The process_action() method will then return a response object (instance of `responses\response_base`). | ||
- It is up to the Placement to check for success (or not) of the response and pass the result back to the | ||
user or for further processing. | ||
|
||
## Plugin Structure | ||
|
||
Placement plugins reside in the `ai/placement` directory. | ||
|
||
Each Placement is in a separate subdirectory and consists of a number of mandatory files and any other | ||
files the developer is going to use. | ||
|
||
The following is the typical structure of a Placement plugin, using the Editor Placement as an example: | ||
|
||
```bash | ||
. | ||
├── classes | ||
│ ├── external | ||
│ │ ├── generate_image.php | ||
│ │ └── generate_text.php | ||
│ ├── placement.php | ||
│ └── privacy | ||
│ └── provider.php | ||
├── db | ||
│ ├── access.php | ||
│ └── services.php | ||
├── lang | ||
│ └── en | ||
│ └── aiplacement_editor.php | ||
└── version.php | ||
|
||
``` | ||
|
||
## Settings | ||
|
||
Settings for the Placement should be defined in the `settings.php` file. | ||
Each Placement plugin should create a new admin settings page using `core_ai\admin\admin_settingspage_provider` class. | ||
|
||
This is the same as for Provider plugins, for example: | ||
|
||
```php | ||
use core_ai\admin\admin_settingspage_provider; | ||
|
||
if ($hassiteconfig) { | ||
// Placement specific settings heading. | ||
$settings = new admin_settingspage_provider( | ||
'aiprovider_openai', | ||
new lang_string('pluginname', 'aiprovider_openai'), | ||
'moodle/site:config', | ||
true, | ||
); | ||
|
||
... | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,228 @@ | ||
--- | ||
title: Providers | ||
tags: | ||
- AI | ||
- LLM | ||
- Provider | ||
--- | ||
Providers are the interface between the LMS AI subsystem and external AI systems. | ||
Their focus should be on converting the data requested by an Action into the format needed | ||
by the external AI services API, and then correctly providing the response back from the AI | ||
in an Action Response object. | ||
|
||
:::warning The Golden Rule: | ||
|
||
Placements DO NOT know about Providers, and Providers DO NOT know about Placements. | ||
Everything should go via the Manager. | ||
|
||
::: | ||
|
||
Providers are defined as classes in their own namespace according to their plugin name. | ||
The naming convention for Action classes is `aiprovider_<plugin name>`, | ||
for example: `aiprovider_openai`, `aiprovider_azureai`. With corresponding namespaces. | ||
|
||
Each Provider MUST inherit from the `\core_ai\provider` abstract class. | ||
They must also implement the following methods: | ||
|
||
- `get_action_list(): array` This is the list of Actions that are supported by this Provider, for example the `aiprovider_openai` plugin defines this as: | ||
|
||
```php | ||
public function get_action_list(): array { | ||
return [ | ||
\core_ai\aiactions\generate_text::class, | ||
\core_ai\aiactions\generate_image::class, | ||
\core_ai\aiactions\summarise_text::class, | ||
]; | ||
} | ||
``` | ||
|
||
## Process classes | ||
|
||
For each action supported by the provider, the provider plugin **MUST** implement a `process_<action>` class, | ||
where `<action>` is the name of the action. For example: `process_generate_image`. | ||
|
||
Every process action class **MUST** inherit from the `\core_ai\process_base` abstract class. | ||
|
||
The process action class **MUST** implement a `process()` method. This method is responsible for | ||
converting the data requested by an Action into the format needed by the external AI services API, | ||
and then correctly providing the response back from the AI in an Action Response object. | ||
|
||
The process action classes and process method are expected by the manager to exist and be callable. | ||
|
||
As most provider plugins will support more than one action, it is recommended to create an | ||
`abstract_processor` class that inherits from the `\core_ai\process_base` class and then have each | ||
process action class inherit from this abstract class. | ||
|
||
For example, the `aiprovider_openai` plugin defines an `abstract_processor` class that inherits from | ||
the `\core_ai\process_base` class and then the `process_generate_image`, `process_generate_text` and | ||
`process_summarise_text` classes inherit from this abstract class. | ||
|
||
This can be visualised as follows: | ||
|
||
```mermaid | ||
graph TD; | ||
A[process_base] --> B[abstract_processor] | ||
B[abstract_processor] --> C[process_generate_image] | ||
B --> D[process_generate_text] | ||
B --> E[process_summarise_text] | ||
``` | ||
|
||
Apart from this, Providers are free to define their own structure. It should be kept in mind that Providers | ||
are designed to be a "thin wrapper" around the external AI systems API. They shouldn't store data, | ||
or have their own UI elements (beyond what is required for configuration). | ||
|
||
## Plugin Structure | ||
|
||
Provider plugins reside in the `ai/provider` directory. | ||
|
||
Each Provider is in a separate subdirectory and consists of a number of mandatory files and any other | ||
files the developer is going to use. | ||
|
||
The following is the typical structure of a Provider plugin, using the OpenAI Provider as an example: | ||
|
||
```bash | ||
. | ||
├── classes | ||
│ ├── abstract_processor.php | ||
│ ├── privacy | ||
│ │ └── provider.php | ||
│ ├── process_generate_image.php | ||
│ ├── process_generate_text.php | ||
│ ├── process_summarise_text.php | ||
│ └── provider.php | ||
├── lang | ||
│ └── en | ||
│ └── aiprovider_openai.php | ||
├── settings.php | ||
├── tests | ||
│ ├── fixtures | ||
│ │ ├── image_request_success.json | ||
│ │ ├── test.jpg | ||
│ │ └── text_request_success.json | ||
│ ├── process_generate_image_test.php | ||
│ ├── process_generate_text_test.php | ||
│ ├── process_summarise_text_test.php | ||
│ └── provider_test.php | ||
└── version.php | ||
|
||
``` | ||
|
||
## Settings | ||
|
||
Settings for the Provider should be defined in the `settings.php` file. | ||
Each Provider plugin should create a new admin settings page using `core_ai\admin\admin_settingspage_provider` class. | ||
|
||
For example, the `aiprovider_openai` plugin defines this: | ||
|
||
```php | ||
use core_ai\admin\admin_settingspage_provider; | ||
|
||
if ($hassiteconfig) { | ||
// Provider specific settings heading. | ||
$settings = new admin_settingspage_provider( | ||
'aiprovider_openai', | ||
new lang_string('pluginname', 'aiprovider_openai'), | ||
'moodle/site:config', | ||
true, | ||
); | ||
|
||
... | ||
``` | ||
|
||
## Rate Limiting | ||
|
||
It is recommended that Providers implement rate limiting to prevent abuse of the external AI services. | ||
|
||
To assist with this, the AI subsystem provides a `core_ai\rate_limiter` class that can be used to implement rate limiting. | ||
This class supports both user and system level rate limiting. | ||
|
||
This should be implemented in a `is_request_allowed()` method in the Provider class. For example, from the | ||
`aiprovider_openai` plugin: | ||
|
||
```php | ||
/** | ||
* Check if the request is allowed by the rate limiter. | ||
* | ||
* @param aiactions\base $action The action to check. | ||
* @return array|bool True on success, array of error details on failure. | ||
*/ | ||
public function is_request_allowed(aiactions\base $action): array|bool { | ||
$ratelimiter = \core\di::get(rate_limiter::class); | ||
$component = \core\component::get_component_from_classname(get_class($this)); | ||
|
||
// Check the user rate limit. | ||
if ($this->enableuserratelimit) { | ||
if (!$ratelimiter->check_user_rate_limit( | ||
component: $component, | ||
ratelimit: $this->userratelimit, | ||
userid: $action->get_configuration('userid') | ||
)) { | ||
return [ | ||
'success' => false, | ||
'errorcode' => 429, | ||
'errormessage' => 'User rate limit exceeded', | ||
]; | ||
} | ||
} | ||
|
||
// Check the global rate limit. | ||
if ($this->enableglobalratelimit) { | ||
if (!$ratelimiter->check_global_rate_limit( | ||
component: $component, | ||
ratelimit: $this->globalratelimit | ||
)) { | ||
return [ | ||
'success' => false, | ||
'errorcode' => 429, | ||
'errormessage' => 'Global rate limit exceeded', | ||
]; | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
``` | ||
|
||
If implementing rate limiting, settings for the rate limits should be provided in the plugin settings. | ||
|
||
For example, the `aiprovider_openai` plugin provides settings for the user and global rate limits: | ||
|
||
```php | ||
// Setting to enable/disable global rate limiting. | ||
$settings->add(new admin_setting_configcheckbox( | ||
'aiprovider_openai/enableglobalratelimit', | ||
new lang_string('enableglobalratelimit', 'aiprovider_openai'), | ||
new lang_string('enableglobalratelimit_desc', 'aiprovider_openai'), | ||
0, | ||
)); | ||
|
||
// Setting to set how many requests per hour are allowed for the global rate limit. | ||
// Should only be enabled when global rate limiting is enabled. | ||
$settings->add(new admin_setting_configtext( | ||
'aiprovider_openai/globalratelimit', | ||
new lang_string('globalratelimit', 'aiprovider_openai'), | ||
new lang_string('globalratelimit_desc', 'aiprovider_openai'), | ||
100, | ||
PARAM_INT, | ||
)); | ||
$settings->hide_if('aiprovider_openai/globalratelimit', 'aiprovider_openai/enableglobalratelimit', 'eq', 0); | ||
|
||
// Setting to enable/disable user rate limiting. | ||
$settings->add(new admin_setting_configcheckbox( | ||
'aiprovider_openai/enableuserratelimit', | ||
new lang_string('enableuserratelimit', 'aiprovider_openai'), | ||
new lang_string('enableuserratelimit_desc', 'aiprovider_openai'), | ||
0, | ||
)); | ||
|
||
// Setting to set how many requests per hour are allowed for the user rate limit. | ||
// Should only be enabled when user rate limiting is enabled. | ||
$settings->add(new admin_setting_configtext( | ||
'aiprovider_openai/userratelimit', | ||
new lang_string('userratelimit', 'aiprovider_openai'), | ||
new lang_string('userratelimit_desc', 'aiprovider_openai'), | ||
10, | ||
PARAM_INT, | ||
)); | ||
$settings->hide_if('aiprovider_openai/userratelimit', 'aiprovider_openai/enableuserratelimit', 'eq', 0); | ||
``` |
Oops, something went wrong.