poc: adds AI user to the system, adds migration, changes AI settings to have switch enable\disable AI features

This commit is contained in:
ievgenii vdovenko 2025-09-01 12:51:26 +02:00
parent 60146345d3
commit b4aecb1e0b
24 changed files with 138 additions and 40 deletions

View file

@ -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()

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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: