244 lines
8.7 KiB
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>
|
|
|