poc: adds AI user to the system, adds migration, changes AI settings to have switch enable\disable AI features
This commit is contained in:
parent
60146345d3
commit
b4aecb1e0b
24 changed files with 138 additions and 40 deletions
|
|
@ -100,7 +100,7 @@ PYRAMID_SETTINGS = {}
|
|||
EXTENSIONS = {}
|
||||
|
||||
__version__ = ".".join((str(each) for each in VERSION[:3]))
|
||||
__dbversion__ = 118 # defines current db version for migrations
|
||||
__dbversion__ = 119 # defines current db version for migrations
|
||||
__license__ = "AGPLv3, and Commercial License"
|
||||
__author__ = "RhodeCode GmbH"
|
||||
__url__ = "https://code.rhodecode.com"
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class TestGetUsers(object):
|
|||
ret_all = []
|
||||
_users = (
|
||||
User.query()
|
||||
.filter(User.username.notin_([User.DEFAULT_USER, User.GHOST_USER]))
|
||||
.filter(User.username.notin_([User.DEFAULT_USER, User.GHOST_USER, User.AI_USER]))
|
||||
.order_by(User.username)
|
||||
.all()
|
||||
)
|
||||
|
|
|
|||
|
|
@ -146,7 +146,10 @@ def get_users(request, apiuser):
|
|||
|
||||
result = []
|
||||
users_list = (
|
||||
User.query().order_by(User.username).filter(User.username.notin_([User.DEFAULT_USER, User.GHOST_USER])).all()
|
||||
User.query()
|
||||
.order_by(User.username)
|
||||
.filter(User.username.notin_([User.DEFAULT_USER, User.GHOST_USER, User.AI_USER]))
|
||||
.all()
|
||||
)
|
||||
for user in users_list:
|
||||
result.append(user.get_api_data(include_secrets=True))
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ class BaseAppView(object):
|
|||
if not user_obj:
|
||||
return
|
||||
|
||||
if user_obj.username in [User.DEFAULT_USER, User.GHOST_USER]:
|
||||
if user_obj.username in [User.DEFAULT_USER, User.GHOST_USER, User.AI_USER]:
|
||||
return
|
||||
|
||||
now = time.time()
|
||||
|
|
@ -270,7 +270,7 @@ class RepoAppView(BaseAppView):
|
|||
|
||||
def _prepare_and_set_clone_url(self, c):
|
||||
username = ""
|
||||
if self._rhodecode_user.username in [User.DEFAULT_USER, User.GHOST_USER]:
|
||||
if self._rhodecode_user.username in [User.DEFAULT_USER, User.GHOST_USER, User.AI_USER]:
|
||||
username = self._rhodecode_user.username
|
||||
|
||||
_def_clone_uri = c.clone_uri_tmpl
|
||||
|
|
@ -558,7 +558,7 @@ class UserAppView(BaseAppView):
|
|||
|
||||
_ = self.request.translate
|
||||
if not request.db_user_supports_default:
|
||||
if self.db_user.username in [User.DEFAULT_USER, User.GHOST_USER]:
|
||||
if self.db_user.username in [User.DEFAULT_USER, User.GHOST_USER, User.AI_USER]:
|
||||
h.flash(
|
||||
_("Editing user `{}` is disabled.".format(self.db_user.username)),
|
||||
category="warning",
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ class NavEntry(object):
|
|||
class NavigationRegistry(object):
|
||||
_base_entries = [
|
||||
NavEntry("global", _("Global"), "admin_settings_global"),
|
||||
NavEntry("ai", _("Ai"), "admin_settings_ai"),
|
||||
NavEntry("ai", _("AI features"), "admin_settings_ai"),
|
||||
NavEntry("vcs", _("VCS"), "admin_settings_vcs"),
|
||||
NavEntry("visual", _("Visual"), "admin_settings_visual"),
|
||||
NavEntry("mapping", _("Remap and Rescan"), "admin_settings_mapping"),
|
||||
|
|
|
|||
|
|
@ -41,7 +41,9 @@ class TestAdminUsersView(TestController):
|
|||
self.log_user()
|
||||
response = self.app.get(route_path("users_data"), extra_environ=xhr_header)
|
||||
|
||||
all_users = User.query().filter(User.username.notin_([User.DEFAULT_USER, User.GHOST_USER])).count()
|
||||
all_users = (
|
||||
User.query().filter(User.username.notin_([User.DEFAULT_USER, User.GHOST_USER, User.AI_USER])).count()
|
||||
)
|
||||
assert response.json["recordsTotal"] == all_users
|
||||
|
||||
def test_show_users_data_filtered(self, xhr_header):
|
||||
|
|
@ -50,7 +52,9 @@ class TestAdminUsersView(TestController):
|
|||
route_path("users_data", params={"search[value]": "empty_search"}), extra_environ=xhr_header
|
||||
)
|
||||
|
||||
all_users = User.query().filter(User.username.notin_([User.DEFAULT_USER, User.GHOST_USER])).count()
|
||||
all_users = (
|
||||
User.query().filter(User.username.notin_([User.DEFAULT_USER, User.GHOST_USER, User.AI_USER])).count()
|
||||
)
|
||||
assert response.json["recordsTotal"] == all_users
|
||||
assert response.json["recordsFiltered"] == 0
|
||||
|
||||
|
|
|
|||
|
|
@ -9,9 +9,11 @@ from rhodecode.apps.ai_agents.ai_settings import AIModelName, GPTVersion, Claude
|
|||
from rhodecode.apps.ai_agents.models.base import AIServiceBase
|
||||
from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, CSRFRequired
|
||||
from rhodecode.lib import helpers as h
|
||||
from rhodecode.model.db import User
|
||||
from rhodecode.model.forms import AiSettingsForm
|
||||
from rhodecode.model.settings import SettingsModel
|
||||
from rhodecode.model.meta import Session
|
||||
from rhodecode.model.user import UserModel
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -32,6 +34,7 @@ class AdminAiView(BaseAppView):
|
|||
c.selected_ai_model = app_settings.get("rhodecode_ai_model", AIModelName.GPT.name)
|
||||
c.selected_ai_model_version = app_settings.get("rhodecode_ai_model_version", GPTVersion.V5_nano.name)
|
||||
c.api_key = app_settings.get("rhodecode_ai_api_key")
|
||||
c.ai_features_enabled = app_settings.get("rhodecode_ai_features_enabled", False)
|
||||
|
||||
c.ai_instructions = app_settings.get("rhodecode_ai_code_review_instructions")
|
||||
if not c.ai_instructions:
|
||||
|
|
@ -59,6 +62,7 @@ class AdminAiView(BaseAppView):
|
|||
def _save_settings(self, _, data):
|
||||
try:
|
||||
settings = [
|
||||
("ai_features_enabled", "rhodecode_ai_features_enabled", "bool"),
|
||||
("ai_model", "rhodecode_ai_model", "unicode"),
|
||||
("ai_model_version", "rhodecode_ai_model_version", "unicode"),
|
||||
("ai_api_key", "rhodecode_ai_api_key", "unicode"),
|
||||
|
|
@ -66,6 +70,9 @@ class AdminAiView(BaseAppView):
|
|||
]
|
||||
for setting, form_key, type_ in settings:
|
||||
sett = SettingsModel().create_or_update_setting(setting, data[form_key], type_)
|
||||
if setting == "ai_features_enabled" and isinstance(data[form_key], bool):
|
||||
self._activate_deactivate_ai_user(data, form_key)
|
||||
|
||||
Session().add(sett)
|
||||
|
||||
Session().commit()
|
||||
|
|
@ -75,6 +82,12 @@ class AdminAiView(BaseAppView):
|
|||
log.exception("Exception saving AI settings: %s", e)
|
||||
h.flash(_("Error occurred during saving AI settings"), category="error")
|
||||
|
||||
def _activate_deactivate_ai_user(self, data, form_key):
|
||||
log.debug("%s AI user" % "Activating" if data[form_key] else "Deactivating")
|
||||
ai_user = User.get_ai_user()
|
||||
ai_user.active = data[form_key]
|
||||
Session().add(ai_user)
|
||||
|
||||
def _parse_form(self, _):
|
||||
try:
|
||||
form = AiSettingsForm()()
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ class AdminUsersView(BaseAppView, DataGridAppView):
|
|||
def user_actions(user_id, username):
|
||||
return _render("user_actions", user_id, username)
|
||||
|
||||
excluded_users = User.username.notin_([User.DEFAULT_USER, User.GHOST_USER])
|
||||
excluded_users = User.username.notin_([User.DEFAULT_USER, User.GHOST_USER, User.AI_USER])
|
||||
users_data_total_count = User.query().filter(excluded_users).count()
|
||||
|
||||
users_data_total_inactive_count = User.query().filter(excluded_users).filter(User.active != true()).count()
|
||||
|
|
|
|||
|
|
@ -239,7 +239,7 @@ class HomeView(BaseAppView, DataGridAppView):
|
|||
User.query()
|
||||
.order_by(func.length(User.username))
|
||||
.order_by(User.username)
|
||||
.filter(User.username.notin_([User.DEFAULT_USER, User.GHOST_USER]))
|
||||
.filter(User.username.notin_([User.DEFAULT_USER, User.GHOST_USER, User.AI_USER]))
|
||||
)
|
||||
|
||||
if name_contains:
|
||||
|
|
|
|||
|
|
@ -53,7 +53,16 @@ class TestUsersController(TestController):
|
|||
assert_response = response.assert_response()
|
||||
assert_response.element_contains(edit_link_css, "Edit")
|
||||
|
||||
def test_user_profile_not_available(self, user_util):
|
||||
@pytest.mark.parametrize(
|
||||
"user",
|
||||
[
|
||||
User.DEFAULT_USER,
|
||||
User.GHOST_USER,
|
||||
User.AI_USER,
|
||||
"unknown",
|
||||
],
|
||||
)
|
||||
def test_user_profile_not_available(self, user_util, user):
|
||||
user = user_util.create_user()
|
||||
username = user.username
|
||||
|
||||
|
|
@ -64,11 +73,5 @@ class TestUsersController(TestController):
|
|||
# after log-in show
|
||||
self.app.get(route_path("user_profile", username=username), status=200)
|
||||
|
||||
# default user, not allowed to show it
|
||||
# default user or system user, not allowed to show it
|
||||
self.app.get(route_path("user_profile", username=User.DEFAULT_USER), status=404)
|
||||
|
||||
# ghost user, not allowed to show it
|
||||
self.app.get(route_path("user_profile", username=User.GHOST_USER), status=404)
|
||||
|
||||
# actual 404
|
||||
self.app.get(route_path("user_profile", username="unknown"), status=404)
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ class UserProfileView(BaseAppView):
|
|||
username = self.request.matchdict.get("username")
|
||||
|
||||
c.user = UserModel().get_by_username(username)
|
||||
if not c.user or c.user.username in [User.DEFAULT_USER, User.GHOST_USER]:
|
||||
if not c.user or c.user.username in [User.DEFAULT_USER, User.GHOST_USER, User.AI_USER]:
|
||||
raise HTTPNotFound()
|
||||
|
||||
return self._get_template_context(c)
|
||||
|
|
|
|||
|
|
@ -1299,6 +1299,10 @@ class AuthUser(object):
|
|||
def is_ghost(self):
|
||||
return self.username == User.GHOST_USER
|
||||
|
||||
@property
|
||||
def is_ai(self):
|
||||
return self.username == User.AI_USER
|
||||
|
||||
@property
|
||||
def is_admin(self):
|
||||
return self.admin
|
||||
|
|
|
|||
|
|
@ -620,6 +620,21 @@ class DbManage(object):
|
|||
Session().add(ghost_user)
|
||||
Session().commit()
|
||||
|
||||
def create_ai_user(self):
|
||||
if not User.get_by_username(User.AI_USER):
|
||||
log.info("creating ai user")
|
||||
ai_user = User()
|
||||
ai_user.name = "RhodeCode Helper"
|
||||
ai_user.username = User.AI_USER
|
||||
ai_user.lastname = "User"
|
||||
ai_user.strict_creation_check = False
|
||||
ai_user.email = User.AI_USER_EMAIL
|
||||
ai_user.active = False
|
||||
ai_user.description = "System user"
|
||||
ai_user.is_new_user = False
|
||||
Session().add(ai_user)
|
||||
Session().commit()
|
||||
|
||||
def create_default_user(self):
|
||||
log.info("creating default user")
|
||||
# create default user for handling default permissions.
|
||||
|
|
|
|||
37
rhodecode/lib/dbmigrate/versions/119_version_5_8_0.py
Normal file
37
rhodecode/lib/dbmigrate/versions/119_version_5_8_0.py
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
from rhodecode.lib.dbmigrate.versions import _reset_base
|
||||
from rhodecode.model import meta, init_model_encryption
|
||||
from rhodecode.model.db import User
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
"""
|
||||
Upgrade operations go here.
|
||||
Don't create your own engine; bind migrate_engine to your metadata
|
||||
"""
|
||||
_reset_base(migrate_engine)
|
||||
|
||||
from rhodecode.lib.dbmigrate.schema import db_4_20_0_1 as db
|
||||
|
||||
init_model_encryption(db)
|
||||
|
||||
UserModel: User = db.User
|
||||
|
||||
username = "ai_rhodecode"
|
||||
if not User.get_by_username(username):
|
||||
ghost_user = UserModel()
|
||||
ghost_user.name = "RhodeCode Helper"
|
||||
ghost_user.username = username
|
||||
ghost_user.lastname = "User"
|
||||
ghost_user.strict_creation_check = False
|
||||
ghost_user.email = "ai@rhodecode.org"
|
||||
ghost_user.active = False
|
||||
ghost_user.description = "System user"
|
||||
ghost_user.is_new_user = False
|
||||
|
||||
session = meta.Session()
|
||||
session.add(ghost_user)
|
||||
session.commit()
|
||||
|
||||
|
||||
def downgrade(migrate_engine):
|
||||
pass
|
||||
|
|
@ -1035,7 +1035,7 @@ def link_to_user(author, length=0, **kwargs):
|
|||
if length:
|
||||
display_person = shorter(display_person, length)
|
||||
|
||||
if user and user.username not in [user.DEFAULT_USER, user.GHOST_USER]:
|
||||
if user and user.username not in [user.DEFAULT_USER, user.GHOST_USER, user.AI_USER]:
|
||||
return link_to(escape(display_person), route_path("user_profile", username=user.username), **kwargs)
|
||||
else:
|
||||
return escape(display_person)
|
||||
|
|
|
|||
|
|
@ -484,7 +484,7 @@ class SimpleVCS(object):
|
|||
auth_user = user_obj.AuthUser()
|
||||
if (
|
||||
user_obj
|
||||
and user_obj.username not in [User.DEFAULT_USER, User.GHOST_USER]
|
||||
and user_obj.username not in [User.DEFAULT_USER, User.GHOST_USER, User.AI_USER]
|
||||
and user_obj.user_data.get("force_password_change")
|
||||
):
|
||||
reason = "password change required"
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ def command(
|
|||
dbmanage.create_settings(opts)
|
||||
dbmanage.create_default_user()
|
||||
dbmanage.create_ghost_user()
|
||||
dbmanage.create_ai_user()
|
||||
dbmanage.create_admin_and_prompt()
|
||||
dbmanage.create_permissions()
|
||||
dbmanage.populate_default_permissions()
|
||||
|
|
|
|||
|
|
@ -841,6 +841,7 @@ def create_test_database(test_path, config):
|
|||
dbmanage.create_settings(dbmanage.config_prompt(test_path))
|
||||
dbmanage.create_default_user()
|
||||
dbmanage.create_ghost_user()
|
||||
dbmanage.create_ai_user()
|
||||
dbmanage.create_test_admin_and_users()
|
||||
dbmanage.create_permissions()
|
||||
dbmanage.populate_default_permissions()
|
||||
|
|
@ -875,8 +876,8 @@ def create_test_repositories(test_path, config):
|
|||
|
||||
|
||||
def password_changed(auth_user, session):
|
||||
# Never report password change in case of default user or anonymous user.
|
||||
if auth_user.username in [User.DEFAULT_USER, User.GHOST_USER] or auth_user.user_id is None:
|
||||
# Never report password change in case of default user, anonymous user or system user.
|
||||
if auth_user.username in [User.DEFAULT_USER, User.GHOST_USER, User.AI_USER] or auth_user.user_id is None:
|
||||
return False
|
||||
|
||||
password_hash = md5(safe_bytes(auth_user.password)) if auth_user.password else None
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ def display_user_sort(obj):
|
|||
of all other resources
|
||||
"""
|
||||
|
||||
if obj.username in [User.DEFAULT_USER, User.GHOST_USER]:
|
||||
if obj.username in [User.DEFAULT_USER, User.GHOST_USER, User.AI_USER]:
|
||||
return "#####"
|
||||
prefix = PERMISSION_TYPE_SORT.get(obj.permission.split(".")[-1], "")
|
||||
extra_sort_num = "1" # default
|
||||
|
|
@ -618,7 +618,9 @@ class User(Base, BaseModel):
|
|||
DEFAULT_USER = "default"
|
||||
DEFAULT_USER_EMAIL = "anonymous@rhodecode.org"
|
||||
GHOST_USER = "ghost"
|
||||
AI_USER = "ai_rhodecode"
|
||||
GHOST_USER_EMAIL = "ghost@rhodecode.org"
|
||||
AI_USER_EMAIL = "ai@rhodecode.org"
|
||||
DEFAULT_GRAVATAR_URL = "https://secure.gravatar.com/avatar/{md5email}?d=identicon&s={size}"
|
||||
RECOVERY_CODES_COUNT = 10
|
||||
|
||||
|
|
@ -1286,6 +1288,10 @@ class User(Base, BaseModel):
|
|||
def get_ghost_user(cls, cache=False, refresh=False):
|
||||
return cls._get_system_user(username=cls.GHOST_USER, cache=cache, refresh=refresh)
|
||||
|
||||
@classmethod
|
||||
def get_ai_user(cls, cache=False, refresh=False):
|
||||
return cls._get_system_user(username=cls.AI_USER, cache=cache, refresh=refresh)
|
||||
|
||||
@classmethod
|
||||
def get_default_user(cls, cache=False, refresh=False):
|
||||
return cls._get_system_user(username=cls.DEFAULT_USER, cache=cache, refresh=refresh)
|
||||
|
|
@ -3047,7 +3053,7 @@ class RepoGroup(Base, BaseModel):
|
|||
@classmethod
|
||||
def get_user_personal_repo_group(cls, user_id):
|
||||
user = User.get(user_id)
|
||||
if user.username in [User.DEFAULT_USER, User.GHOST_USER]:
|
||||
if user.username in [User.DEFAULT_USER, User.GHOST_USER, User.AI_USER]:
|
||||
return None
|
||||
|
||||
return cls.query().filter(cls.personal == true()).filter(cls.user == user).order_by(cls.group_id.asc()).first()
|
||||
|
|
|
|||
|
|
@ -671,6 +671,7 @@ def IssueTrackerPatternsForm(localizer):
|
|||
def AiSettingsForm():
|
||||
class _AiSettingsForm(formencode.Schema):
|
||||
allow_extra_fields = True
|
||||
rhodecode_ai_features_enabled = v.StringBoolean(if_missing=False)
|
||||
rhodecode_ai_model = v.UnicodeString(strip=True, required=True)
|
||||
rhodecode_ai_model_version = v.UnicodeString(strip=True, required=True)
|
||||
rhodecode_ai_api_key = v.UnicodeString(strip=True)
|
||||
|
|
|
|||
|
|
@ -688,8 +688,8 @@ class RepoModel(BaseModel):
|
|||
if member_type == "user":
|
||||
member_name = User.get(member_id).username
|
||||
|
||||
if member_name == User.GHOST_USER:
|
||||
raise ValueError("Modify user permissions for ghost user is not allowed.")
|
||||
if member_name in [User.GHOST_USER, User.AI_USER]:
|
||||
raise ValueError("Modify user permissions for %s user is not allowed." % member_name)
|
||||
|
||||
if member_name == User.DEFAULT_USER:
|
||||
# NOTE(dan): detect if we changed permissions for default user
|
||||
|
|
|
|||
|
|
@ -359,7 +359,7 @@ class RepoGroupModel(BaseModel):
|
|||
elif isinstance(_obj, Repository):
|
||||
# private repos will not allow to change the default
|
||||
# permissions using recursive mode
|
||||
if _obj.private and _user_obj.username in [User.DEFAULT_USER, User.GHOST_USER]:
|
||||
if _obj.private and _user_obj.username in [User.DEFAULT_USER, User.GHOST_USER, User.AI_USER]:
|
||||
log.debug("Skipping private repo %s for user %s", _obj, _user_obj)
|
||||
return
|
||||
|
||||
|
|
@ -380,8 +380,8 @@ class RepoGroupModel(BaseModel):
|
|||
self.revoke_user_permission(repo_group=_obj, user=_user_obj)
|
||||
elif isinstance(_obj, Repository):
|
||||
# private repos will not allow to change the default
|
||||
# permissions using recursive mode, also there's no revocation fo default user, just update
|
||||
if _user_obj.username in [User.DEFAULT_USER, User.GHOST_USER]:
|
||||
# permissions using recursive mode, also there's no revocation for default user, just update
|
||||
if _user_obj.username in [User.DEFAULT_USER, User.GHOST_USER, User.AI_USER]:
|
||||
log.debug("Skipping private repo %s for user %s", _obj, _user_obj)
|
||||
return
|
||||
RepoModel().revoke_user_permission(repo=_obj, user=_user_obj)
|
||||
|
|
@ -426,8 +426,8 @@ class RepoGroupModel(BaseModel):
|
|||
member_obj = User.get(member_id)
|
||||
member_name = member_obj.username
|
||||
is_repo_group = isinstance(obj, RepoGroup) and obj == repo_group
|
||||
if is_repo_group and member_name == User.GHOST_USER:
|
||||
raise ValueError("Modify user permissions for ghost user is not allowed.")
|
||||
if is_repo_group and member_name in [User.GHOST_USER, User.AI_USER]:
|
||||
raise ValueError("Modify user permissions for %s user is not allowed." % member_name)
|
||||
|
||||
if is_repo_group and member_name == User.DEFAULT_USER:
|
||||
# NOTE(dan): detect if we changed permissions for default user
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ class UserModel(BaseModel):
|
|||
qry = (
|
||||
User.query()
|
||||
.filter(User.active == true())
|
||||
.filter(User.username.notin_([User.DEFAULT_USER, User.GHOST_USER]))
|
||||
.filter(User.username.notin_([User.DEFAULT_USER, User.GHOST_USER, User.AI_USER]))
|
||||
)
|
||||
if cache:
|
||||
qry = qry.options(FromCache("sql_cache_short", "get_active_users"))
|
||||
|
|
@ -180,7 +180,7 @@ class UserModel(BaseModel):
|
|||
|
||||
def update_user(self, user, skip_attrs=None, **kwargs):
|
||||
user = self._get_user(user)
|
||||
if user.username in [User.DEFAULT_USER, User.GHOST_USER]:
|
||||
if user.username in [User.DEFAULT_USER, User.GHOST_USER, User.AI_USER]:
|
||||
raise DefaultUserException(
|
||||
"You can't edit this user (`%(username)s`) since it's "
|
||||
"crucial for entire application" % {"username": user.username}
|
||||
|
|
@ -363,8 +363,8 @@ class UserModel(BaseModel):
|
|||
new_user = user
|
||||
edit = True
|
||||
|
||||
# we're not allowed to edit default user
|
||||
if user.username in [User.DEFAULT_USER, User.GHOST_USER]:
|
||||
# we're not allowed to edit default user or system user
|
||||
if user.username in [User.DEFAULT_USER, User.GHOST_USER, User.AI_USER]:
|
||||
raise DefaultUserException(
|
||||
"You can't edit this user (`%(username)s`) since it's "
|
||||
"crucial for entire application" % {"username": user.username}
|
||||
|
|
@ -576,7 +576,7 @@ class UserModel(BaseModel):
|
|||
user = self._get_user(user)
|
||||
|
||||
try:
|
||||
if user.username in [User.DEFAULT_USER, User.GHOST_USER]:
|
||||
if user.username in [User.DEFAULT_USER, User.GHOST_USER, User.AI_USER]:
|
||||
raise DefaultUserException("You can't remove this user since it's crucial for entire application")
|
||||
|
||||
if delete_reviewer:
|
||||
|
|
|
|||
|
|
@ -5,6 +5,16 @@
|
|||
<div class="panel-body">
|
||||
${h.secure_form(h.route_path('admin_settings_ai_update'), 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="model">
|
||||
<div class="label label">
|
||||
<label for="model">${_('Model')}</label>
|
||||
|
|
@ -26,11 +36,11 @@
|
|||
${h.text('rhodecode_ai_api_key', c.api_key, size=30)}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="field">
|
||||
<div class="label-textarea">
|
||||
<label for="rhodecode_ai_code_review_instructions">${_('Code Review Instructions')}:</label>
|
||||
</div>
|
||||
<div class="textarea large textarea-full text-area" style="width: 60%;">
|
||||
<div class="large textarea-full text-area" style="width: 60%;">
|
||||
${h.textarea('rhodecode_ai_code_review_instructions', c.ai_instructions, rows=14)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue