Skip to content

Commit

Permalink
feat: add feature flag for LLM summarize
Browse files Browse the repository at this point in the history
  • Loading branch information
Serafin-dev committed Oct 10, 2024
1 parent d780110 commit 859d76d
Show file tree
Hide file tree
Showing 3 changed files with 199 additions and 4 deletions.
7 changes: 7 additions & 0 deletions lms/djangoapps/edxnotes/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from django.conf import settings
from xblock.exceptions import NoSuchServiceError
from openedx.core.djangoapps.plugins.plugins_hooks import run_extension_point

from common.djangoapps.edxmako.shortcuts import render_to_string
from common.djangoapps.student.auth import is_ccx_course
Expand Down Expand Up @@ -50,6 +51,8 @@ def get_html(self, *args, **kwargs):
except NoSuchServiceError:
user = None

is_llm_summarize_enabled = run_extension_point('PEARSON_CORE_ENABLE_LLM_SUMMARIZE', course_id=str(course.id))

if is_studio or not is_feature_enabled(course, user):
return original_get_html(self, *args, **kwargs)
else:
Expand All @@ -69,6 +72,10 @@ def get_html(self, *args, **kwargs):
"endpoint": get_public_endpoint(),
"debug": settings.DEBUG,
"eventStringLimit": settings.TRACK_MAX_EVENT / 6,
"llmSummarize": {
"isEnabled": is_llm_summarize_enabled,
"courseId": str(course.id),
},
},
})

