unrhodecode/rhodecode/templates/admin/settings/settings_ai.mako

244 lines
8.7 KiB
Mako

<style>
.field > .label {
min-width: 300px;
}
</style>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">${_('Ai Configuration')}</h3>
</div>
<div class="panel-body">
${h.secure_form(h.route_path('admin_settings_ai_update'), id='ai_features_form', request=request)}
<div class="fields">
<div class="field" id="ai-features-toggle">
<div class="label label-checkbox">
<label for="rhodecode_ai_features_enabled">${_('Enable AI features')}:</label>
</div>
<div class="checkboxes">
${h.checkbox('rhodecode_ai_features_enabled',value=True, checked=c.ai_features_enabled==True)}
</div>
</div>
<div class="field" id="ai-default-review-toggle" >
<div class="label label-checkbox">
<label for="rhodecode_ai_default_code_review">${_('Turn on AI code review by default for new PRs')}:</label>
<strong>Warning</strong>: can potentially spend a lot of tokens
</div>
<div class="checkboxes">
${h.checkbox('rhodecode_ai_default_code_review',value=True, checked=c.ai_default_code_review==True)}
</div>
</div>
<div class="field" id="model" style="display: flex;">
<div class="label label">
<label for="model">${_('Model')}</label>
</div>
<select id="rhodecode_ai_model" name="rhodecode_ai_model">
% for m in c.model_map:
<option value="${m}">${m}</option>
% endfor
</select>
<button type="button" class="btn btn-primary" id="model_version_button_update" style="display: inline-flex">
${_('Update model versions')}
</button>
</div>
<div id="model_version_slot"></div>
<div class="field" id="rhodecode_ai_api_key">
<div class="label label">
<label for="rhodecode_ai_api_key">${_('Api key')}</label>
</div>
<div class="input">
${h.password('rhodecode_ai_api_key', c.api_key, size=30)}
</div>
</div>
<div class="field">
<div class="label-textarea">
<label for="rhodecode_ai_code_review_instructions">${_('Code Review Instructions')}:</label>
</div>
<div class="large textarea-full text-area" style="width: 60%;">
${h.textarea('rhodecode_ai_code_review_instructions', c.ai_instructions, rows=14)}
</div>
</div>
<div class="buttons">
${h.submit('save',_('Save'),class_="btn")}
</div>
${h.end_form()}
</div>
</div>
</div>
<%!
import json
%>
<script type="text/javascript">
$(document).ready(function () {
function selector(_id) {
$(_id).select2({
containerCssClass: 'drop-menu',
dropdownCssClass: 'drop-menu-dropdown',
dropdownAutoWidth: true,
minimumResultsForSearch: -1
});
}
const MODEL_MAP = ${json.dumps(c.model_map) | n};
const SELECTED_MODEL = ${json.dumps(getattr(c, 'selected_ai_model', None)) | n};
const SELECTED_VERSION = ${json.dumps(getattr(c, 'selected_ai_model_version', None)) | n};
const $model = $('#rhodecode_ai_model');
const $slot = $('#model_version_slot');
const $enable = $('#rhodecode_ai_features_enabled');
const $codeReview = $('#rhodecode_ai_default_code_review');
const $form = $('#ai_features_form');
const $updateModelsBtn = $('#model_version_button_update');
function renderVersionField(model) {
const versions = MODEL_MAP[model] || [];
$slot.empty();
if (!versions.length) {
setSaveButtonActive(false);
return;
}
setSaveButtonActive(true);
const html = `
<div class="field" id="model_version">
<div class="label label">
<label for="rhodecode_ai_model_version">${_('Model Version')}</label>
</div>
<select id="rhodecode_ai_model_version" name="rhodecode_ai_model_version"></select>
</div>
`;
$slot.append(html);
const $ver = $('#rhodecode_ai_model_version');
for (const [key, display_name] of versions) {
const isSelected = (model === SELECTED_MODEL) && (String(key) === String(SELECTED_VERSION));
$ver.append(new Option(display_name, key, isSelected, isSelected));
}
if (!$ver.find('option:selected').length && versions.length) {
$ver.prop('selectedIndex', 0);
}
selector('#rhodecode_ai_model_version');
$ver.trigger('change');
setAIFieldsActive($enable.is(':checked'));
}
function setSaveButtonActive(enable) {
$('.buttons')
.find('input, button')
.prop('disabled', !enable);
}
function setAIFieldsActive(enabled) {
const $affectedFields = $('.fields .field').not('#ai-features-toggle');
$affectedFields.toggleClass('is-disabled', !enabled);
$affectedFields
.find('input, textarea, button')
.prop('disabled', !enabled);
$affectedFields
.find('select')
.prop('disabled', !enabled)
.trigger('change.select2');
}
renderVersionField($model.val());
$model.on('change', function () {
renderVersionField(this.value);
});
selector('#rhodecode_ai_model');
if (SELECTED_MODEL && $model.find('option[value="' + SELECTED_MODEL.replace(/"/g, '\\"') + '"]').length) {
$model.val(SELECTED_MODEL).trigger('change.select2');
}
renderVersionField($model.val());
$enable.on('change', function () {
setAIFieldsActive(this.checked);
});
$updateModelsBtn.on('click', function () {
const $apiKey = $('#rhodecode_ai_api_key input');
if (!$apiKey.val()) {
$.Topic('/notifications').publish({
message: {
message: "API key required to update model versions.",
level: "warning",
force: true
}
});
return;
}
const url = "${h.route_path('admin_settings_ai_update_models')}";
$.ajax({
type: "POST",
url: url,
data: {
'rhodecode_ai_model': $model.val(),
'rhodecode_ai_api_key': $apiKey.val(),
'rhodecode_ai_features_enabled': $enable.is(':checked'),
'rhodecode_ai_default_code_review': $codeReview.is(':checked')
},
success: function (response) {
$.Topic('/notifications').publish({
message: {
message: response["message"],
level: response["message_category"],
force: true
}
});
if (Array.isArray(response["versions"]) && response["versions"].length !== 0) {
MODEL_MAP[$model.val()] = response["versions"];
renderVersionField($model.val());
}
},
error: function (data, textStatus, errorThrown) {
console.log(data);
$.Topic('/notifications').publish({
message: {
message: "Error while updating models entry.\nError code {0} ({1}). URL: {2}".format(data.status, data.statusText, url),
level: "error",
force: true
}
});
}
});
});
$form.on('submit', function () {
let $f = $(this);
let $tmpEnabled = $f.find(':disabled');
$tmpEnabled.prop('disabled', false);
setTimeout(function () {
// immediately re-enable disable property
$tmpEnabled.prop('disabled', true);
}, 0);
});
});
</script>