Skip to content

Commit

Permalink
Plugin: AI Helper: Add feature to use AI Helper to create learning pa…
Browse files Browse the repository at this point in the history
…ths - refs GH#4594

Author: @christianbeeznest
  • Loading branch information
christianbeeznest committed Feb 7, 2023
1 parent e9c14f1 commit f86cf35
Show file tree
Hide file tree
Showing 6 changed files with 280 additions and 0 deletions.
89 changes: 89 additions & 0 deletions main/lp/LpAiHelper.php
@@ -0,0 +1,89 @@
<?php
/* For licensing terms, see /license.txt */

class LpAiHelper
{
/**
* AiHelper constructor.
* Requires plugin ai_helper to connect to the api.
*/
public function __construct()
{
if ('true' !== api_get_plugin_setting('ai_helper', 'tool_enable')) {
return false;
}
}

/**
* Get the form to generate Lp items using Ai Helper.
*/
public function aiHelperForm()
{
$form = new FormValidator(
'lp_ai_generate',
'post',
api_get_self()."?".api_get_cidreq(),
null
);
$form->addElement('header', get_lang('LpAiGenerator'));
$form->addElement('text', 'lp_name', [get_lang('LpAiTopic'), get_lang('LpAiTopicHelp')]);
$form->addRule('lp_name', get_lang('ThisFieldIsRequired'), 'required');
$form->addElement('number', 'nro_items', [get_lang('LpAiNumberOfItems'), get_lang('LpAiNumberOfItemsHelper')]);
$form->addRule('nro_items', get_lang('ThisFieldIsRequired'), 'required');
$form->addElement('number', 'words_count', [get_lang('LpAiWordsCount'), get_lang('LpAiWordsCountHelper')]);
$form->addRule('words_count', get_lang('ThisFieldIsRequired'), 'required');

$generateUrl = api_get_path(WEB_PLUGIN_PATH).'ai_helper/tool/learnpath.php';
$language = api_get_interface_language();
$courseCode = api_get_course_id();
$sessionId = api_get_session_id();
$redirectSuccess = api_get_path(WEB_CODE_PATH).'lp/lp_controller.php?'.api_get_cidreq().'&action=add_item&type=step&isStudentView=false&lp_id=';
$form->addHtml('<script>
$(function () {
$("#create-lp-ai").on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var btnGenerate = $(this);
var lpName = $("[name=\'lp_name\']").val();
var nroItems = parseInt($("[name=\'nro_items\']").val());
var wordsCount = parseInt($("[name=\'words_count\']").val());
var valid = (lpName != \'\' && nroItems > 0 && wordsCount > 0);
if (valid) {
btnGenerate.attr("disabled", true);
btnGenerate.text("'.get_lang('PleaseWaitThisCouldTakeAWhile').'");
$.getJSON("'.$generateUrl.'", {
"lp_name": lpName,
"nro_items": nroItems,
"words_count": wordsCount,
"language": "'.$language.'",
"course_code": "'.$courseCode.'",
"session_id": "'.$sessionId.'"
}).done(function (data) {
btnGenerate.attr("disabled", false);
btnGenerate.text("'.get_lang('Generate').'");
if (data.success && data.success == true) {
location.href = "'.$redirectSuccess.'" + data.lp_id;
} else {
alert("'.get_lang('NoSearchResults').'. '.get_lang('PleaseTryAgain').'");
}
});
}
});
});
</script>');

$form->addButton(
'create_lp_button',
get_lang('CreateLp'),
'',
'default',
'default',
null,
['id' => 'create-lp-ai']
);

echo $form->returnForm();
}
}
28 changes: 28 additions & 0 deletions main/lp/lp_add_ai_helper.php
@@ -0,0 +1,28 @@
<?php
/* For licensing terms, see /license.txt */

$this_section = SECTION_COURSES;
api_protect_course_script();

$interbreadcrumb[] = [
'url' => 'lp_controller.php?action=list&'.api_get_cidreq(),
'name' => get_lang('LearningPaths'),
];

Display::display_header(get_lang('CreateLpWithAiHelper'), 'Learnpath');

echo '<div class="actions">';
echo '<a href="lp_controller.php?'.api_get_cidreq().'">'.
Display::return_icon(
'back.png',
get_lang('ReturnToLearningPaths'),
'',
ICON_SIZE_MEDIUM
).'</a>';
echo '</div>';

$aiHelper = new LpAiHelper();

$aiHelper->aiHelperForm();

Display::display_footer();
6 changes: 6 additions & 0 deletions main/lp/lp_controller.php
Expand Up @@ -738,6 +738,12 @@ function(reponse) {
}
require 'lp_add_category.php';
break;
case 'ai_helper':
if (!$is_allowed_to_edit) {
api_not_allowed(true);
}
require 'lp_add_ai_helper.php';
break;
case 'move_up_category':
if (!$is_allowed_to_edit) {
api_not_allowed(true);
Expand Down
13 changes: 13 additions & 0 deletions main/lp/lp_list.php
Expand Up @@ -122,6 +122,19 @@ function confirmation(name) {
api_get_self().'?'.api_get_cidreq().'&action=add_lp_category'
);
}

if ('true' === api_get_plugin_setting('ai_helper', 'tool_enable')) {
$actionLeft .= Display::url(
Display::return_icon(
'help.png',
get_lang('CreateLpWithAiHelper'),
[],
ICON_SIZE_MEDIUM
),
api_get_self().'?'.api_get_cidreq().'&action=ai_helper'
);
}

$actions = Display::toolbarAction('actions-lp', [$actionLeft]);
}

Expand Down
40 changes: 40 additions & 0 deletions plugin/ai_helper/AiHelperPlugin.php
Expand Up @@ -45,6 +45,46 @@ public function getApiList()
return $list;
}

/**
* Get the completion text from openai.
*
* @return string
*/
public function openAiGetCompletionText(string $prompt)
{
require_once __DIR__.'/src/openai/OpenAi.php';

$apiKey = $this->get('api_key');
$organizationId = $this->get('organization_id');

$ai = new OpenAi($apiKey, $organizationId);

$temperature = 0.2;
$model = 'text-davinci-003';
$maxTokens = 2000;
$frequencyPenalty = 0;
$presencePenalty = 0.6;
$topP = 1.0;

$complete = $ai->completion([
'model' => $model,
'prompt' => $prompt,
'temperature' => $temperature,
'max_tokens' => $maxTokens,
'frequency_penalty' => $frequencyPenalty,
'presence_penalty' => $presencePenalty,
'top_p' => $topP,
]);

$result = json_decode($complete, true);
$resultText = '';
if (!empty($result['choices'])) {
$resultText = trim($result['choices'][0]['text']);
}

return $resultText;
}

/**
* Get the plugin directory name.
*/
Expand Down
104 changes: 104 additions & 0 deletions plugin/ai_helper/tool/learnpath.php
@@ -0,0 +1,104 @@
<?php
/* For license terms, see /license.txt */

/**
Create a learnpath with contents based on existing knowledge.
*/
require_once __DIR__.'/../../../main/inc/global.inc.php';
require_once __DIR__.'/../AiHelperPlugin.php';

$plugin = AiHelperPlugin::create();

$apiList = $plugin->getApiList();
$apiName = $plugin->get('api_name');

if (!in_array($apiName, array_keys($apiList))) {
throw new Exception("Ai API is not available for this request.");
}

switch ($apiName) {
case AiHelperPlugin::OPENAI_API:

$courseLanguage = (string) $_REQUEST['language'];
$chaptersCount = (int) $_REQUEST['nro_items'];
$topic = (string) $_REQUEST['lp_name'];
$wordsCount = (int) $_REQUEST['words_count'];
$courseCode = (string) $_REQUEST['course_code'];
$sessionId = (int) $_REQUEST['session_id'];

$messageGetItems = 'Generate the table of contents of a course in "%s" in %d or less chapters on the topic of "%s" in a list separated with comma, without chapter number';
$prompt = sprintf($messageGetItems, $courseLanguage, $chaptersCount, $topic);
$resultText = $plugin->openAiGetCompletionText($prompt);
$lpItems = [];
if (!empty($resultText)) {
$items = explode(',', $resultText);
$position = 1;
foreach ($items as $item) {
$explodedItem = preg_split('/\d\./', $item);
$title = count($explodedItem) > 1 ? $explodedItem[1] : $explodedItem[0];
if (!empty($title)) {
$lpItems[$position]['title'] = trim($title);
$messageGetItemContent = 'In the context of "%s", generate a document with HTML tags in "%s" with %d words of content or less, about "%s"';
$promptItem = sprintf($messageGetItemContent, $topic, $courseLanguage, $wordsCount, $title);
$resultContentText = $plugin->openAiGetCompletionText($promptItem);
if (!empty($resultContentText)) {
$lpItems[$position]['content'] = trim($resultContentText);
}
$position++;
}
}
}

// Create the learnpath and return the id generated.
$return = ['success' => false, 'lp_id' => 0];
if (!empty($lpItems)) {
$lpId = learnpath::add_lp(
$courseCode,
$topic,
'',
'chamilo',
'manual'
);

if (!empty($lpId)) {
learnpath::toggle_visibility($lpId, 0);
$courseInfo = api_get_course_info($courseCode);
$lp = new \learnpath(
$courseCode,
$lpId,
api_get_user_id()
);
$lp->generate_lp_folder($courseInfo, $topic);

foreach ($lpItems as $dspOrder => $item) {
$documentId = $lp->create_document(
$courseInfo,
$item['content'],
$item['title'],
'html'
);

if (!empty($documentId)) {
$lpItemId = $lp->add_item(
0,
0,
'document',
$documentId,
$item['title'],
'',
0,
0,
api_get_user_id(),
$dspOrder
);
}
}
}
$return = [
'success' => true,
'lp_id' => $lpId,
];
}
echo json_encode($return);
break;
}

0 comments on commit f86cf35

Please sign in to comment.