Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e78ad8a
add crossencoder model
dasgoutam Apr 2, 2025
23d434c
test search pipeline
dasgoutam Apr 6, 2025
c1bde09
change pipeline params
dasgoutam Apr 8, 2025
6b844a3
add model id of reranker
dasgoutam Apr 9, 2025
206b48e
add var for reranker model in search pipeline
dasgoutam Apr 9, 2025
29741bd
try by field reranking
dasgoutam Apr 16, 2025
c0219a2
correct typo
dasgoutam Apr 16, 2025
47f5ed6
revert to cross encoder model
dasgoutam Apr 30, 2025
fac8a90
increase number of docs
dasgoutam Jun 4, 2025
45d4abf
test commit
dasgoutam Jun 4, 2025
046324f
try no score threshold
dasgoutam Jun 4, 2025
f25dde6
change crossencoder model
dasgoutam Jun 5, 2025
82d39af
add huggingface to reranker model name
dasgoutam Jun 5, 2025
1816b2b
change error messages
dasgoutam Jun 5, 2025
93975fa
change payload model group
dasgoutam Jun 5, 2025
14167a5
add logging statement
dasgoutam Jun 5, 2025
11d585a
change logging to print
dasgoutam Jun 5, 2025
b25fd10
revert back to logger
dasgoutam Jun 5, 2025
62d296e
add logging for model registration
dasgoutam Jun 6, 2025
dcdc7cf
revert to opensearch pretrained model
dasgoutam Jun 6, 2025
dbcb85b
convert crossencoder logits using sigmoid
dasgoutam Jun 6, 2025
3db4861
remove type conversion from sigmoid
dasgoutam Jun 6, 2025
78a2ce2
change search score threshold back to 0.1
dasgoutam Jun 10, 2025
f46004b
change score threshold to 0.05
dasgoutam Jun 10, 2025
e4843c4
no threshold
dasgoutam Jun 10, 2025
3278a86
change parameters
dasgoutam Jun 10, 2025
e0474fd
change parameters
dasgoutam Jun 11, 2025
d3957ac
change k to 30
dasgoutam Jun 11, 2025
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
7 changes: 6 additions & 1 deletion integreat_chat/core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,13 @@
RAG_FALLBACK_LANGUAGE = "en"

# SEARCH_MAX_DOCUMENTS - number of documents retrieved from the VDB
SEARCH_MAX_DOCUMENTS = 15
SEARCH_MAX_DOCUMENTS = 30
SEARCH_SCORE_THRESHOLD = 0.1
SEARCH_MAX_PAGES = 10
SEARCH_EMBEDDING_MODEL_NAME = "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"
OPENSEARCH_CROSSENCODER_MODEL_NAME = (
"huggingface/cross-encoders/ms-marco-MiniLM-L-6-v2"
)
OPENSEARCH_EMBEDDING_MODEL_NAME = (
"huggingface/sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"
)
Expand All @@ -115,7 +118,9 @@
SEARCH_DENSE_WEIGHT = 0.7
SEARCH_FALLBACK_LANGUAGE = "en"
SEARCH_OPENSEARCH_MODEL_ID = config["OPENSEARCH"]["MODEL_ID"]
SEARCH_OPENSEARCH_MODEL_ID_RERANKER = config["OPENSEARCH"]["MODEL_ID_RERANKER"]
SEARCH_OPENSEARCH_MODEL_GROUP_ID = config["OPENSEARCH"]["MODEL_GROUP_ID"]

