Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 19 additions & 5 deletions core/concepts/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from core.bundles.models import Bundle
from core.bundles.serializers import BundleSerializer
from core.collections.documents import CollectionDocument
from core.common import ERRBIT_LOGGER
from core.common.constants import (
HEAD, INCLUDE_INVERSE_MAPPINGS_PARAM, INCLUDE_RETIRED_PARAM, ACCESS_TYPE_NONE, LIMIT_PARAM, LIST_DEFAULT_LIMIT)
from core.common.exceptions import Http400, Http403, Http409
Expand Down Expand Up @@ -994,7 +995,7 @@ class RerankConceptsListView(BaseAPIView):
serializer_class = ConceptListSerializer
permission_classes = (IsAuthenticated,)

def post(self, request, **kwargs): # pylint: disable=unused-argument
def post(self, request, **kwargs): # pylint: disable=unused-argument,too-many-return-statements
user = self.request.user
if user.is_mapper_waitlisted or not user.is_mapper_approved:
return Response(
Expand All @@ -1006,6 +1007,12 @@ def post(self, request, **kwargs): # pylint: disable=unused-argument
name_key = self.request.data.get('name_key', None) or 'display_name'
text = self.request.data.get('q', None)
score_key = self.request.data.get('score_key', None)
encoder_model = self.request.data.get('encoder_model', None)
if encoder_model and encoder_model != settings.ENCODER_MODEL_NAME and not user.is_core_group:
return Response(
{'detail': 'You do not have permission to request for custom encoder models.'},
status=status.HTTP_403_FORBIDDEN
)

if not isinstance(rows, list) or not rows:
return Response(
Expand All @@ -1017,7 +1024,14 @@ def post(self, request, **kwargs): # pylint: disable=unused-argument
{'detail': 'Missing "q" in request body.'},
status=status.HTTP_400_BAD_REQUEST
)

return Response(
Reranker().rerank(hits=rows, name_key=name_key, txt=text, score_key=score_key, order_results=True)
)
try:
reranker = Reranker(model_name=encoder_model)
results = reranker.rerank(hits=rows, name_key=name_key, txt=text, score_key=score_key, order_results=True)
return Response(results)
except (ValueError, RuntimeError, OSError) as e:
return Response({'detail': str(e)}, status=status.HTTP_400_BAD_REQUEST)
Comment thread Fixed

Check warning

Code scanning / CodeQL

Information exposure through an exception

[Stack trace information](1) flows to this location and may be exposed to an external user.

Copilot Autofix

AI 3 days ago

To fix this, stop returning raw exception messages to the client. Instead:

  • Log the exception server-side (including stack trace context where possible).
  • Return a generic error message to the user for the handled exception block as well.

Best minimal fix in core/concepts/views.py is to update the except (ValueError, RuntimeError, OSError) block in RerankConceptsListView.post:

  • replace {'detail': str(e)} with a generic message string.
  • add ERRBIT_LOGGER.log(e) in that block (same as the generic Exception block already does), preserving current status code and behavior shape.

No new imports or dependencies are required.

Suggested changeset 1
core/concepts/views.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/core/concepts/views.py b/core/concepts/views.py
--- a/core/concepts/views.py
+++ b/core/concepts/views.py
@@ -1029,7 +1029,11 @@
             results = reranker.rerank(hits=rows, name_key=name_key, txt=text, score_key=score_key, order_results=True)
             return Response(results)
         except (ValueError, RuntimeError, OSError) as e:
-            return Response({'detail': str(e)}, status=status.HTTP_400_BAD_REQUEST)
+            ERRBIT_LOGGER.log(e)
+            return Response(
+                {'detail': 'Invalid rerank request.'},
+                status=status.HTTP_400_BAD_REQUEST
+            )
         except Exception as e:
             ERRBIT_LOGGER.log(e)
             return Response(
EOF
@@ -1029,7 +1029,11 @@
results = reranker.rerank(hits=rows, name_key=name_key, txt=text, score_key=score_key, order_results=True)
return Response(results)
except (ValueError, RuntimeError, OSError) as e:
return Response({'detail': str(e)}, status=status.HTTP_400_BAD_REQUEST)
ERRBIT_LOGGER.log(e)
return Response(
{'detail': 'Invalid rerank request.'},
status=status.HTTP_400_BAD_REQUEST
)
except Exception as e:
ERRBIT_LOGGER.log(e)
return Response(
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated
except Exception as e:
ERRBIT_LOGGER.log(e)
return Response(
{'detail': 'An error occurred while processing the rerank request.'},
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
18 changes: 18 additions & 0 deletions core/map_projects/migrations/0031_mapproject_encoder_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.1.5 on 2026-04-06 05:31

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('map_projects', '0030_auto_20260221_0250'),
]

operations = [
migrations.AddField(
model_name='mapproject',
name='encoder_model',
field=models.TextField(blank=True, default='BAAI/bge-reranker-v2-m3', null=True),
),
]
16 changes: 14 additions & 2 deletions core/map_projects/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import json
from collections import Counter

from django.conf import settings
from django.contrib.postgres.fields import ArrayField
from django.core.exceptions import ValidationError
from django.db import models, IntegrityError
Expand Down Expand Up @@ -38,6 +39,7 @@ class MapProject(BaseModel):
candidates = models.JSONField(default=dict, null=True, blank=True)
lookup_config = models.JSONField(default=dict, null=True, blank=True)
analysis = models.JSONField(default=dict, null=True, blank=True)
encoder_model = models.TextField(null=True, blank=True, default=settings.ENCODER_MODEL_NAME)

class Meta:
db_table = 'map_projects'
Expand Down Expand Up @@ -193,14 +195,24 @@ def soft_delete(self):

def clean(self):
self.clean_filters()
if not self.include_retired:
self.include_retired = False
self.clean_include_retired()
self.clean_encoder_model()
self.clean_matches()

def clean_matches(self):
if self.matches:
try:
self.matches = json.loads(self.matches)
except (json.JSONDecodeError, TypeError):
pass

def clean_include_retired(self):
if not self.include_retired:
self.include_retired = False

def clean_encoder_model(self):
self.encoder_model = self.encoder_model.strip() if self.encoder_model else settings.ENCODER_MODEL_NAME

def clean_filters(self):
if not self.filters:
self.filters = {}
Expand Down
8 changes: 5 additions & 3 deletions core/map_projects/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class Meta:
'created_by', 'updated_by', 'created_at', 'updated_at', 'url', 'is_active',
'public_access', 'file', 'user_id', 'organization_id', 'description',
'target_repo_url', 'include_retired', 'score_configuration',
'filters', 'candidates', 'algorithms', 'lookup_config', 'analysis'
'filters', 'candidates', 'algorithms', 'lookup_config', 'analysis', 'encoder_model'
]

def prepare_object(self, validated_data, instance=None, file=None):
Expand All @@ -36,7 +36,8 @@ def prepare_object(self, validated_data, instance=None, file=None):
instance.columns = columns
for attr in [
'name', 'description', 'extras', 'target_repo_url', 'include_retired',
'score_configuration', 'filters', 'candidates', 'algorithms', 'lookup_config', 'analysis'
'score_configuration', 'filters', 'candidates', 'algorithms', 'lookup_config', 'analysis',
'encoder_model'
]:
setattr(instance, attr, validated_data.get(attr, get(instance, attr)))
if not instance.id:
Expand Down Expand Up @@ -109,7 +110,8 @@ class Meta:
'created_by', 'updated_by', 'created_at', 'updated_at', 'url', 'is_active',
'owner', 'owner_type', 'owner_url', 'public_access',
'target_repo_url', 'summary', 'logs', 'include_retired',
'score_configuration', 'filters', 'candidates', 'algorithms', 'lookup_config', 'analysis'
'score_configuration', 'filters', 'candidates', 'algorithms', 'lookup_config', 'analysis',
'encoder_model'
]

def __init__(self, *args, **kwargs):
Expand Down
6 changes: 5 additions & 1 deletion core/users/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from core.common.tasks import send_user_verification_email, send_user_reset_password_email
from core.common.utils import web_url
from core.users.constants import AUTH_GROUPS, MAPPER_WAITLIST_GROUP, STAFF_GROUP, SUPERADMIN_GROUP, GUEST_GROUP, \
MAPPER_APPROVED_GROUP
MAPPER_APPROVED_GROUP, CORE_USER_GROUP
from .constants import USER_OBJECT_TYPE
from ..common.checksums import ChecksumModel

Expand Down Expand Up @@ -243,6 +243,10 @@ def is_staff_group(self):
def is_superadmin_group(self):
return self.has_auth_group(SUPERADMIN_GROUP)

@property
def is_core_group(self):
return self.has_auth_group(CORE_USER_GROUP)

@property
def auth_headers(self):
return {'Authorization': f'Token {self.get_token()}'}
Expand Down
Loading