Expand Down
180 changes: 180 additions & 0 deletions lms/static/js/edxnotes/plugins/llm_summarize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
(function(define, undefined) {
'use strict';
define(['jquery', 'annotator_1.2.9'], function($, Annotator) {
/**
* LlmSummarize plugin adds a button to the annotatorjs adder in order to
* summarize the text selected and save as annotation.
**/
Annotator.Plugin.LlmSummarize = function() {
Annotator.Plugin.apply(this, arguments);
};

$.extend(Annotator.Plugin.LlmSummarize.prototype, new Annotator.Plugin(), {
pluginInit: function() {
// Overrides of annotatorjs HTML/CSS to add summarize button.
const style = document.createElement('style');
style.innerHTML = `
.annotator-adder::before {
content: '';
width: 10px;
height: 10px;
background-color: white;
display: block;
position: absolute;
top: 100%;
left: 5px;
border-bottom: 1px solid gray;
border-right: 1px solid gray;
z-index: 0;
transform: translateY(-50%) rotate(45deg);
}
.annotator-adder button::before,
.annotator-adder button::after {
display: none !important;
}
.annotator-adder #annotateButton,
.annotator-adder #summarizeButton {
border: none !important;
background: rgb(0, 48, 87) !important;
box-shadow: none !important;
width: initial;
transition: color .1s;
text-indent: initial;
font-size: 20px;
padding: 0;
height: fit-content;
color: white;
border-radius: 5px;
padding-left: 5px;
padding-right: 5px;
font-weight: normal;
display: inline-block;
}
.annotator-adder #summarizeButton {
margin-left: 3px;
}
.annotator-adder button i.fa {
font-style: normal;
}
.annotator-adder {
width: fit-content;
height: fit-content;
padding: 5px;
background-color: white;
border: 1px solid gray;
}
// Defining content loader since fontawesome icons don't work
// inside the annotatorjs modal.
.loader {
width: 5em !important;
border-color: red !important;
}
@keyframes rotation {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
`;
let annotator = this.annotator;
document.head.appendChild(style);
this.modifyDom(this.annotator);
annotator.editor.options.llmSummarize = annotator.options.llmSummarize
const summarizeButton = document.getElementById('summarizeButton');

summarizeButton.addEventListener('click', function(ev) {
annotator.editor.options.isSummarizing = true;
});
annotator.subscribe('annotationEditorShown', this.handleSummarize);
annotator.subscribe('annotationEditorHidden', this.cleanupSummarize);
},
handleSummarize: function (editor, annotation) {
if (!editor.options || !editor.options.isSummarizing) return;

function toggleLoader() {
const saveButton = document.querySelector('.annotator-controls .annotator-save');
const loaderWrapper = document.querySelector('.summarize-loader-wrapper');
editor.fields[0].element.children[0].classList.toggle('d-none');
loaderWrapper.classList.toggle('d-none');
saveButton.disabled = !saveButton.disabled;
}
const textAreaWrapper = editor.fields[0].element;
const request = new Request('/pearson-core/llm-assistance/api/v0/summarize-text', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': $.cookie('csrftoken'),
},
body: JSON.stringify({
text_to_summarize: annotation.quote,
course_id: editor.options && editor.options.llmSummarize && editor.options.llmSummarize.courseId,
}),
});

editor.fields[1].element.children[0].value = 'ai_summary';
toggleLoader(editor);
fetch(request)
.then((response) => {
toggleLoader();
if (response.ok) return response.json();
throw new Error(gettext('There was an error while summarizing the content.'));
})
.then((data) => {
textAreaWrapper.children[0].value = data.summary;
})
.catch((error) => {
alert(error.message);
editor.hide();
});
},
cleanupSummarize: function(editor) {
const textAreaWrapper = editor.fields[0].element;
const loaderWrapper = document.querySelector('.summarize-loader-wrapper');

textAreaWrapper.children[0].value = '';
textAreaWrapper.children[1].value = '';
editor.options.isSummarizing = false;
loaderWrapper.classList.add('d-none');
},
modifyDom: function(annotator) {
const textAreaWrapper = annotator.editor.fields[0].element;

annotator.adder[0].children[0].id = 'annotateButton';
annotator.adder[0].children[0].innerHTML = '<i class="fa fa-pencil" aria-hidden="true"></i>';
annotator.adder[0].innerHTML += `
<button class="summarize-button" id="summarizeButton" title="${gettext('Summarize text using AI.')}">
<i class="fa fa-star" aria-hidden="true"></i>
</button>
`;
// Style is being defined here since classes styling not working
// for this element.
textAreaWrapper.innerHTML += `
<div class="summarize-loader-wrapper d-none">
<span class="loader" style="
width: 1.2em;
height: 1.2em;
border: 3px solid rgb(0, 48, 87);
border-bottom-color: white;
border-radius: 50%;
display: inline-block;
box-sizing: border-box;
animation: rotation 3s linear infinite;
margin-right: 5px;
transform-origin: center;">
</span>
<span style="font-size: 1.2em; color: rgb(0, 48, 87);">
${gettext('Summarizing...')}
</span>
</div>`;
},
});
});
}).call(this, define || RequireJS.define);
16 changes: 12 additions & 4 deletions lms/static/js/edxnotes/views/notes_factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
'js/edxnotes/plugins/events', 'js/edxnotes/plugins/accessibility',
'js/edxnotes/plugins/caret_navigation',
'js/edxnotes/plugins/store_error_handler',
'js/edxnotes/plugins/search_override'
'js/edxnotes/plugins/search_override',
'js/edxnotes/plugins/llm_summarize',
], function($, _, Annotator, NotesLogger, NotesCollector) {
var plugins = ['Auth', 'Store', 'Scroller', 'Events', 'Accessibility', 'CaretNavigation', 'Tags'],
var plugins = ['Auth', 'Store', 'Scroller', 'Events', 'Accessibility', 'CaretNavigation', 'Tags', 'LlmSummarize'],
getOptions, setupPlugins, getAnnotator;

/**
Expand Down Expand Up @@ -50,7 +51,11 @@
destroy: '/annotations/:id/',
search: '/search/'
}
}
},
llmSummarize: {
isEnabled: params && params.llmSummarize && params.llmSummarize.isEnabled,
courseId: params && params.llmSummarize && params.llmSummarize.courseId,
},
};
};

Expand Down Expand Up @@ -84,6 +89,9 @@
logger = NotesLogger.getLogger(element.id, params.debug),
annotator;

if (options && options.llmSummarize && options.llmSummarize.isEnabled) {
plugins.push('LlmSummarize');
}
annotator = $el.annotator(options).data('annotator');
setupPlugins(annotator, plugins, options);
NotesCollector.storeNotesRequestData(
Expand All @@ -99,4 +107,4 @@
factory: getAnnotator
};
});
}).call(this, define || RequireJS.define);
}).call(this, define || RequireJS.define);

0 comments on commit 859d76d

Please sign in to comment.