OPENSEARCH_USER = (
config["OPENSEARCH"]["USER"]
if "USER" in config["OPENSEARCH"]
Expand Down
5 changes: 3 additions & 2 deletions integreat_chat/search/management/commands/opensearch_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ class Command(BaseCommand):

def handle(self, *args, **options):
oss = OpenSearchSetup(password=settings.OPENSEARCH_PASSWORD)
group_id, model_id = oss.setup()
group_id, model_id, model_id_ce = oss.setup()
self.stdout.write(
self.style.SUCCESS( # pylint: disable=no-member
f'Successfully set up OpenSearch. Change the following settings '
f'in the OPENSEARCH section of your config:\n'
f'MODEL_GROUP_ID = {group_id}\n'
f'MODEL_ID = {model_id}\n'
f'MODEL_ID_EMBEDDING_MODEL = {model_id}\n'
f'MODEL_ID_CROSSENCODER_MODEL = {model_id_ce}\n'
)
)
82 changes: 70 additions & 12 deletions integreat_chat/search/services/opensearch.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@
Setup and use of OpenSearch
"""

import logging
import hashlib
import time
from datetime import timedelta, datetime
import math

import requests
from django.conf import settings
from langchain_text_splitters import HTMLHeaderTextSplitter
from integreat_chat.core.utils.integreat_cms import get_all_pages, get_parent_page_titles

LOGGER = logging.getLogger("django")

class OpenSearch:
"""
Class for searching and updating documents in OpenSearch
Expand All @@ -36,6 +40,7 @@ def __init__(
self.user = user
self.password = password
self.model_id = settings.SEARCH_OPENSEARCH_MODEL_ID
self.model_id_reranker = settings.SEARCH_OPENSEARCH_MODEL_ID_RERANKER
self.model_group_id = settings.SEARCH_OPENSEARCH_MODEL_GROUP_ID

def request(self, path: str, payload: dict, method: str = "GET") -> dict:
Expand Down Expand Up @@ -75,6 +80,7 @@ def reduce_search_result(
"""
result = []
found_urls = []
sigmoid = lambda x: 1 / (1 + math.exp(-x))
if "hits" not in response:
raise ValueError("Missing hits in result")
for document in response["hits"]["hits"]:
Expand All @@ -87,7 +93,7 @@ def reduce_search_result(
"url": document["_source"]["url"],
"title": document["_source"]["title"],
"parent_titles": document["_source"]["parent_titles"],
"score": document["_score"],
"score": sigmoid(document["_score"]),
"chunk_text": document["_source"]["chunk_text"],
})
found_urls.append(document["_source"]["url"])
Expand Down Expand Up @@ -129,7 +135,7 @@ def search(self, region_slug: str, language_slug: str, message: str) -> dict:
"title_embedding": {
"query_text": message,
"model_id": self.model_id,
"k": 5
"k": 30
}
}
},
Expand All @@ -138,12 +144,19 @@ def search(self, region_slug: str, language_slug: str, message: str) -> dict:
"chunk_embedding": {
"query_text": message,
"model_id": self.model_id,
"k": 5
"k": 30
}
}
}
]
}
},
"ext": {
"rerank": {
"query_context": {
"query_text": message
}
}
}
}
return self.request(
Expand Down Expand Up @@ -325,20 +338,26 @@ def setup(self) -> str:
group_id = self.create_model_group()
if not group_id:
raise ValueError("Unexpected OpenSearch response while creating model group")
model_id = self.register_embedding_model(group_id)
if not model_id:
raise ValueError("Unexpected OpenSearch response while registering model")
self.deploy_model(model_id)
self.create_ingestion_pipeline(model_id)
model_id_embedding = self.register_embedding_model(group_id)
model_id_crossencoder = self.register_crossencoder_model(group_id)
if not model_id_embedding:
raise ValueError("Unexpected OpenSearch response while registering embedding model")
elif not model_id_crossencoder:
raise ValueError("Unexpected OpenSearch response while registering crossencoder model")
self.deploy_model(model_id_embedding)
self.deploy_model(model_id_crossencoder)
self.create_ingestion_pipeline(model_id_embedding)
self.create_search_pipeline()
return group_id, model_id
return group_id, model_id_embedding, model_id_crossencoder

def delete_model_group(self):
"""
Delete previously created model group and model
"""
self.request(f"/_plugins/_ml/models/{self.model_id}/_undeploy", {}, "POST")
self.request(f"/_plugins/_ml/models/{self.model_id}", {}, "DELETE")
self.request(f"/_plugins/_ml/models/{self.model_id_reranker}/_undeploy", {}, "POST")
self.request(f"/_plugins/_ml/models/{self.model_id_reranker}", {}, "DELETE")
self.request(f"/_plugins/_ml/model_groups/{self.model_group_id}", {}, "DELETE")

def prepare_index(self, region_slug: str = "", language_slug: str = ""):
Expand Down Expand Up @@ -369,13 +388,37 @@ def create_model_group(self):
Create model group
"""
payload = {
"name": "integreat-chat-2025-01-31",
"description": "Integreat Chat embedding models"
"name": "integreat-chat-2025-06-05",
"description": "Integreat Chat model group"
}
response = self.request("/_plugins/_ml/model_groups/_register", payload, "POST")
LOGGER.debug(f"Model group response: {response}")
if "model_group_id" in response:
return response["model_group_id"]
return False

def register_crossencoder_model(self, model_group_id: str) -> str:
"""
Register crossencoder model
"""
payload = {
"name": settings.OPENSEARCH_CROSSENCODER_MODEL_NAME,
"version": "1.0.2",
"model_group_id": model_group_id,
"model_format": "TORCH_SCRIPT"
}
register_response = self.request(
"/_plugins/_ml/models/_register", payload, "POST"
)
LOGGER.debug(f"Embedding model response: {register_response}")
if "task_id" in register_response:
for n in range(0, 10):
time.sleep(5)
if "model_id" in (task_response := self.request(
f"/_plugins/_ml/tasks/{register_response['task_id']}", {}, "GET"
)):
return task_response["model_id"]
return False

def register_embedding_model(self, model_group_id: str) -> str:
"""
Expand All @@ -390,6 +433,7 @@ def register_embedding_model(self, model_group_id: str) -> str:
register_response = self.request(
"/_plugins/_ml/models/_register", payload, "POST"
)
LOGGER.debug(f"Cross Encoder model response: {register_response}")
if "task_id" in register_response:
for n in range(0, 10): # pylint: disable=W0612
time.sleep(5)
Expand Down Expand Up @@ -461,7 +505,20 @@ def create_search_pipeline(self):
}
}
}
]
],
"response_processors": [
{
"rerank": {
"ml_opensearch": {
"model_id": self.model_id_reranker
},
"context": {
"document_fields": [
"chunk_text"
]
}
}
}]
}
self.request(f"/_search/pipeline/{self.search_pipeline_name}", payload, "PUT")

Expand Down Expand Up @@ -496,6 +553,7 @@ def create_index(self, index_slug: str):
"settings": {
"index.knn": True,
"default_pipeline": self.ingest_pipeline_name,
"index.search.default_pipeline": self.search_pipeline_name,
},
"mappings": {
"properties": {
Expand Down