From e9a2b8f03a3b8e7f732abf50107934edb248641c Mon Sep 17 00:00:00 2001 From: Niharika Goulikar Date: Tue, 26 Nov 2024 12:16:21 +0000 Subject: [PATCH 01/66] Fixed the feedback issue --- application/api/user/routes.py | 24 +++++++++++-------- frontend/src/conversation/Conversation.tsx | 2 +- .../src/conversation/ConversationBubble.tsx | 20 +++++++++++++--- .../src/conversation/conversationHandlers.ts | 4 ++++ 4 files changed, 36 insertions(+), 14 deletions(-) diff --git a/application/api/user/routes.py b/application/api/user/routes.py index e305845d..0e4a9f4d 100644 --- a/application/api/user/routes.py +++ b/application/api/user/routes.py @@ -176,10 +176,12 @@ class SubmitFeedback(Resource): "FeedbackModel", { "question": fields.String( - required=True, description="The user question" + required=False, description="The user question" ), - "answer": fields.String(required=True, description="The AI answer"), + "answer": fields.String(required=False, description="The AI answer"), "feedback": fields.String(required=True, description="User feedback"), + "question_index":fields.Integer(required=True, description="The question number in that particular conversation"), + "conversation_id":fields.String(required=True, description="id of the particular conversation"), "api_key": fields.String(description="Optional API key"), }, ) @@ -189,23 +191,25 @@ class SubmitFeedback(Resource): ) def post(self): data = request.get_json() - required_fields = ["question", "answer", "feedback"] + required_fields = [ "feedback","conversation_id","question_index"] missing_fields = check_required_fields(data, required_fields) if missing_fields: return missing_fields - new_doc = { - "question": data["question"], - "answer": data["answer"], - "feedback": data["feedback"], - "timestamp": datetime.datetime.now(datetime.timezone.utc), - } if "api_key" in data: new_doc["api_key"] = data["api_key"] try: - feedback_collection.insert_one(new_doc) + conversations_collection.update_one( + {"_id": ObjectId(data["conversation_id"]), f"queries.{data["question_index"]}": {"$exists": True}}, + { + "$set": { + f"queries.{data["question_index"]}.feedback": data["feedback"] + } + } + ) + except Exception as err: return make_response(jsonify({"success": False, "error": str(err)}), 400) diff --git a/frontend/src/conversation/Conversation.tsx b/frontend/src/conversation/Conversation.tsx index ed69064a..41bc8d2f 100644 --- a/frontend/src/conversation/Conversation.tsx +++ b/frontend/src/conversation/Conversation.tsx @@ -111,7 +111,7 @@ export default function Conversation() { const handleFeedback = (query: Query, feedback: FEEDBACK, index: number) => { const prevFeedback = query.feedback; dispatch(updateQuery({ index, query: { feedback } })); - handleSendFeedback(query.prompt, query.response!, feedback).catch(() => + handleSendFeedback(query.prompt, query.response!, feedback,conversationId as string,index).catch(() => dispatch(updateQuery({ index, query: { feedback: prevFeedback } })), ); }; diff --git a/frontend/src/conversation/ConversationBubble.tsx b/frontend/src/conversation/ConversationBubble.tsx index 567b09e9..7fe1b003 100644 --- a/frontend/src/conversation/ConversationBubble.tsx +++ b/frontend/src/conversation/ConversationBubble.tsx @@ -429,6 +429,11 @@ const ConversationBubble = forwardRef< feedback === 'LIKE' || type !== 'ERROR' ? 'group-hover:lg:visible' : '' + } + ${ + feedback === 'DISLIKE' && type !== 'ERROR' + ? ' hidden' + : '' }`} >
@@ -445,11 +450,14 @@ const ConversationBubble = forwardRef< isLikeClicked || feedback === 'LIKE' ? 'fill-white-3000 stroke-purple-30 dark:fill-transparent' : 'fill-none stroke-gray-4000' - }`} + } `} onClick={() => { + if(feedback===undefined){ + console.log("liked") handleFeedback?.('LIKE'); setIsLikeClicked(true); setIsDislikeClicked(false); + } }} onMouseEnter={() => setIsLikeHovered(true)} onMouseLeave={() => setIsLikeHovered(false)} @@ -462,9 +470,13 @@ const ConversationBubble = forwardRef< !isDislikeClicked ? 'lg:invisible' : '' } ${ feedback === 'DISLIKE' || type !== 'ERROR' - ? 'group-hover:lg:visible' + ? ' group-hover:lg:visible' : '' - }`} + } ${ + feedback === 'LIKE' && type !== 'ERROR' + ? ' hidden' + : '' + } `} >
{ + if(feedback===undefined){ handleFeedback?.('DISLIKE'); setIsDislikeClicked(true); setIsLikeClicked(false); + } }} onMouseEnter={() => setIsDislikeHovered(true)} onMouseLeave={() => setIsDislikeHovered(false)} diff --git a/frontend/src/conversation/conversationHandlers.ts b/frontend/src/conversation/conversationHandlers.ts index be046bca..ea8ad6ea 100644 --- a/frontend/src/conversation/conversationHandlers.ts +++ b/frontend/src/conversation/conversationHandlers.ts @@ -202,12 +202,16 @@ export function handleSendFeedback( prompt: string, response: string, feedback: FEEDBACK, + conversation_id:string, + prompt_index:number ) { return conversationService .feedback({ question: prompt, answer: response, feedback: feedback, + conversation_id:conversation_id, + question_index:prompt_index }) .then((response) => { if (response.ok) { From faf031ce8037aac6abbbb35ad66447df98c5ff3b Mon Sep 17 00:00:00 2001 From: Niharika Goulikar Date: Tue, 26 Nov 2024 12:28:16 +0000 Subject: [PATCH 02/66] fixed linting issues --- application/api/user/routes.py | 4 --- .../src/conversation/ConversationBubble.tsx | 33 +++++++------------ 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/application/api/user/routes.py b/application/api/user/routes.py index 0e4a9f4d..96fc0e0f 100644 --- a/application/api/user/routes.py +++ b/application/api/user/routes.py @@ -196,10 +196,6 @@ class SubmitFeedback(Resource): if missing_fields: return missing_fields - - if "api_key" in data: - new_doc["api_key"] = data["api_key"] - try: conversations_collection.update_one( {"_id": ObjectId(data["conversation_id"]), f"queries.{data["question_index"]}": {"$exists": True}}, diff --git a/frontend/src/conversation/ConversationBubble.tsx b/frontend/src/conversation/ConversationBubble.tsx index 7fe1b003..03a3ab08 100644 --- a/frontend/src/conversation/ConversationBubble.tsx +++ b/frontend/src/conversation/ConversationBubble.tsx @@ -430,11 +430,7 @@ const ConversationBubble = forwardRef< ? 'group-hover:lg:visible' : '' } - ${ - feedback === 'DISLIKE' && type !== 'ERROR' - ? ' hidden' - : '' - }`} + ${feedback === 'DISLIKE' && type !== 'ERROR' ? 'hidden' : ''}`} >
{ - if(feedback===undefined){ - console.log("liked") - handleFeedback?.('LIKE'); - setIsLikeClicked(true); - setIsDislikeClicked(false); + if (feedback === undefined) { + handleFeedback?.('LIKE'); + setIsLikeClicked(true); + setIsDislikeClicked(false); } }} onMouseEnter={() => setIsLikeHovered(true)} @@ -470,13 +465,9 @@ const ConversationBubble = forwardRef< !isDislikeClicked ? 'lg:invisible' : '' } ${ feedback === 'DISLIKE' || type !== 'ERROR' - ? ' group-hover:lg:visible' + ? 'group-hover:lg:visible' : '' - } ${ - feedback === 'LIKE' && type !== 'ERROR' - ? ' hidden' - : '' - } `} + } ${feedback === 'LIKE' && type !== 'ERROR' ? ' hidden' : ''} `} >
{ - if(feedback===undefined){ - handleFeedback?.('DISLIKE'); - setIsDislikeClicked(true); - setIsLikeClicked(false); + if (feedback === undefined) { + handleFeedback?.('DISLIKE'); + setIsDislikeClicked(true); + setIsLikeClicked(false); } }} onMouseEnter={() => setIsDislikeHovered(true)} From 9d4aee5de2a9bbefc33b61f55da5d2a5cc6e11a0 Mon Sep 17 00:00:00 2001 From: Niharika Goulikar Date: Tue, 26 Nov 2024 13:05:50 +0000 Subject: [PATCH 03/66] fixed the python error --- application/api/user/routes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/api/user/routes.py b/application/api/user/routes.py index 96fc0e0f..6eb57cc0 100644 --- a/application/api/user/routes.py +++ b/application/api/user/routes.py @@ -198,10 +198,10 @@ class SubmitFeedback(Resource): try: conversations_collection.update_one( - {"_id": ObjectId(data["conversation_id"]), f"queries.{data["question_index"]}": {"$exists": True}}, + {"_id": ObjectId(data["conversation_id"]), f"queries.{data['question_index']}": {"$exists": True}}, { "$set": { - f"queries.{data["question_index"]}.feedback": data["feedback"] + f"queries.{data['question_index']}.feedback": data["feedback"] } } ) From 1f649274d1f1a7f7f0d94d52b76602b166cdd1bf Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 5 Dec 2024 22:44:40 +0000 Subject: [PATCH 04/66] feat: tooling init --- application/cache.py | 18 ++--- application/llm/base.py | 14 ++-- application/llm/openai.py | 22 +++++-- application/requirements.txt | 2 +- application/retriever/classic_rag.py | 11 ++-- application/tools/agent.py | 98 ++++++++++++++++++++++++++++ application/tools/base.py | 20 ++++++ application/tools/cryptoprice.py | 73 +++++++++++++++++++++ application/tools/telegram.py | 79 ++++++++++++++++++++++ application/tools/tool_manager.py | 43 ++++++++++++ application/usage.py | 19 ++++-- application/utils.py | 16 ++++- 12 files changed, 383 insertions(+), 32 deletions(-) create mode 100644 application/tools/agent.py create mode 100644 application/tools/base.py create mode 100644 application/tools/cryptoprice.py create mode 100644 application/tools/telegram.py create mode 100644 application/tools/tool_manager.py diff --git a/application/cache.py b/application/cache.py index 33022e45..7239abac 100644 --- a/application/cache.py +++ b/application/cache.py @@ -5,6 +5,7 @@ import logging from threading import Lock from application.core.settings import settings from application.utils import get_hash +import sys logger = logging.getLogger(__name__) @@ -23,18 +24,19 @@ def get_redis_instance(): _redis_instance = None return _redis_instance -def gen_cache_key(*messages, model="docgpt"): +def gen_cache_key(messages, model="docgpt", tools=None): if not all(isinstance(msg, dict) for msg in messages): raise ValueError("All messages must be dictionaries.") - messages_str = json.dumps(list(messages), sort_keys=True) - combined = f"{model}_{messages_str}" + messages_str = json.dumps(messages) + tools_str = json.dumps(tools) if tools else "" + combined = f"{model}_{messages_str}_{tools_str}" cache_key = get_hash(combined) return cache_key def gen_cache(func): - def wrapper(self, model, messages, *args, **kwargs): + def wrapper(self, model, messages, stream, tools=None, *args, **kwargs): try: - cache_key = gen_cache_key(*messages) + cache_key = gen_cache_key(messages, model, tools) redis_client = get_redis_instance() if redis_client: try: @@ -44,8 +46,8 @@ def gen_cache(func): except redis.ConnectionError as e: logger.error(f"Redis connection error: {e}") - result = func(self, model, messages, *args, **kwargs) - if redis_client: + result = func(self, model, messages, stream, tools, *args, **kwargs) + if redis_client and isinstance(result, str): try: redis_client.set(cache_key, result, ex=1800) except redis.ConnectionError as e: @@ -59,7 +61,7 @@ def gen_cache(func): def stream_cache(func): def wrapper(self, model, messages, stream, *args, **kwargs): - cache_key = gen_cache_key(*messages) + cache_key = gen_cache_key(messages) logger.info(f"Stream cache key: {cache_key}") redis_client = get_redis_instance() diff --git a/application/llm/base.py b/application/llm/base.py index 1caab5d3..b9b0e524 100644 --- a/application/llm/base.py +++ b/application/llm/base.py @@ -13,12 +13,12 @@ class BaseLLM(ABC): return method(self, *args, **kwargs) @abstractmethod - def _raw_gen(self, model, messages, stream, *args, **kwargs): + def _raw_gen(self, model, messages, stream, tools, *args, **kwargs): pass - def gen(self, model, messages, stream=False, *args, **kwargs): + def gen(self, model, messages, stream=False, tools=None, *args, **kwargs): decorators = [gen_token_usage, gen_cache] - return self._apply_decorator(self._raw_gen, decorators=decorators, model=model, messages=messages, stream=stream, *args, **kwargs) + return self._apply_decorator(self._raw_gen, decorators=decorators, model=model, messages=messages, stream=stream, tools=tools, *args, **kwargs) @abstractmethod def _raw_gen_stream(self, model, messages, stream, *args, **kwargs): @@ -26,4 +26,10 @@ class BaseLLM(ABC): def gen_stream(self, model, messages, stream=True, *args, **kwargs): decorators = [stream_cache, stream_token_usage] - return self._apply_decorator(self._raw_gen_stream, decorators=decorators, model=model, messages=messages, stream=stream, *args, **kwargs) \ No newline at end of file + return self._apply_decorator(self._raw_gen_stream, decorators=decorators, model=model, messages=messages, stream=stream, *args, **kwargs) + + def supports_tools(self): + return hasattr(self, '_supports_tools') and callable(getattr(self, '_supports_tools')) + + def _supports_tools(self): + raise NotImplementedError("Subclass must implement _supports_tools method") \ No newline at end of file diff --git a/application/llm/openai.py b/application/llm/openai.py index f85de6ea..cc2285a1 100644 --- a/application/llm/openai.py +++ b/application/llm/openai.py @@ -25,14 +25,20 @@ class OpenAILLM(BaseLLM): model, messages, stream=False, + tools=None, engine=settings.AZURE_DEPLOYMENT_NAME, **kwargs - ): - response = self.client.chat.completions.create( - model=model, messages=messages, stream=stream, **kwargs - ) - - return response.choices[0].message.content + ): + if tools: + response = self.client.chat.completions.create( + model=model, messages=messages, stream=stream, tools=tools, **kwargs + ) + return response.choices[0] + else: + response = self.client.chat.completions.create( + model=model, messages=messages, stream=stream, **kwargs + ) + return response.choices[0].message.content def _raw_gen_stream( self, @@ -40,6 +46,7 @@ class OpenAILLM(BaseLLM): model, messages, stream=True, + tools=None, engine=settings.AZURE_DEPLOYMENT_NAME, **kwargs ): @@ -52,6 +59,9 @@ class OpenAILLM(BaseLLM): # print(line.choices[0].delta.content, file=sys.stderr) if line.choices[0].delta.content is not None: yield line.choices[0].delta.content + + def _supports_tools(self): + return True class AzureOpenAILLM(OpenAILLM): diff --git a/application/requirements.txt b/application/requirements.txt index 2f28c2ea..c8f16d85 100644 --- a/application/requirements.txt +++ b/application/requirements.txt @@ -43,7 +43,7 @@ multidict==6.1.0 mypy-extensions==1.0.0 networkx==3.3 numpy==1.26.4 -openai==1.46.1 +openai==1.57.0 openapi-schema-validator==0.6.2 openapi-spec-validator==0.6.0 openapi3-parser==1.1.18 diff --git a/application/retriever/classic_rag.py b/application/retriever/classic_rag.py index 42e318d2..4ac52bc5 100644 --- a/application/retriever/classic_rag.py +++ b/application/retriever/classic_rag.py @@ -2,6 +2,7 @@ from application.retriever.base import BaseRetriever from application.core.settings import settings from application.vectorstore.vector_creator import VectorCreator from application.llm.llm_creator import LLMCreator +from application.tools.agent import Agent from application.utils import num_tokens_from_string @@ -90,10 +91,12 @@ class ClassicRAG(BaseRetriever): ) messages_combine.append({"role": "user", "content": self.question}) - llm = LLMCreator.create_llm( - settings.LLM_NAME, api_key=settings.API_KEY, user_api_key=self.user_api_key - ) - completion = llm.gen_stream(model=self.gpt_model, messages=messages_combine) + # llm = LLMCreator.create_llm( + # settings.LLM_NAME, api_key=settings.API_KEY, user_api_key=self.user_api_key + # ) + # completion = llm.gen_stream(model=self.gpt_model, messages=messages_combine) + agent = Agent(llm_name=settings.LLM_NAME,gpt_model=self.gpt_model, api_key=settings.API_KEY, user_api_key=self.user_api_key) + completion = agent.gen(messages_combine) for line in completion: yield {"answer": str(line)} diff --git a/application/tools/agent.py b/application/tools/agent.py new file mode 100644 index 00000000..2df14442 --- /dev/null +++ b/application/tools/agent.py @@ -0,0 +1,98 @@ +from application.llm.llm_creator import LLMCreator +from application.core.settings import settings +from application.tools.tool_manager import ToolManager +import json + +tool_tg = { + "name": "telegram_send_message", + "description": "Send a notification to telegram about current chat", + "parameters": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Text to send in the notification" + } + }, + "required": ["text"], + "additionalProperties": False + } +} + +tool_crypto = { + "name": "cryptoprice_get", + "description": "Retrieve the price of a specified cryptocurrency in a given currency", + "parameters": { + "type": "object", + "properties": { + "symbol": { + "type": "string", + "description": "The cryptocurrency symbol (e.g. BTC)" + }, + "currency": { + "type": "string", + "description": "The currency in which you want the price (e.g. USD)" + } + }, + "required": ["symbol", "currency"], + "additionalProperties": False + } +} + +class Agent: + def __init__(self, llm_name, gpt_model, api_key, user_api_key=None): + # Initialize the LLM with the provided parameters + self.llm = LLMCreator.create_llm(llm_name, api_key=api_key, user_api_key=user_api_key) + self.gpt_model = gpt_model + # Static tool configuration (to be replaced later) + self.tools = [ + { + "type": "function", + "function": tool_crypto + } + ] + self.tool_config = { + } + + def gen(self, messages): + # Generate initial response from the LLM + resp = self.llm.gen(model=self.gpt_model, messages=messages, tools=self.tools) + + if isinstance(resp, str): + # Yield the response if it's a string and exit + yield resp + return + + while resp.finish_reason == "tool_calls": + # Append the assistant's message to the conversation + messages.append(json.loads(resp.model_dump_json())['message']) + # Handle each tool call + tool_calls = resp.message.tool_calls + for call in tool_calls: + tm = ToolManager(config={}) + call_name = call.function.name + call_args = json.loads(call.function.arguments) + call_id = call.id + # Determine the tool name and load it + tool_name = call_name.split("_")[0] + tool = tm.load_tool(tool_name, tool_config=self.tool_config) + # Execute the tool's action + resp_tool = tool.execute_action(call_name, **call_args) + # Append the tool's response to the conversation + messages.append( + { + "role": "tool", + "content": str(resp_tool), + "tool_call_id": call_id + } + ) + # Generate a new response from the LLM after processing tools + resp = self.llm.gen(model=self.gpt_model, messages=messages, tools=self.tools) + + # If no tool calls are needed, generate the final response + if isinstance(resp, str): + yield resp + else: + completion = self.llm.gen_stream(model=self.gpt_model, messages=messages, tools=self.tools) + for line in completion: + yield line diff --git a/application/tools/base.py b/application/tools/base.py new file mode 100644 index 00000000..00cfee3a --- /dev/null +++ b/application/tools/base.py @@ -0,0 +1,20 @@ +from abc import ABC, abstractmethod + +class Tool(ABC): + @abstractmethod + def execute_action(self, action_name: str, **kwargs): + pass + + @abstractmethod + def get_actions_metadata(self): + """ + Returns a list of JSON objects describing the actions supported by the tool. + """ + pass + + @abstractmethod + def get_config_requirements(self): + """ + Returns a dictionary describing the configuration requirements for the tool. + """ + pass diff --git a/application/tools/cryptoprice.py b/application/tools/cryptoprice.py new file mode 100644 index 00000000..d7cf61e1 --- /dev/null +++ b/application/tools/cryptoprice.py @@ -0,0 +1,73 @@ +from application.tools.base import Tool +import requests + +class CryptoPriceTool(Tool): + def __init__(self, config): + self.config = config + + def execute_action(self, action_name, **kwargs): + actions = { + "cryptoprice_get": self.get_price + } + + if action_name in actions: + return actions[action_name](**kwargs) + else: + raise ValueError(f"Unknown action: {action_name}") + + def get_price(self, symbol, currency): + """ + Fetches the current price of a given cryptocurrency symbol in the specified currency. + Example: + symbol = "BTC" + currency = "USD" + returns price in USD. + """ + url = f"https://min-api.cryptocompare.com/data/price?fsym={symbol.upper()}&tsyms={currency.upper()}" + response = requests.get(url) + if response.status_code == 200: + data = response.json() + # data will be like {"USD": } if the call is successful + if currency.upper() in data: + return { + "status_code": response.status_code, + "price": data[currency.upper()], + "message": f"Price of {symbol.upper()} in {currency.upper()} retrieved successfully." + } + else: + return { + "status_code": response.status_code, + "message": f"Couldn't find price for {symbol.upper()} in {currency.upper()}." + } + else: + return { + "status_code": response.status_code, + "message": "Failed to retrieve price." + } + + def get_actions_metadata(self): + return [ + { + "name": "cryptoprice_get", + "description": "Retrieve the price of a specified cryptocurrency in a given currency", + "parameters": { + "type": "object", + "properties": { + "symbol": { + "type": "string", + "description": "The cryptocurrency symbol (e.g. BTC)" + }, + "currency": { + "type": "string", + "description": "The currency in which you want the price (e.g. USD)" + } + }, + "required": ["symbol", "currency"], + "additionalProperties": False + } + } + ] + + def get_config_requirements(self): + # No specific configuration needed for this tool as it just queries a public endpoint + return {} diff --git a/application/tools/telegram.py b/application/tools/telegram.py new file mode 100644 index 00000000..8210d8e7 --- /dev/null +++ b/application/tools/telegram.py @@ -0,0 +1,79 @@ +from application.tools.base import Tool +import requests + +class TelegramTool(Tool): + def __init__(self, config): + self.config = config + self.chat_id = config.get("chat_id", "142189016") + self.token = config.get("token", "YOUR_TG_TOKEN") + + def execute_action(self, action_name, **kwargs): + actions = { + "telegram_send_message": self.send_message, + "telegram_send_image": self.send_image + } + + if action_name in actions: + return actions[action_name](**kwargs) + else: + raise ValueError(f"Unknown action: {action_name}") + + def send_message(self, text): + print(f"Sending message: {text}") + url = f"https://api.telegram.org/bot{self.token}/sendMessage" + payload = {"chat_id": self.chat_id, "text": text} + response = requests.post(url, data=payload) + return {"status_code": response.status_code, "message": "Message sent"} + + def send_image(self, image_url): + print(f"Sending image: {image_url}") + url = f"https://api.telegram.org/bot{self.token}/sendPhoto" + payload = {"chat_id": self.chat_id, "photo": image_url} + response = requests.post(url, data=payload) + return {"status_code": response.status_code, "message": "Image sent"} + + def get_actions_metadata(self): + return [ + { + "name": "telegram_send_message", + "description": "Send a notification to telegram chat", + "parameters": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Text to send in the notification" + } + }, + "required": ["text"], + "additionalProperties": False + } + }, + { + "name": "telegram_send_image", + "description": "Send an image to the Telegram chat", + "parameters": { + "type": "object", + "properties": { + "image_url": { + "type": "string", + "description": "URL of the image to send" + } + }, + "required": ["image_url"], + "additionalProperties": False + } + } + ] + + def get_config_requirements(self): + return { + "chat_id": { + "type": "string", + "description": "Telegram chat ID to send messages to" + }, + "token": { + "type": "string", + "description": "Bot token for authentication" + } + } diff --git a/application/tools/tool_manager.py b/application/tools/tool_manager.py new file mode 100644 index 00000000..10231cb2 --- /dev/null +++ b/application/tools/tool_manager.py @@ -0,0 +1,43 @@ +import importlib +import inspect +import pkgutil +import os + +from application.tools.base import Tool + +class ToolManager: + def __init__(self, config): + self.config = config + self.tools = {} + self.load_tools() + + def load_tools(self): + tools_dir = os.path.dirname(__file__) + for finder, name, ispkg in pkgutil.iter_modules([tools_dir]): + if name == 'base' or name.startswith('__'): + continue + module = importlib.import_module(f'application.tools.{name}') + for member_name, obj in inspect.getmembers(module, inspect.isclass): + if issubclass(obj, Tool) and obj is not Tool: + tool_config = self.config.get(name, {}) + self.tools[name] = obj(tool_config) + + def load_tool(self, tool_name, tool_config): + self.config[tool_name] = tool_config + tools_dir = os.path.dirname(__file__) + module = importlib.import_module(f'application.tools.{tool_name}') + for member_name, obj in inspect.getmembers(module, inspect.isclass): + if issubclass(obj, Tool) and obj is not Tool: + return obj(tool_config) + + + def execute_action(self, tool_name, action_name, **kwargs): + if tool_name not in self.tools: + raise ValueError(f"Tool '{tool_name}' not loaded") + return self.tools[tool_name].execute_action(action_name, **kwargs) + + def get_all_actions_metadata(self): + metadata = [] + for tool in self.tools.values(): + metadata.extend(tool.get_actions_metadata()) + return metadata diff --git a/application/usage.py b/application/usage.py index e87ebe38..fe4cd50e 100644 --- a/application/usage.py +++ b/application/usage.py @@ -1,7 +1,7 @@ import sys from datetime import datetime from application.core.mongo_db import MongoDB -from application.utils import num_tokens_from_string +from application.utils import num_tokens_from_string, num_tokens_from_object_or_list mongo = MongoDB.get_client() db = mongo["docsgpt"] @@ -21,11 +21,16 @@ def update_token_usage(user_api_key, token_usage): def gen_token_usage(func): - def wrapper(self, model, messages, stream, **kwargs): + def wrapper(self, model, messages, stream, tools, **kwargs): for message in messages: - self.token_usage["prompt_tokens"] += num_tokens_from_string(message["content"]) - result = func(self, model, messages, stream, **kwargs) - self.token_usage["generated_tokens"] += num_tokens_from_string(result) + if message["content"]: + self.token_usage["prompt_tokens"] += num_tokens_from_string(message["content"]) + result = func(self, model, messages, stream, tools, **kwargs) + # check if result is a string + if isinstance(result, str): + self.token_usage["generated_tokens"] += num_tokens_from_string(result) + else: + self.token_usage["generated_tokens"] += num_tokens_from_object_or_list(result) update_token_usage(self.user_api_key, self.token_usage) return result @@ -33,11 +38,11 @@ def gen_token_usage(func): def stream_token_usage(func): - def wrapper(self, model, messages, stream, **kwargs): + def wrapper(self, model, messages, stream, tools, **kwargs): for message in messages: self.token_usage["prompt_tokens"] += num_tokens_from_string(message["content"]) batch = [] - result = func(self, model, messages, stream, **kwargs) + result = func(self, model, messages, stream, tools, **kwargs) for r in result: batch.append(r) yield r diff --git a/application/utils.py b/application/utils.py index 1fc9e329..3b2eb9f3 100644 --- a/application/utils.py +++ b/application/utils.py @@ -15,9 +15,21 @@ def get_encoding(): def num_tokens_from_string(string: str) -> int: encoding = get_encoding() - num_tokens = len(encoding.encode(string)) - return num_tokens + if isinstance(string, str): + num_tokens = len(encoding.encode(string)) + return num_tokens + else: + return 0 +def num_tokens_from_object_or_list(thing): + if isinstance(thing, list): + return sum([num_tokens_from_object_or_list(x) for x in thing]) + elif isinstance(thing, dict): + return sum([num_tokens_from_object_or_list(x) for x in thing.values()]) + elif isinstance(thing, str): + return num_tokens_from_string(thing) + else: + return 0 def count_tokens_docs(docs): docs_content = "" From 863950963f867e6fc5af2beb54ce53db7b7bc75e Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 6 Dec 2024 22:19:01 +0000 Subject: [PATCH 05/66] simple user tool handling endpoint --- application/api/user/routes.py | 121 +++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/application/api/user/routes.py b/application/api/user/routes.py index 6a2f3bea..814490dd 100644 --- a/application/api/user/routes.py +++ b/application/api/user/routes.py @@ -30,6 +30,7 @@ api_key_collection = db["api_keys"] token_usage_collection = db["token_usage"] shared_conversations_collections = db["shared_conversations"] user_logs_collection = db["user_logs"] +user_tools_collection = db["user_tools"] user = Blueprint("user", __name__) user_ns = Namespace("user", description="User related operations", path="/") @@ -1786,3 +1787,123 @@ class TextToSpeech(Resource): ) except Exception as err: return make_response(jsonify({"success": False, "error": str(err)}), 400) + + +@user_ns.route("/api/create_tool") +class CreateTool(Resource): + # write code such that it will accept tool_name, took_config and tool_actions + create_tool_model = api.model( + "CreateToolModel", + { + "tool_name": fields.String(required=True, description="Name of the tool"), + "tool_config": fields.Raw(required=True, description="Configuration of the tool"), + "tool_actions": fields.List(required=True, description="Actions the tool can perform"), + }, + ) + + @api.expect(create_tool_model) + @api.doc(description="Create a new tool") + def post(self): + data = request.get_json() + required_fields = ["tool_name", "tool_config", "tool_actions"] + missing_fields = check_required_fields(data, required_fields) + if missing_fields: + return missing_fields + + user = "local" + try: + new_tool = { + "tool_name": data["tool_name"], + "tool_config": data["tool_config"], + "tool_actions": data["tool_actions"], + "user": user, + } + resp = user_tools_collection.insert_one(new_tool) + new_id = str(resp.inserted_id) + except Exception as err: + return make_response(jsonify({"success": False, "error": str(err)}), 400) + + return make_response(jsonify({"id": new_id}), 200) + +@user_ns.route("/api/update_tool_config") +class UpdateToolConfig(Resource): + update_tool_config_model = api.model( + "UpdateToolConfigModel", + { + "tool_id": fields.String(required=True, description="Tool ID"), + "tool_config": fields.Raw(required=True, description="Configuration of the tool"), + }, + ) + + @api.expect(update_tool_config_model) + @api.doc(description="Update the configuration of a tool") + def post(self): + data = request.get_json() + required_fields = ["tool_id", "tool_config"] + missing_fields = check_required_fields(data, required_fields) + if missing_fields: + return missing_fields + + try: + user_tools_collection.update_one( + {"_id": ObjectId(data["tool_id"])}, + {"$set": {"tool_config": data["tool_config"]}}, + ) + except Exception as err: + return make_response(jsonify({"success": False, "error": str(err)}), 400) + + return make_response(jsonify({"success": True}), 200) + +@user_ns.route("/api/update_tool_actions") +class UpdateToolActions(Resource): + update_tool_actions_model = api.model( + "UpdateToolActionsModel", + { + "tool_id": fields.String(required=True, description="Tool ID"), + "tool_actions": fields.List(required=True, description="Actions the tool can perform"), + }, + ) + + @api.expect(update_tool_actions_model) + @api.doc(description="Update the actions of a tool") + def post(self): + data = request.get_json() + required_fields = ["tool_id", "tool_actions"] + missing_fields = check_required_fields(data, required_fields) + if missing_fields: + return missing_fields + + try: + user_tools_collection.update_one( + {"_id": ObjectId(data["tool_id"])}, + {"$set": {"tool_actions": data["tool_actions"]}}, + ) + except Exception as err: + return make_response(jsonify({"success": False, "error": str(err)}), 400) + + return make_response(jsonify({"success": True}), 200) + +@user_ns.route("/api/delete_tool") +class DeleteTool(Resource): + delete_tool_model = api.model( + "DeleteToolModel", + {"tool_id": fields.String(required=True, description="Tool ID")}, + ) + + @api.expect(delete_tool_model) + @api.doc(description="Delete a tool by ID") + def post(self): + data = request.get_json() + required_fields = ["tool_id"] + missing_fields = check_required_fields(data, required_fields) + if missing_fields: + return missing_fields + + try: + result = user_tools_collection.delete_one({"_id": ObjectId(data["tool_id"])}) + if result.deleted_count == 0: + return {"success": False, "message": "Tool not found"}, 404 + except Exception as err: + return {"success": False, "error": str(err)}, 400 + + return {"success": True}, 200 \ No newline at end of file From 3e2e1ecddfc5431c63105ea43d99cee071ba48f9 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 6 Dec 2024 23:11:16 +0000 Subject: [PATCH 06/66] fix: add status to tools --- application/api/user/routes.py | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/application/api/user/routes.py b/application/api/user/routes.py index 814490dd..627f5665 100644 --- a/application/api/user/routes.py +++ b/application/api/user/routes.py @@ -1798,6 +1798,8 @@ class CreateTool(Resource): "tool_name": fields.String(required=True, description="Name of the tool"), "tool_config": fields.Raw(required=True, description="Configuration of the tool"), "tool_actions": fields.List(required=True, description="Actions the tool can perform"), + "status": fields.Boolean(required=True, description="Status of the tool") + }, ) @@ -1805,7 +1807,7 @@ class CreateTool(Resource): @api.doc(description="Create a new tool") def post(self): data = request.get_json() - required_fields = ["tool_name", "tool_config", "tool_actions"] + required_fields = ["tool_name", "tool_config", "tool_actions", "status"] missing_fields = check_required_fields(data, required_fields) if missing_fields: return missing_fields @@ -1817,6 +1819,7 @@ class CreateTool(Resource): "tool_config": data["tool_config"], "tool_actions": data["tool_actions"], "user": user, + "status": data["status"], } resp = user_tools_collection.insert_one(new_tool) new_id = str(resp.inserted_id) @@ -1882,6 +1885,35 @@ class UpdateToolActions(Resource): return make_response(jsonify({"success": False, "error": str(err)}), 400) return make_response(jsonify({"success": True}), 200) + +@user_ns.route("/api/update_tool_status") +class UpdateToolStatus(Resource): + update_tool_status_model = api.model( + "UpdateToolStatusModel", + { + "tool_id": fields.String(required=True, description="Tool ID"), + "status": fields.Boolean(required=True, description="Status of the tool"), + }, + ) + + @api.expect(update_tool_status_model) + @api.doc(description="Update the status of a tool") + def post(self): + data = request.get_json() + required_fields = ["tool_id", "status"] + missing_fields = check_required_fields(data, required_fields) + if missing_fields: + return missing_fields + + try: + user_tools_collection.update_one( + {"_id": ObjectId(data["tool_id"])}, + {"$set": {"status": data["status"]}}, + ) + except Exception as err: + return make_response(jsonify({"success": False, "error": str(err)}), 400) + + return make_response(jsonify({"success": True}), 200) @user_ns.route("/api/delete_tool") class DeleteTool(Resource): From f87ae429f422aba53c793b107b955c7705b4cc07 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 9 Dec 2024 17:52:20 +0000 Subject: [PATCH 07/66] fix: edit names --- application/api/user/routes.py | 47 +++++++++++++++++----------------- application/tools/agent.py | 38 ++++++++++++++++++++++++--- 2 files changed, 58 insertions(+), 27 deletions(-) diff --git a/application/api/user/routes.py b/application/api/user/routes.py index 627f5665..efb0242d 100644 --- a/application/api/user/routes.py +++ b/application/api/user/routes.py @@ -1791,13 +1791,12 @@ class TextToSpeech(Resource): @user_ns.route("/api/create_tool") class CreateTool(Resource): - # write code such that it will accept tool_name, took_config and tool_actions create_tool_model = api.model( "CreateToolModel", { - "tool_name": fields.String(required=True, description="Name of the tool"), - "tool_config": fields.Raw(required=True, description="Configuration of the tool"), - "tool_actions": fields.List(required=True, description="Actions the tool can perform"), + "name": fields.String(required=True, description="Name of the tool"), + "config": fields.Raw(required=True, description="Configuration of the tool"), + "actions": fields.List(required=True, description="Actions the tool can perform"), "status": fields.Boolean(required=True, description="Status of the tool") }, @@ -1807,7 +1806,7 @@ class CreateTool(Resource): @api.doc(description="Create a new tool") def post(self): data = request.get_json() - required_fields = ["tool_name", "tool_config", "tool_actions", "status"] + required_fields = ["name", "config", "actions", "status"] missing_fields = check_required_fields(data, required_fields) if missing_fields: return missing_fields @@ -1815,9 +1814,9 @@ class CreateTool(Resource): user = "local" try: new_tool = { - "tool_name": data["tool_name"], - "tool_config": data["tool_config"], - "tool_actions": data["tool_actions"], + "name": data["name"], + "config": data["config"], + "actions": data["actions"], "user": user, "status": data["status"], } @@ -1833,8 +1832,8 @@ class UpdateToolConfig(Resource): update_tool_config_model = api.model( "UpdateToolConfigModel", { - "tool_id": fields.String(required=True, description="Tool ID"), - "tool_config": fields.Raw(required=True, description="Configuration of the tool"), + "id": fields.String(required=True, description="Tool ID"), + "config": fields.Raw(required=True, description="Configuration of the tool"), }, ) @@ -1842,15 +1841,15 @@ class UpdateToolConfig(Resource): @api.doc(description="Update the configuration of a tool") def post(self): data = request.get_json() - required_fields = ["tool_id", "tool_config"] + required_fields = ["id", "config"] missing_fields = check_required_fields(data, required_fields) if missing_fields: return missing_fields try: user_tools_collection.update_one( - {"_id": ObjectId(data["tool_id"])}, - {"$set": {"tool_config": data["tool_config"]}}, + {"_id": ObjectId(data["id"])}, + {"$set": {"config": data["config"]}}, ) except Exception as err: return make_response(jsonify({"success": False, "error": str(err)}), 400) @@ -1862,8 +1861,8 @@ class UpdateToolActions(Resource): update_tool_actions_model = api.model( "UpdateToolActionsModel", { - "tool_id": fields.String(required=True, description="Tool ID"), - "tool_actions": fields.List(required=True, description="Actions the tool can perform"), + "id": fields.String(required=True, description="Tool ID"), + "actions": fields.List(required=True, description="Actions the tool can perform"), }, ) @@ -1871,15 +1870,15 @@ class UpdateToolActions(Resource): @api.doc(description="Update the actions of a tool") def post(self): data = request.get_json() - required_fields = ["tool_id", "tool_actions"] + required_fields = ["id", "actions"] missing_fields = check_required_fields(data, required_fields) if missing_fields: return missing_fields try: user_tools_collection.update_one( - {"_id": ObjectId(data["tool_id"])}, - {"$set": {"tool_actions": data["tool_actions"]}}, + {"_id": ObjectId(data["id"])}, + {"$set": {"actions": data["actions"]}}, ) except Exception as err: return make_response(jsonify({"success": False, "error": str(err)}), 400) @@ -1891,7 +1890,7 @@ class UpdateToolStatus(Resource): update_tool_status_model = api.model( "UpdateToolStatusModel", { - "tool_id": fields.String(required=True, description="Tool ID"), + "id": fields.String(required=True, description="Tool ID"), "status": fields.Boolean(required=True, description="Status of the tool"), }, ) @@ -1900,14 +1899,14 @@ class UpdateToolStatus(Resource): @api.doc(description="Update the status of a tool") def post(self): data = request.get_json() - required_fields = ["tool_id", "status"] + required_fields = ["id", "status"] missing_fields = check_required_fields(data, required_fields) if missing_fields: return missing_fields try: user_tools_collection.update_one( - {"_id": ObjectId(data["tool_id"])}, + {"_id": ObjectId(data["id"])}, {"$set": {"status": data["status"]}}, ) except Exception as err: @@ -1919,20 +1918,20 @@ class UpdateToolStatus(Resource): class DeleteTool(Resource): delete_tool_model = api.model( "DeleteToolModel", - {"tool_id": fields.String(required=True, description="Tool ID")}, + {"id": fields.String(required=True, description="Tool ID")}, ) @api.expect(delete_tool_model) @api.doc(description="Delete a tool by ID") def post(self): data = request.get_json() - required_fields = ["tool_id"] + required_fields = ["id"] missing_fields = check_required_fields(data, required_fields) if missing_fields: return missing_fields try: - result = user_tools_collection.delete_one({"_id": ObjectId(data["tool_id"])}) + result = user_tools_collection.delete_one({"_id": ObjectId(data["id"])}) if result.deleted_count == 0: return {"success": False, "message": "Tool not found"}, 404 except Exception as err: diff --git a/application/tools/agent.py b/application/tools/agent.py index 2df14442..af23b99e 100644 --- a/application/tools/agent.py +++ b/application/tools/agent.py @@ -1,6 +1,7 @@ from application.llm.llm_creator import LLMCreator from application.core.settings import settings from application.tools.tool_manager import ToolManager +from application.core.mongo_db import MongoDB import json tool_tg = { @@ -53,9 +54,31 @@ class Agent: ] self.tool_config = { } + + def _get_user_tools(self, user="local"): + mongo = MongoDB.get_client() + db = mongo["docsgpt"] + user_tools_collection = db["user_tools"] + user_tools = user_tools_collection.find({"user": user, "status": True}) + user_tools = list(user_tools) + for tool in user_tools: + tool.pop("_id") + user_tools = {tool["name"]: tool for tool in user_tools} + return user_tools + + def _simple_tool_agent(self, messages): + tools_dict = self._get_user_tools() + # combine all tool_actions into one list + self.tools.extend([ + { + "type": "function", + "function": tool_action + } + for tool in tools_dict.values() + for tool_action in tool["actions"] + ]) + - def gen(self, messages): - # Generate initial response from the LLM resp = self.llm.gen(model=self.gpt_model, messages=messages, tools=self.tools) if isinstance(resp, str): @@ -75,7 +98,7 @@ class Agent: call_id = call.id # Determine the tool name and load it tool_name = call_name.split("_")[0] - tool = tm.load_tool(tool_name, tool_config=self.tool_config) + tool = tm.load_tool(tool_name, tool_config=tools_dict[tool_name]['config']) # Execute the tool's action resp_tool = tool.execute_action(call_name, **call_args) # Append the tool's response to the conversation @@ -96,3 +119,12 @@ class Agent: completion = self.llm.gen_stream(model=self.gpt_model, messages=messages, tools=self.tools) for line in completion: yield line + + def gen(self, messages): + # Generate initial response from the LLM + if self.llm.supports_tools(): + self._simple_tool_agent(messages) + else: + resp = self.llm.gen_stream(model=self.gpt_model, messages=messages) + for line in resp: + yield line \ No newline at end of file From 46b0de367a7a3ed12f14db99ba0b25c1117e54d2 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 12 Dec 2024 10:40:55 +0000 Subject: [PATCH 08/66] fix: strings --- application/api/user/routes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/api/user/routes.py b/application/api/user/routes.py index efb0242d..a5807b81 100644 --- a/application/api/user/routes.py +++ b/application/api/user/routes.py @@ -1796,7 +1796,7 @@ class CreateTool(Resource): { "name": fields.String(required=True, description="Name of the tool"), "config": fields.Raw(required=True, description="Configuration of the tool"), - "actions": fields.List(required=True, description="Actions the tool can perform"), + "actions": fields.List(fields.String, required=True, description="Actions the tool can perform"), "status": fields.Boolean(required=True, description="Status of the tool") }, @@ -1862,7 +1862,7 @@ class UpdateToolActions(Resource): "UpdateToolActionsModel", { "id": fields.String(required=True, description="Tool ID"), - "actions": fields.List(required=True, description="Actions the tool can perform"), + "actions": fields.List(fields.String, required=True, description="Actions the tool can perform"), }, ) From b473e13b8327fec06055a34500c80215c66b4f74 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2024 20:09:08 +0000 Subject: [PATCH 09/66] build(deps): bump sentence-transformers in /application Bumps [sentence-transformers](https://github.com/UKPLab/sentence-transformers) from 3.0.1 to 3.3.1. - [Release notes](https://github.com/UKPLab/sentence-transformers/releases) - [Commits](https://github.com/UKPLab/sentence-transformers/compare/v3.0.1...v3.3.1) --- updated-dependencies: - dependency-name: sentence-transformers dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- application/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/requirements.txt b/application/requirements.txt index 232b8508..de043d2d 100644 --- a/application/requirements.txt +++ b/application/requirements.txt @@ -73,7 +73,7 @@ referencing==0.30.2 regex==2024.9.11 requests==2.32.3 retry==0.9.2 -sentence-transformers==3.0.1 +sentence-transformers==3.3.1 tiktoken==0.7.0 tokenizers==0.21.0 torch==2.4.1 From 38753c4395484932362749fe4c14533bb3cc5629 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2024 20:09:34 +0000 Subject: [PATCH 10/66] build(deps): bump yarl from 1.11.1 to 1.18.3 in /application Bumps [yarl](https://github.com/aio-libs/yarl) from 1.11.1 to 1.18.3. - [Release notes](https://github.com/aio-libs/yarl/releases) - [Changelog](https://github.com/aio-libs/yarl/blob/master/CHANGES.rst) - [Commits](https://github.com/aio-libs/yarl/compare/v1.11.1...v1.18.3) --- updated-dependencies: - dependency-name: yarl dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- application/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/requirements.txt b/application/requirements.txt index 232b8508..13e9a30a 100644 --- a/application/requirements.txt +++ b/application/requirements.txt @@ -86,4 +86,4 @@ urllib3==2.2.3 vine==5.1.0 wcwidth==0.2.13 werkzeug==3.1.3 -yarl==1.11.1 \ No newline at end of file +yarl==1.18.3 \ No newline at end of file From f8e4e42a364ef05084abae67e1dcaf1fcb81658a Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Tue, 17 Dec 2024 16:14:17 +0530 Subject: [PATCH 11/66] (feat:limit conv history) add util method --- application/api/answer/routes.py | 5 ++--- application/utils.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/application/api/answer/routes.py b/application/api/answer/routes.py index bccffb66..6a4a0929 100644 --- a/application/api/answer/routes.py +++ b/application/api/answer/routes.py @@ -18,7 +18,7 @@ from application.error import bad_request from application.extensions import api from application.llm.llm_creator import LLMCreator from application.retriever.retriever_creator import RetrieverCreator -from application.utils import check_required_fields +from application.utils import check_required_fields, limit_chat_history logger = logging.getLogger(__name__) @@ -324,8 +324,7 @@ class Stream(Resource): try: question = data["question"] - history = str(data.get("history", [])) - history = str(json.loads(history)) + history = str(limit_chat_history(json.loads(data.get("history", [])))) conversation_id = data.get("conversation_id") prompt_id = data.get("prompt_id", "default") diff --git a/application/utils.py b/application/utils.py index 1fc9e329..8b5ddf2c 100644 --- a/application/utils.py +++ b/application/utils.py @@ -46,3 +46,17 @@ def check_required_fields(data, required_fields): def get_hash(data): return hashlib.md5(data.encode()).hexdigest() +def limit_chat_history(history,max_token_limit = 500): + + cumulative_token_count = 0 + trimmed_history = [] + + for i in reversed(history): + + if("prompt" in i and "response" in i): + cumulative_token_count += num_tokens_from_string(i["prompt"] + i["response"]) + if(cumulative_token_count > max_token_limit): + break + trimmed_history.insert(0,i) + + return trimmed_history \ No newline at end of file From 47ecf98e2a868ff24303dc226c80663238735b57 Mon Sep 17 00:00:00 2001 From: Niharika Goulikar Date: Wed, 18 Dec 2024 10:47:34 +0000 Subject: [PATCH 12/66] Resolved merge conflicts --- application/api/user/routes.py | 2 +- frontend/src/conversation/Conversation.tsx | 19 ++++++++++++++++--- .../src/conversation/ConversationBubble.tsx | 12 ++++++++++-- .../src/conversation/conversationHandlers.ts | 8 ++++---- .../src/conversation/conversationModels.ts | 2 +- 5 files changed, 32 insertions(+), 11 deletions(-) diff --git a/application/api/user/routes.py b/application/api/user/routes.py index 6eb57cc0..961e375b 100644 --- a/application/api/user/routes.py +++ b/application/api/user/routes.py @@ -1791,4 +1791,4 @@ class TextToSpeech(Resource): 200, ) except Exception as err: - return make_response(jsonify({"success": False, "error": str(err)}), 400) + return make_response(jsonify({"success": False, "error": str(err)}), 400) \ No newline at end of file diff --git a/frontend/src/conversation/Conversation.tsx b/frontend/src/conversation/Conversation.tsx index 41bc8d2f..1c3a296f 100644 --- a/frontend/src/conversation/Conversation.tsx +++ b/frontend/src/conversation/Conversation.tsx @@ -15,7 +15,6 @@ import { useDarkTheme, useMediaQuery } from '../hooks'; import { ShareConversationModal } from '../modals/ShareConversationModal'; import { selectConversationId } from '../preferences/preferenceSlice'; import { AppDispatch } from '../store'; -import conversationService from '../api/services/conversationService'; import ConversationBubble from './ConversationBubble'; import { handleSendFeedback } from './conversationHandlers'; import { FEEDBACK, Query } from './conversationModels'; @@ -111,8 +110,22 @@ export default function Conversation() { const handleFeedback = (query: Query, feedback: FEEDBACK, index: number) => { const prevFeedback = query.feedback; dispatch(updateQuery({ index, query: { feedback } })); - handleSendFeedback(query.prompt, query.response!, feedback,conversationId as string,index).catch(() => - dispatch(updateQuery({ index, query: { feedback: prevFeedback } })), + handleSendFeedback( + query.prompt, + query.response!, + feedback, + conversationId as string, + index, + ).catch(() => + handleSendFeedback( + query.prompt, + query.response!, + feedback, + conversationId as string, + index, + ).catch(() => + dispatch(updateQuery({ index, query: { feedback: prevFeedback } })), + ), ); }; diff --git a/frontend/src/conversation/ConversationBubble.tsx b/frontend/src/conversation/ConversationBubble.tsx index 03a3ab08..2d6fca1a 100644 --- a/frontend/src/conversation/ConversationBubble.tsx +++ b/frontend/src/conversation/ConversationBubble.tsx @@ -448,10 +448,14 @@ const ConversationBubble = forwardRef< : 'fill-none stroke-gray-4000' }`} onClick={() => { - if (feedback === undefined) { + if (feedback === undefined || feedback === null) { handleFeedback?.('LIKE'); setIsLikeClicked(true); setIsDislikeClicked(false); + } else if (feedback === 'LIKE') { + handleFeedback?.(null); + setIsLikeClicked(false); + setIsDislikeClicked(false); } }} onMouseEnter={() => setIsLikeHovered(true)} @@ -484,10 +488,14 @@ const ConversationBubble = forwardRef< : 'fill-none stroke-gray-4000' }`} onClick={() => { - if (feedback === undefined) { + if (feedback === undefined || feedback === null) { handleFeedback?.('DISLIKE'); setIsDislikeClicked(true); setIsLikeClicked(false); + } else if (feedback === 'DISLIKE') { + handleFeedback?.(null); + setIsLikeClicked(false); + setIsDislikeClicked(false); } }} onMouseEnter={() => setIsDislikeHovered(true)} diff --git a/frontend/src/conversation/conversationHandlers.ts b/frontend/src/conversation/conversationHandlers.ts index ea8ad6ea..1abdb74d 100644 --- a/frontend/src/conversation/conversationHandlers.ts +++ b/frontend/src/conversation/conversationHandlers.ts @@ -202,16 +202,16 @@ export function handleSendFeedback( prompt: string, response: string, feedback: FEEDBACK, - conversation_id:string, - prompt_index:number + conversation_id: string, + prompt_index: number, ) { return conversationService .feedback({ question: prompt, answer: response, feedback: feedback, - conversation_id:conversation_id, - question_index:prompt_index + conversation_id: conversation_id, + question_index: prompt_index, }) .then((response) => { if (response.ok) { diff --git a/frontend/src/conversation/conversationModels.ts b/frontend/src/conversation/conversationModels.ts index cd286852..a499deec 100644 --- a/frontend/src/conversation/conversationModels.ts +++ b/frontend/src/conversation/conversationModels.ts @@ -1,6 +1,6 @@ export type MESSAGE_TYPE = 'QUESTION' | 'ANSWER' | 'ERROR'; export type Status = 'idle' | 'loading' | 'failed'; -export type FEEDBACK = 'LIKE' | 'DISLIKE'; +export type FEEDBACK = 'LIKE' | 'DISLIKE' | null; export interface Message { text: string; From 1448d7e6ebb844593724c8fa83b617fe8c6a866d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 Dec 2024 12:02:03 +0000 Subject: [PATCH 13/66] build(deps): bump next from 14.2.12 to 14.2.20 in /docs Bumps [next](https://github.com/vercel/next.js) from 14.2.12 to 14.2.20. - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/compare/v14.2.12...v14.2.20) --- updated-dependencies: - dependency-name: next dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- docs/package-lock.json | 88 +++++++++++++++++++++--------------------- docs/package.json | 2 +- 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index 4994aa79..2e9c7f7d 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -8,7 +8,7 @@ "dependencies": { "@vercel/analytics": "^1.1.1", "docsgpt-react": "^0.4.8", - "next": "^14.2.12", + "next": "^14.2.20", "nextra": "^2.13.2", "nextra-theme-docs": "^2.13.2", "react": "^18.2.0", @@ -931,14 +931,14 @@ } }, "node_modules/@next/env": { - "version": "14.2.12", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.12.tgz", - "integrity": "sha512-3fP29GIetdwVIfIRyLKM7KrvJaqepv+6pVodEbx0P5CaMLYBtx+7eEg8JYO5L9sveJO87z9eCReceZLi0hxO1Q==" + "version": "14.2.20", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.20.tgz", + "integrity": "sha512-JfDpuOCB0UBKlEgEy/H6qcBSzHimn/YWjUHzKl1jMeUO+QVRdzmTTl8gFJaNO87c8DXmVKhFCtwxQ9acqB3+Pw==" }, "node_modules/@next/swc-darwin-arm64": { - "version": "14.2.12", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.12.tgz", - "integrity": "sha512-crHJ9UoinXeFbHYNok6VZqjKnd8rTd7K3Z2zpyzF1ch7vVNKmhjv/V7EHxep3ILoN8JB9AdRn/EtVVyG9AkCXw==", + "version": "14.2.20", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.20.tgz", + "integrity": "sha512-WDfq7bmROa5cIlk6ZNonNdVhKmbCv38XteVFYsxea1vDJt3SnYGgxLGMTXQNfs5OkFvAhmfKKrwe7Y0Hs+rWOg==", "cpu": [ "arm64" ], @@ -951,9 +951,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "14.2.12", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.12.tgz", - "integrity": "sha512-JbEaGbWq18BuNBO+lCtKfxl563Uw9oy2TodnN2ioX00u7V1uzrsSUcg3Ep9ce+P0Z9es+JmsvL2/rLphz+Frcw==", + "version": "14.2.20", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.20.tgz", + "integrity": "sha512-XIQlC+NAmJPfa2hruLvr1H1QJJeqOTDV+v7tl/jIdoFvqhoihvSNykLU/G6NMgoeo+e/H7p/VeWSOvMUHKtTIg==", "cpu": [ "x64" ], @@ -966,9 +966,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.2.12", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.12.tgz", - "integrity": "sha512-qBy7OiXOqZrdp88QEl2H4fWalMGnSCrr1agT/AVDndlyw2YJQA89f3ttR/AkEIP9EkBXXeGl6cC72/EZT5r6rw==", + "version": "14.2.20", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.20.tgz", + "integrity": "sha512-pnzBrHTPXIMm5QX3QC8XeMkpVuoAYOmyfsO4VlPn+0NrHraNuWjdhe+3xLq01xR++iCvX+uoeZmJDKcOxI201Q==", "cpu": [ "arm64" ], @@ -981,9 +981,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.2.12", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.12.tgz", - "integrity": "sha512-EfD9L7o9biaQxjwP1uWXnk3vYZi64NVcKUN83hpVkKocB7ogJfyH2r7o1pPnMtir6gHZiGCeHKagJ0yrNSLNHw==", + "version": "14.2.20", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.20.tgz", + "integrity": "sha512-WhJJAFpi6yqmUx1momewSdcm/iRXFQS0HU2qlUGlGE/+98eu7JWLD5AAaP/tkK1mudS/rH2f9E3WCEF2iYDydQ==", "cpu": [ "arm64" ], @@ -996,9 +996,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.2.12", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.12.tgz", - "integrity": "sha512-iQ+n2pxklJew9IpE47hE/VgjmljlHqtcD5UhZVeHICTPbLyrgPehaKf2wLRNjYH75udroBNCgrSSVSVpAbNoYw==", + "version": "14.2.20", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.20.tgz", + "integrity": "sha512-ao5HCbw9+iG1Kxm8XsGa3X174Ahn17mSYBQlY6VGsdsYDAbz/ZP13wSLfvlYoIDn1Ger6uYA+yt/3Y9KTIupRg==", "cpu": [ "x64" ], @@ -1011,9 +1011,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "14.2.12", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.12.tgz", - "integrity": "sha512-rFkUkNwcQ0ODn7cxvcVdpHlcOpYxMeyMfkJuzaT74xjAa5v4fxP4xDk5OoYmPi8QNLDs3UgZPMSBmpBuv9zKWA==", + "version": "14.2.20", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.20.tgz", + "integrity": "sha512-CXm/kpnltKTT7945np6Td3w7shj/92TMRPyI/VvveFe8+YE+/YOJ5hyAWK5rpx711XO1jBCgXl211TWaxOtkaA==", "cpu": [ "x64" ], @@ -1026,9 +1026,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.2.12", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.12.tgz", - "integrity": "sha512-PQFYUvwtHs/u0K85SG4sAdDXYIPXpETf9mcEjWc0R4JmjgMKSDwIU/qfZdavtP6MPNiMjuKGXHCtyhR/M5zo8g==", + "version": "14.2.20", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.20.tgz", + "integrity": "sha512-upJn2HGQgKNDbXVfIgmqT2BN8f3z/mX8ddoyi1I565FHbfowVK5pnMEwauvLvaJf4iijvuKq3kw/b6E9oIVRWA==", "cpu": [ "arm64" ], @@ -1041,9 +1041,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.2.12", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.12.tgz", - "integrity": "sha512-FAj2hMlcbeCV546eU2tEv41dcJb4NeqFlSXU/xL/0ehXywHnNpaYajOUvn3P8wru5WyQe6cTZ8fvckj/2XN4Vw==", + "version": "14.2.20", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.20.tgz", + "integrity": "sha512-igQW/JWciTGJwj3G1ipalD2V20Xfx3ywQy17IV0ciOUBbFhNfyU1DILWsTi32c8KmqgIDviUEulW/yPb2FF90w==", "cpu": [ "ia32" ], @@ -1056,9 +1056,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.2.12", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.12.tgz", - "integrity": "sha512-yu8QvV53sBzoIVRHsxCHqeuS8jYq6Lrmdh0briivuh+Brsp6xjg80MAozUsBTAV9KNmY08KlX0KYTWz1lbPzEg==", + "version": "14.2.20", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.20.tgz", + "integrity": "sha512-AFmqeLW6LtxeFTuoB+MXFeM5fm5052i3MU6xD0WzJDOwku6SkZaxb1bxjBaRC8uNqTRTSPl0yMFtjNowIVI67w==", "cpu": [ "x64" ], @@ -6759,11 +6759,11 @@ } }, "node_modules/next": { - "version": "14.2.12", - "resolved": "https://registry.npmjs.org/next/-/next-14.2.12.tgz", - "integrity": "sha512-cDOtUSIeoOvt1skKNihdExWMTybx3exnvbFbb9ecZDIxlvIbREQzt9A5Km3Zn3PfU+IFjyYGsHS+lN9VInAGKA==", + "version": "14.2.20", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.20.tgz", + "integrity": "sha512-yPvIiWsiyVYqJlSQxwmzMIReXn5HxFNq4+tlVQ812N1FbvhmE+fDpIAD7bcS2mGYQwPJ5vAsQouyme2eKsxaug==", "dependencies": { - "@next/env": "14.2.12", + "@next/env": "14.2.20", "@swc/helpers": "0.5.5", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", @@ -6778,15 +6778,15 @@ "node": ">=18.17.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "14.2.12", - "@next/swc-darwin-x64": "14.2.12", - "@next/swc-linux-arm64-gnu": "14.2.12", - "@next/swc-linux-arm64-musl": "14.2.12", - "@next/swc-linux-x64-gnu": "14.2.12", - "@next/swc-linux-x64-musl": "14.2.12", - "@next/swc-win32-arm64-msvc": "14.2.12", - "@next/swc-win32-ia32-msvc": "14.2.12", - "@next/swc-win32-x64-msvc": "14.2.12" + "@next/swc-darwin-arm64": "14.2.20", + "@next/swc-darwin-x64": "14.2.20", + "@next/swc-linux-arm64-gnu": "14.2.20", + "@next/swc-linux-arm64-musl": "14.2.20", + "@next/swc-linux-x64-gnu": "14.2.20", + "@next/swc-linux-x64-musl": "14.2.20", + "@next/swc-win32-arm64-msvc": "14.2.20", + "@next/swc-win32-ia32-msvc": "14.2.20", + "@next/swc-win32-x64-msvc": "14.2.20" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", diff --git a/docs/package.json b/docs/package.json index 96de9ea8..b9f53250 100644 --- a/docs/package.json +++ b/docs/package.json @@ -8,7 +8,7 @@ "dependencies": { "@vercel/analytics": "^1.1.1", "docsgpt-react": "^0.4.8", - "next": "^14.2.12", + "next": "^14.2.20", "nextra": "^2.13.2", "nextra-theme-docs": "^2.13.2", "react": "^18.2.0", From f9a7db11ebc89d63d7d7dad4cea062bfbe1567d0 Mon Sep 17 00:00:00 2001 From: Siddhant Rai Date: Wed, 18 Dec 2024 22:48:40 +0530 Subject: [PATCH 14/66] feat: tools frontend and endpoints refactor --- application/api/user/routes.py | 237 ++++++++++---- application/llm/groq.py | 39 +-- application/tools/agent.py | 84 +++-- application/tools/base.py | 1 + .../{ => implementations}/cryptoprice.py | 30 +- .../tools/{ => implementations}/telegram.py | 42 +-- application/tools/tool_manager.py | 18 +- .../public/toolIcons/tool_cryptoprice.svg | 1 + frontend/public/toolIcons/tool_telegram.svg | 10 + frontend/src/api/endpoints.ts | 5 + frontend/src/api/services/userService.ts | 10 + frontend/src/assets/cogwheel.svg | 3 + frontend/src/components/SettingsBar.tsx | 1 + frontend/src/index.css | 9 + frontend/src/locale/en.json | 3 + frontend/src/modals/AddToolModal.tsx | 136 ++++++++ frontend/src/modals/ConfigToolModal.tsx | 95 ++++++ frontend/src/modals/types/index.ts | 11 + frontend/src/settings/Documents.tsx | 18 +- frontend/src/settings/ToolConfig.tsx | 293 ++++++++++++++++++ frontend/src/settings/Tools.tsx | 157 ++++++++++ frontend/src/settings/index.tsx | 5 +- frontend/src/settings/types/index.ts | 29 ++ 23 files changed, 1069 insertions(+), 168 deletions(-) rename application/tools/{ => implementations}/cryptoprice.py (82%) rename application/tools/{ => implementations}/telegram.py (75%) create mode 100644 frontend/public/toolIcons/tool_cryptoprice.svg create mode 100644 frontend/public/toolIcons/tool_telegram.svg create mode 100644 frontend/src/assets/cogwheel.svg create mode 100644 frontend/src/modals/AddToolModal.tsx create mode 100644 frontend/src/modals/ConfigToolModal.tsx create mode 100644 frontend/src/modals/types/index.ts create mode 100644 frontend/src/settings/ToolConfig.tsx create mode 100644 frontend/src/settings/Tools.tsx diff --git a/application/api/user/routes.py b/application/api/user/routes.py index efb0242d..05248f97 100644 --- a/application/api/user/routes.py +++ b/application/api/user/routes.py @@ -1,14 +1,14 @@ import datetime +import math import os import shutil import uuid -import math from bson.binary import Binary, UuidRepresentation from bson.dbref import DBRef from bson.objectid import ObjectId -from flask import Blueprint, jsonify, make_response, request, redirect -from flask_restx import inputs, fields, Namespace, Resource +from flask import Blueprint, jsonify, make_response, redirect, request +from flask_restx import fields, inputs, Namespace, Resource from werkzeug.utils import secure_filename from application.api.user.tasks import ingest, ingest_remote @@ -16,9 +16,10 @@ from application.api.user.tasks import ingest, ingest_remote from application.core.mongo_db import MongoDB from application.core.settings import settings from application.extensions import api +from application.tools.tool_manager import ToolManager +from application.tts.google_tts import GoogleTTS from application.utils import check_required_fields from application.vectorstore.vector_creator import VectorCreator -from application.tts.google_tts import GoogleTTS mongo = MongoDB.get_client() db = mongo["docsgpt"] @@ -40,6 +41,9 @@ current_dir = os.path.dirname( os.path.dirname(os.path.dirname(os.path.abspath(__file__))) ) +tool_config = {} +tool_manager = ToolManager(config=tool_config) + def generate_minute_range(start_date, end_date): return { @@ -1789,24 +1793,88 @@ class TextToSpeech(Resource): return make_response(jsonify({"success": False, "error": str(err)}), 400) +@user_ns.route("/api/available_tools") +class AvailableTools(Resource): + @api.doc(description="Get available tools for a user") + def get(self): + try: + tools_metadata = [] + for tool_name, tool_instance in tool_manager.tools.items(): + doc = tool_instance.__doc__.strip() + lines = doc.split("\n", 1) + name = lines[0].strip() + description = lines[1].strip() if len(lines) > 1 else "" + tools_metadata.append( + { + "name": tool_name, + "displayName": name, + "description": description, + "configRequirements": tool_instance.get_config_requirements(), + "actions": tool_instance.get_actions_metadata(), + } + ) + except Exception as err: + return make_response(jsonify({"success": False, "error": str(err)}), 400) + + return make_response(jsonify({"success": True, "data": tools_metadata}), 200) + + +@user_ns.route("/api/get_tools") +class GetTools(Resource): + @api.doc(description="Get tools created by a user") + def get(self): + try: + user = "local" + tools = user_tools_collection.find({"user": user}) + user_tools = [] + for tool in tools: + tool["id"] = str(tool["_id"]) + tool.pop("_id") + user_tools.append(tool) + except Exception as err: + return make_response(jsonify({"success": False, "error": str(err)}), 400) + + return make_response(jsonify({"success": True, "tools": user_tools}), 200) + + @user_ns.route("/api/create_tool") class CreateTool(Resource): - create_tool_model = api.model( - "CreateToolModel", - { - "name": fields.String(required=True, description="Name of the tool"), - "config": fields.Raw(required=True, description="Configuration of the tool"), - "actions": fields.List(required=True, description="Actions the tool can perform"), - "status": fields.Boolean(required=True, description="Status of the tool") - - }, + @api.expect( + api.model( + "CreateToolModel", + { + "name": fields.String(required=True, description="Name of the tool"), + "displayName": fields.String( + required=True, description="Display name for the tool" + ), + "description": fields.String( + required=True, description="Tool description" + ), + "config": fields.Raw( + required=True, description="Configuration of the tool" + ), + "actions": fields.List( + fields.Raw, + required=True, + description="Actions the tool can perform", + ), + "status": fields.Boolean( + required=True, description="Status of the tool" + ), + }, + ) ) - - @api.expect(create_tool_model) @api.doc(description="Create a new tool") def post(self): data = request.get_json() - required_fields = ["name", "config", "actions", "status"] + required_fields = [ + "name", + "displayName", + "description", + "actions", + "config", + "status", + ] missing_fields = check_required_fields(data, required_fields) if missing_fields: return missing_fields @@ -1814,10 +1882,12 @@ class CreateTool(Resource): user = "local" try: new_tool = { - "name": data["name"], - "config": data["config"], - "actions": data["actions"], "user": user, + "name": data["name"], + "displayName": data["displayName"], + "description": data["description"], + "actions": data["actions"], + "config": data["config"], "status": data["status"], } resp = user_tools_collection.insert_one(new_tool) @@ -1826,18 +1896,72 @@ class CreateTool(Resource): return make_response(jsonify({"success": False, "error": str(err)}), 400) return make_response(jsonify({"id": new_id}), 200) - + + +@user_ns.route("/api/update_tool") +class UpdateTool(Resource): + @api.expect( + api.model( + "UpdateToolModel", + { + "id": fields.String(required=True, description="Tool ID"), + "name": fields.String(description="Name of the tool"), + "displayName": fields.String(description="Display name for the tool"), + "description": fields.String(description="Tool description"), + "config": fields.Raw(description="Configuration of the tool"), + "actions": fields.List( + fields.Raw, description="Actions the tool can perform" + ), + "status": fields.Boolean(description="Status of the tool"), + }, + ) + ) + @api.doc(description="Update a tool by ID") + def post(self): + data = request.get_json() + required_fields = ["id"] + missing_fields = check_required_fields(data, required_fields) + if missing_fields: + return missing_fields + + try: + update_data = {} + if "name" in data: + update_data["name"] = data["name"] + if "displayName" in data: + update_data["displayName"] = data["displayName"] + if "description" in data: + update_data["description"] = data["description"] + if "actions" in data: + update_data["actions"] = data["actions"] + if "config" in data: + update_data["config"] = data["config"] + if "status" in data: + update_data["status"] = data["status"] + + user_tools_collection.update_one( + {"_id": ObjectId(data["id"]), "user": "local"}, + {"$set": update_data}, + ) + except Exception as err: + return make_response(jsonify({"success": False, "error": str(err)}), 400) + + return make_response(jsonify({"success": True}), 200) + + @user_ns.route("/api/update_tool_config") class UpdateToolConfig(Resource): - update_tool_config_model = api.model( - "UpdateToolConfigModel", - { - "id": fields.String(required=True, description="Tool ID"), - "config": fields.Raw(required=True, description="Configuration of the tool"), - }, + @api.expect( + api.model( + "UpdateToolConfigModel", + { + "id": fields.String(required=True, description="Tool ID"), + "config": fields.Raw( + required=True, description="Configuration of the tool" + ), + }, + ) ) - - @api.expect(update_tool_config_model) @api.doc(description="Update the configuration of a tool") def post(self): data = request.get_json() @@ -1855,18 +1979,23 @@ class UpdateToolConfig(Resource): return make_response(jsonify({"success": False, "error": str(err)}), 400) return make_response(jsonify({"success": True}), 200) - + + @user_ns.route("/api/update_tool_actions") class UpdateToolActions(Resource): - update_tool_actions_model = api.model( - "UpdateToolActionsModel", - { - "id": fields.String(required=True, description="Tool ID"), - "actions": fields.List(required=True, description="Actions the tool can perform"), - }, + @api.expect( + api.model( + "UpdateToolActionsModel", + { + "id": fields.String(required=True, description="Tool ID"), + "actions": fields.List( + fields.Raw, + required=True, + description="Actions the tool can perform", + ), + }, + ) ) - - @api.expect(update_tool_actions_model) @api.doc(description="Update the actions of a tool") def post(self): data = request.get_json() @@ -1885,17 +2014,20 @@ class UpdateToolActions(Resource): return make_response(jsonify({"success": True}), 200) + @user_ns.route("/api/update_tool_status") class UpdateToolStatus(Resource): - update_tool_status_model = api.model( - "UpdateToolStatusModel", - { - "id": fields.String(required=True, description="Tool ID"), - "status": fields.Boolean(required=True, description="Status of the tool"), - }, + @api.expect( + api.model( + "UpdateToolStatusModel", + { + "id": fields.String(required=True, description="Tool ID"), + "status": fields.Boolean( + required=True, description="Status of the tool" + ), + }, + ) ) - - @api.expect(update_tool_status_model) @api.doc(description="Update the status of a tool") def post(self): data = request.get_json() @@ -1913,15 +2045,16 @@ class UpdateToolStatus(Resource): return make_response(jsonify({"success": False, "error": str(err)}), 400) return make_response(jsonify({"success": True}), 200) - + + @user_ns.route("/api/delete_tool") class DeleteTool(Resource): - delete_tool_model = api.model( - "DeleteToolModel", - {"id": fields.String(required=True, description="Tool ID")}, + @api.expect( + api.model( + "DeleteToolModel", + {"id": fields.String(required=True, description="Tool ID")}, + ) ) - - @api.expect(delete_tool_model) @api.doc(description="Delete a tool by ID") def post(self): data = request.get_json() @@ -1937,4 +2070,4 @@ class DeleteTool(Resource): except Exception as err: return {"success": False, "error": str(err)}, 400 - return {"success": True}, 200 \ No newline at end of file + return {"success": True}, 200 diff --git a/application/llm/groq.py b/application/llm/groq.py index b5731a90..f2fcfbeb 100644 --- a/application/llm/groq.py +++ b/application/llm/groq.py @@ -1,45 +1,32 @@ from application.llm.base import BaseLLM - +from openai import OpenAI class GroqLLM(BaseLLM): - def __init__(self, api_key=None, user_api_key=None, *args, **kwargs): - from openai import OpenAI - super().__init__(*args, **kwargs) self.client = OpenAI(api_key=api_key, base_url="https://api.groq.com/openai/v1") self.api_key = api_key self.user_api_key = user_api_key - def _raw_gen( - self, - baseself, - model, - messages, - stream=False, - **kwargs - ): - response = self.client.chat.completions.create( - model=model, messages=messages, stream=stream, **kwargs - ) - + def _raw_gen(self, baseself, model, messages, stream=False, tools=None, **kwargs): + if tools: + response = self.client.chat.completions.create( + model=model, messages=messages, stream=stream, tools=tools, **kwargs + ) + return response.choices[0] + else: + response = self.client.chat.completions.create( + model=model, messages=messages, stream=stream, **kwargs + ) return response.choices[0].message.content def _raw_gen_stream( - self, - baseself, - model, - messages, - stream=True, - **kwargs - ): + self, baseself, model, messages, stream=True, tools=None, **kwargs + ): response = self.client.chat.completions.create( model=model, messages=messages, stream=stream, **kwargs ) - for line in response: - # import sys - # print(line.choices[0].delta.content, file=sys.stderr) if line.choices[0].delta.content is not None: yield line.choices[0].delta.content diff --git a/application/tools/agent.py b/application/tools/agent.py index af23b99e..ffd14770 100644 --- a/application/tools/agent.py +++ b/application/tools/agent.py @@ -1,9 +1,10 @@ -from application.llm.llm_creator import LLMCreator -from application.core.settings import settings -from application.tools.tool_manager import ToolManager -from application.core.mongo_db import MongoDB import json +from application.core.mongo_db import MongoDB +from application.core.settings import settings +from application.llm.llm_creator import LLMCreator +from application.tools.tool_manager import ToolManager + tool_tg = { "name": "telegram_send_message", "description": "Send a notification to telegram about current chat", @@ -12,15 +13,15 @@ tool_tg = { "properties": { "text": { "type": "string", - "description": "Text to send in the notification" + "description": "Text to send in the notification", } }, "required": ["text"], - "additionalProperties": False - } + "additionalProperties": False, + }, } -tool_crypto = { +tool_crypto = { "name": "cryptoprice_get", "description": "Retrieve the price of a specified cryptocurrency in a given currency", "parameters": { @@ -28,33 +29,30 @@ tool_crypto = { "properties": { "symbol": { "type": "string", - "description": "The cryptocurrency symbol (e.g. BTC)" + "description": "The cryptocurrency symbol (e.g. BTC)", }, "currency": { "type": "string", - "description": "The currency in which you want the price (e.g. USD)" - } + "description": "The currency in which you want the price (e.g. USD)", + }, }, "required": ["symbol", "currency"], - "additionalProperties": False - } + "additionalProperties": False, + }, } + class Agent: def __init__(self, llm_name, gpt_model, api_key, user_api_key=None): # Initialize the LLM with the provided parameters - self.llm = LLMCreator.create_llm(llm_name, api_key=api_key, user_api_key=user_api_key) + self.llm = LLMCreator.create_llm( + llm_name, api_key=api_key, user_api_key=user_api_key + ) self.gpt_model = gpt_model # Static tool configuration (to be replaced later) - self.tools = [ - { - "type": "function", - "function": tool_crypto - } - ] - self.tool_config = { - } - + self.tools = [{"type": "function", "function": tool_crypto}] + self.tool_config = {} + def _get_user_tools(self, user="local"): mongo = MongoDB.get_client() db = mongo["docsgpt"] @@ -65,19 +63,17 @@ class Agent: tool.pop("_id") user_tools = {tool["name"]: tool for tool in user_tools} return user_tools - + def _simple_tool_agent(self, messages): tools_dict = self._get_user_tools() # combine all tool_actions into one list - self.tools.extend([ - { - "type": "function", - "function": tool_action - } - for tool in tools_dict.values() - for tool_action in tool["actions"] - ]) - + self.tools.extend( + [ + {"type": "function", "function": tool_action} + for tool in tools_dict.values() + for tool_action in tool["actions"] + ] + ) resp = self.llm.gen(model=self.gpt_model, messages=messages, tools=self.tools) @@ -88,7 +84,7 @@ class Agent: while resp.finish_reason == "tool_calls": # Append the assistant's message to the conversation - messages.append(json.loads(resp.model_dump_json())['message']) + messages.append(json.loads(resp.model_dump_json())["message"]) # Handle each tool call tool_calls = resp.message.tool_calls for call in tool_calls: @@ -98,25 +94,27 @@ class Agent: call_id = call.id # Determine the tool name and load it tool_name = call_name.split("_")[0] - tool = tm.load_tool(tool_name, tool_config=tools_dict[tool_name]['config']) + tool = tm.load_tool( + tool_name, tool_config=tools_dict[tool_name]["config"] + ) # Execute the tool's action resp_tool = tool.execute_action(call_name, **call_args) # Append the tool's response to the conversation messages.append( - { - "role": "tool", - "content": str(resp_tool), - "tool_call_id": call_id - } + {"role": "tool", "content": str(resp_tool), "tool_call_id": call_id} ) # Generate a new response from the LLM after processing tools - resp = self.llm.gen(model=self.gpt_model, messages=messages, tools=self.tools) + resp = self.llm.gen( + model=self.gpt_model, messages=messages, tools=self.tools + ) # If no tool calls are needed, generate the final response if isinstance(resp, str): yield resp else: - completion = self.llm.gen_stream(model=self.gpt_model, messages=messages, tools=self.tools) + completion = self.llm.gen_stream( + model=self.gpt_model, messages=messages, tools=self.tools + ) for line in completion: yield line @@ -127,4 +125,4 @@ class Agent: else: resp = self.llm.gen_stream(model=self.gpt_model, messages=messages) for line in resp: - yield line \ No newline at end of file + yield line diff --git a/application/tools/base.py b/application/tools/base.py index 00cfee3a..fd7b4a85 100644 --- a/application/tools/base.py +++ b/application/tools/base.py @@ -1,5 +1,6 @@ from abc import ABC, abstractmethod + class Tool(ABC): @abstractmethod def execute_action(self, action_name: str, **kwargs): diff --git a/application/tools/cryptoprice.py b/application/tools/implementations/cryptoprice.py similarity index 82% rename from application/tools/cryptoprice.py rename to application/tools/implementations/cryptoprice.py index d7cf61e1..7b88c866 100644 --- a/application/tools/cryptoprice.py +++ b/application/tools/implementations/cryptoprice.py @@ -1,21 +1,25 @@ -from application.tools.base import Tool import requests +from application.tools.base import Tool + class CryptoPriceTool(Tool): + """ + CryptoPrice + A tool for retrieving cryptocurrency prices using the CryptoCompare public API + """ + def __init__(self, config): self.config = config def execute_action(self, action_name, **kwargs): - actions = { - "cryptoprice_get": self.get_price - } + actions = {"cryptoprice_get": self._get_price} if action_name in actions: return actions[action_name](**kwargs) else: raise ValueError(f"Unknown action: {action_name}") - def get_price(self, symbol, currency): + def _get_price(self, symbol, currency): """ Fetches the current price of a given cryptocurrency symbol in the specified currency. Example: @@ -32,17 +36,17 @@ class CryptoPriceTool(Tool): return { "status_code": response.status_code, "price": data[currency.upper()], - "message": f"Price of {symbol.upper()} in {currency.upper()} retrieved successfully." + "message": f"Price of {symbol.upper()} in {currency.upper()} retrieved successfully.", } else: return { "status_code": response.status_code, - "message": f"Couldn't find price for {symbol.upper()} in {currency.upper()}." + "message": f"Couldn't find price for {symbol.upper()} in {currency.upper()}.", } else: return { "status_code": response.status_code, - "message": "Failed to retrieve price." + "message": "Failed to retrieve price.", } def get_actions_metadata(self): @@ -55,16 +59,16 @@ class CryptoPriceTool(Tool): "properties": { "symbol": { "type": "string", - "description": "The cryptocurrency symbol (e.g. BTC)" + "description": "The cryptocurrency symbol (e.g. BTC)", }, "currency": { "type": "string", - "description": "The currency in which you want the price (e.g. USD)" - } + "description": "The currency in which you want the price (e.g. USD)", + }, }, "required": ["symbol", "currency"], - "additionalProperties": False - } + "additionalProperties": False, + }, } ] diff --git a/application/tools/telegram.py b/application/tools/implementations/telegram.py similarity index 75% rename from application/tools/telegram.py rename to application/tools/implementations/telegram.py index 8210d8e7..a2b436b4 100644 --- a/application/tools/telegram.py +++ b/application/tools/implementations/telegram.py @@ -1,16 +1,23 @@ -from application.tools.base import Tool import requests +from application.tools.base import Tool + class TelegramTool(Tool): + """ + Telegram Bot + A flexible Telegram tool for performing various actions (e.g., sending messages, images). + Requires a bot token and chat ID for configuration + """ + def __init__(self, config): self.config = config - self.chat_id = config.get("chat_id", "142189016") - self.token = config.get("token", "YOUR_TG_TOKEN") + self.token = config.get("token", "") + self.chat_id = config.get("chat_id", "") def execute_action(self, action_name, **kwargs): actions = { - "telegram_send_message": self.send_message, - "telegram_send_image": self.send_image + "telegram_send_message": self._send_message, + "telegram_send_image": self._send_image, } if action_name in actions: @@ -18,14 +25,14 @@ class TelegramTool(Tool): else: raise ValueError(f"Unknown action: {action_name}") - def send_message(self, text): + def _send_message(self, text): print(f"Sending message: {text}") url = f"https://api.telegram.org/bot{self.token}/sendMessage" payload = {"chat_id": self.chat_id, "text": text} response = requests.post(url, data=payload) return {"status_code": response.status_code, "message": "Message sent"} - def send_image(self, image_url): + def _send_image(self, image_url): print(f"Sending image: {image_url}") url = f"https://api.telegram.org/bot{self.token}/sendPhoto" payload = {"chat_id": self.chat_id, "photo": image_url} @@ -42,12 +49,12 @@ class TelegramTool(Tool): "properties": { "text": { "type": "string", - "description": "Text to send in the notification" + "description": "Text to send in the notification", } }, "required": ["text"], - "additionalProperties": False - } + "additionalProperties": False, + }, }, { "name": "telegram_send_image", @@ -57,23 +64,20 @@ class TelegramTool(Tool): "properties": { "image_url": { "type": "string", - "description": "URL of the image to send" + "description": "URL of the image to send", } }, "required": ["image_url"], - "additionalProperties": False - } - } + "additionalProperties": False, + }, + }, ] def get_config_requirements(self): return { "chat_id": { "type": "string", - "description": "Telegram chat ID to send messages to" + "description": "Telegram chat ID to send messages to", }, - "token": { - "type": "string", - "description": "Bot token for authentication" - } + "token": {"type": "string", "description": "Bot token for authentication"}, } diff --git a/application/tools/tool_manager.py b/application/tools/tool_manager.py index 10231cb2..cc9a055a 100644 --- a/application/tools/tool_manager.py +++ b/application/tools/tool_manager.py @@ -1,10 +1,11 @@ import importlib import inspect -import pkgutil import os +import pkgutil from application.tools.base import Tool + class ToolManager: def __init__(self, config): self.config = config @@ -12,11 +13,13 @@ class ToolManager: self.load_tools() def load_tools(self): - tools_dir = os.path.dirname(__file__) + tools_dir = os.path.join(os.path.dirname(__file__), "implementations") for finder, name, ispkg in pkgutil.iter_modules([tools_dir]): - if name == 'base' or name.startswith('__'): + if name == "base" or name.startswith("__"): continue - module = importlib.import_module(f'application.tools.{name}') + module = importlib.import_module( + f"application.tools.implementations.{name}" + ) for member_name, obj in inspect.getmembers(module, inspect.isclass): if issubclass(obj, Tool) and obj is not Tool: tool_config = self.config.get(name, {}) @@ -24,13 +27,14 @@ class ToolManager: def load_tool(self, tool_name, tool_config): self.config[tool_name] = tool_config - tools_dir = os.path.dirname(__file__) - module = importlib.import_module(f'application.tools.{tool_name}') + tools_dir = os.path.join(os.path.dirname(__file__), "implementations") + module = importlib.import_module( + f"application.tools.implementations.{tool_name}" + ) for member_name, obj in inspect.getmembers(module, inspect.isclass): if issubclass(obj, Tool) and obj is not Tool: return obj(tool_config) - def execute_action(self, tool_name, action_name, **kwargs): if tool_name not in self.tools: raise ValueError(f"Tool '{tool_name}' not loaded") diff --git a/frontend/public/toolIcons/tool_cryptoprice.svg b/frontend/public/toolIcons/tool_cryptoprice.svg new file mode 100644 index 00000000..6a422694 --- /dev/null +++ b/frontend/public/toolIcons/tool_cryptoprice.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/toolIcons/tool_telegram.svg b/frontend/public/toolIcons/tool_telegram.svg new file mode 100644 index 00000000..27536ded --- /dev/null +++ b/frontend/public/toolIcons/tool_telegram.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/api/endpoints.ts b/frontend/src/api/endpoints.ts index 4e7112d0..8a7f9ae2 100644 --- a/frontend/src/api/endpoints.ts +++ b/frontend/src/api/endpoints.ts @@ -18,6 +18,11 @@ const endpoints = { FEEDBACK_ANALYTICS: '/api/get_feedback_analytics', LOGS: `/api/get_user_logs`, MANAGE_SYNC: '/api/manage_sync', + GET_AVAILABLE_TOOLS: '/api/available_tools', + GET_USER_TOOLS: '/api/get_tools', + CREATE_TOOL: '/api/create_tool', + UPDATE_TOOL_STATUS: '/api/update_tool_status', + UPDATE_TOOL: '/api/update_tool', }, CONVERSATION: { ANSWER: '/api/answer', diff --git a/frontend/src/api/services/userService.ts b/frontend/src/api/services/userService.ts index 942318ae..ab91a0a4 100644 --- a/frontend/src/api/services/userService.ts +++ b/frontend/src/api/services/userService.ts @@ -35,6 +35,16 @@ const userService = { apiClient.post(endpoints.USER.LOGS, data), manageSync: (data: any): Promise => apiClient.post(endpoints.USER.MANAGE_SYNC, data), + getAvailableTools: (): Promise => + apiClient.get(endpoints.USER.GET_AVAILABLE_TOOLS), + getUserTools: (): Promise => + apiClient.get(endpoints.USER.GET_USER_TOOLS), + createTool: (data: any): Promise => + apiClient.post(endpoints.USER.CREATE_TOOL, data), + updateToolStatus: (data: any): Promise => + apiClient.post(endpoints.USER.UPDATE_TOOL_STATUS, data), + updateTool: (data: any): Promise => + apiClient.post(endpoints.USER.UPDATE_TOOL, data), }; export default userService; diff --git a/frontend/src/assets/cogwheel.svg b/frontend/src/assets/cogwheel.svg new file mode 100644 index 00000000..f5299b8b --- /dev/null +++ b/frontend/src/assets/cogwheel.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/components/SettingsBar.tsx b/frontend/src/components/SettingsBar.tsx index f617c6e8..c6970600 100644 --- a/frontend/src/components/SettingsBar.tsx +++ b/frontend/src/components/SettingsBar.tsx @@ -13,6 +13,7 @@ const useTabs = () => { t('settings.apiKeys.label'), t('settings.analytics.label'), t('settings.logs.label'), + t('settings.tools.label'), ]; return tabs; }; diff --git a/frontend/src/index.css b/frontend/src/index.css index 9bd2ec96..ecd4da1e 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -68,6 +68,15 @@ body.dark { .table-default td:last-child { @apply border-r-0; /* Ensure no right border on the last column */ } + + .table-default th, + .table-default td { + min-width: 150px; + max-width: 320px; + overflow: auto; + scrollbar-width: thin; + scrollbar-color: grey transparent; + } } /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ diff --git a/frontend/src/locale/en.json b/frontend/src/locale/en.json index 13460463..4048f632 100644 --- a/frontend/src/locale/en.json +++ b/frontend/src/locale/en.json @@ -73,6 +73,9 @@ }, "logs": { "label": "Logs" + }, + "tools": { + "label": "Tools" } }, "modals": { diff --git a/frontend/src/modals/AddToolModal.tsx b/frontend/src/modals/AddToolModal.tsx new file mode 100644 index 00000000..330cf3bb --- /dev/null +++ b/frontend/src/modals/AddToolModal.tsx @@ -0,0 +1,136 @@ +import React from 'react'; + +import userService from '../api/services/userService'; +import Exit from '../assets/exit.svg'; +import { ActiveState } from '../models/misc'; +import { AvailableTool } from './types'; +import ConfigToolModal from './ConfigToolModal'; + +export default function AddToolModal({ + message, + modalState, + setModalState, + getUserTools, +}: { + message: string; + modalState: ActiveState; + setModalState: (state: ActiveState) => void; + getUserTools: () => void; +}) { + const [availableTools, setAvailableTools] = React.useState( + [], + ); + const [selectedTool, setSelectedTool] = React.useState( + null, + ); + const [configModalState, setConfigModalState] = + React.useState('INACTIVE'); + + const getAvailableTools = () => { + userService + .getAvailableTools() + .then((res) => { + return res.json(); + }) + .then((data) => { + setAvailableTools(data.data); + }); + }; + + const handleAddTool = (tool: AvailableTool) => { + if (Object.keys(tool.configRequirements).length === 0) { + userService + .createTool({ + name: tool.name, + displayName: tool.displayName, + description: tool.description, + config: {}, + actions: tool.actions, + status: true, + }) + .then((res) => { + if (res.status === 200) { + getUserTools(); + setModalState('INACTIVE'); + } + }); + } else { + setModalState('INACTIVE'); + setConfigModalState('ACTIVE'); + } + }; + + React.useEffect(() => { + if (modalState === 'ACTIVE') getAvailableTools(); + }, [modalState]); + return ( + <> +
+
+
+ +
+

+ Select a tool to set up +

+
+ {availableTools.map((tool, index) => ( +
{ + setSelectedTool(tool); + handleAddTool(tool); + }} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + setSelectedTool(tool); + handleAddTool(tool); + } + }} + > +
+
+ +
+
+

+ {tool.displayName} +

+

+ {tool.description} +

+
+
+
+ ))} +
+
+
+
+
+ + + ); +} diff --git a/frontend/src/modals/ConfigToolModal.tsx b/frontend/src/modals/ConfigToolModal.tsx new file mode 100644 index 00000000..96bb15be --- /dev/null +++ b/frontend/src/modals/ConfigToolModal.tsx @@ -0,0 +1,95 @@ +import React from 'react'; + +import Exit from '../assets/exit.svg'; +import Input from '../components/Input'; +import { ActiveState } from '../models/misc'; +import { AvailableTool } from './types'; +import userService from '../api/services/userService'; + +export default function ConfigToolModal({ + modalState, + setModalState, + tool, + getUserTools, +}: { + modalState: ActiveState; + setModalState: (state: ActiveState) => void; + tool: AvailableTool | null; + getUserTools: () => void; +}) { + const [authKey, setAuthKey] = React.useState(''); + + const handleAddTool = (tool: AvailableTool) => { + userService + .createTool({ + name: tool.name, + displayName: tool.displayName, + description: tool.description, + config: { token: authKey }, + actions: tool.actions, + status: true, + }) + .then(() => { + setModalState('INACTIVE'); + getUserTools(); + }); + }; + return ( +
+
+
+ +
+

+ Tool Config +

+

+ Type: {tool?.name} +

+
+ + API Key / Oauth + + setAuthKey(e.target.value)} + borderVariant="thin" + placeholder="Enter API Key / Oauth" + > +
+
+ + +
+
+
+
+
+ ); +} diff --git a/frontend/src/modals/types/index.ts b/frontend/src/modals/types/index.ts new file mode 100644 index 00000000..044ba438 --- /dev/null +++ b/frontend/src/modals/types/index.ts @@ -0,0 +1,11 @@ +export type AvailableTool = { + name: string; + displayName: string; + description: string; + configRequirements: object; + actions: { + name: string; + description: string; + parameters: object; + }[]; +}; diff --git a/frontend/src/settings/Documents.tsx b/frontend/src/settings/Documents.tsx index f91a3355..6375b4e2 100644 --- a/frontend/src/settings/Documents.tsx +++ b/frontend/src/settings/Documents.tsx @@ -215,18 +215,22 @@ const Documents: React.FC = ({ {document.type === 'remote' ? 'Pre-loaded' : 'Private'} -
+
{document.type !== 'remote' && ( - Delete { event.stopPropagation(); handleDeleteDocument(index, document); }} - /> + > + Delete + )} {document.syncFrequency && (
diff --git a/frontend/src/settings/ToolConfig.tsx b/frontend/src/settings/ToolConfig.tsx new file mode 100644 index 00000000..0de4dab9 --- /dev/null +++ b/frontend/src/settings/ToolConfig.tsx @@ -0,0 +1,293 @@ +import React from 'react'; + +import userService from '../api/services/userService'; +import ArrowLeft from '../assets/arrow-left.svg'; +import Input from '../components/Input'; +import { UserTool } from './types'; + +export default function ToolConfig({ + tool, + setTool, + handleGoBack, +}: { + tool: UserTool; + setTool: (tool: UserTool) => void; + handleGoBack: () => void; +}) { + const [authKey, setAuthKey] = React.useState( + tool.config?.token || '', + ); + + const handleCheckboxChange = (actionIndex: number, property: string) => { + setTool({ + ...tool, + actions: tool.actions.map((action, index) => { + if (index === actionIndex) { + return { + ...action, + parameters: { + ...action.parameters, + properties: { + ...action.parameters.properties, + [property]: { + ...action.parameters.properties[property], + filled_by_llm: + !action.parameters.properties[property].filled_by_llm, + }, + }, + }, + }; + } + return action; + }), + }); + }; + + const handleSaveChanges = () => { + userService + .updateTool({ + id: tool.id, + name: tool.name, + displayName: tool.displayName, + description: tool.description, + config: { token: authKey }, + actions: tool.actions, + status: tool.status, + }) + .then(() => { + handleGoBack(); + }); + }; + return ( +
+
+ +

Back to all tools

+
+
+

+ Type +

+

+ {tool.name} +

+
+
+ {Object.keys(tool?.config).length !== 0 && ( +

+ Authentication +

+ )} +
+ {Object.keys(tool?.config).length !== 0 && ( +
+ + API Key / Oauth + + setAuthKey(e.target.value)} + borderVariant="thin" + placeholder="Enter API Key / Oauth" + > +
+ )} + +
+
+
+
+

+ Actions +

+
+ {tool.actions.map((action, actionIndex) => { + return ( +
+
+

+ {action.name} +

+ +
+
+ { + setTool({ + ...tool, + actions: tool.actions.map((act, index) => { + if (index === actionIndex) { + return { + ...act, + description: e.target.value, + }; + } + return act; + }), + }); + }} + borderVariant="thin" + > +
+
+ + + + + + + + + + + + {Object.entries(action.parameters?.properties).map( + (param, index) => { + const uniqueKey = `${actionIndex}-${param[0]}`; + return ( + + + + + + + + ); + }, + )} + +
Field NameField TypeFilled by LLMFIeld descriptionValue
{param[0]}{param[1].type} + + + { + setTool({ + ...tool, + actions: tool.actions.map( + (act, index) => { + if (index === actionIndex) { + return { + ...act, + parameters: { + ...act.parameters, + properties: { + ...act.parameters.properties, + [param[0]]: { + ...act.parameters + .properties[param[0]], + description: e.target.value, + }, + }, + }, + }; + } + return act; + }, + ), + }); + }} + > + + { + setTool({ + ...tool, + actions: tool.actions.map( + (act, index) => { + if (index === actionIndex) { + return { + ...act, + parameters: { + ...act.parameters, + properties: { + ...act.parameters.properties, + [param[0]]: { + ...act.parameters + .properties[param[0]], + value: e.target.value, + }, + }, + }, + }; + } + return act; + }, + ), + }); + }} + > +
+
+
+ ); + })} +
+
+
+ ); +} diff --git a/frontend/src/settings/Tools.tsx b/frontend/src/settings/Tools.tsx new file mode 100644 index 00000000..d29ba42c --- /dev/null +++ b/frontend/src/settings/Tools.tsx @@ -0,0 +1,157 @@ +import React from 'react'; + +import userService from '../api/services/userService'; +import CogwheelIcon from '../assets/cogwheel.svg'; +import Input from '../components/Input'; +import AddToolModal from '../modals/AddToolModal'; +import { ActiveState } from '../models/misc'; +import { UserTool } from './types'; +import ToolConfig from './ToolConfig'; + +export default function Tools() { + const [searchTerm, setSearchTerm] = React.useState(''); + const [addToolModalState, setAddToolModalState] = + React.useState('INACTIVE'); + const [userTools, setUserTools] = React.useState([]); + const [selectedTool, setSelectedTool] = React.useState(null); + + const getUserTools = () => { + userService + .getUserTools() + .then((res) => { + return res.json(); + }) + .then((data) => { + setUserTools(data.tools); + }); + }; + + const updateToolStatus = (toolId: string, newStatus: boolean) => { + userService + .updateToolStatus({ id: toolId, status: newStatus }) + .then(() => { + setUserTools((prevTools) => + prevTools.map((tool) => + tool.id === toolId ? { ...tool, status: newStatus } : tool, + ), + ); + }) + .catch((error) => { + console.error('Failed to update tool status:', error); + }); + }; + + const handleSettingsClick = (tool: UserTool) => { + setSelectedTool(tool); + }; + + const handleGoBack = () => { + setSelectedTool(null); + getUserTools(); + }; + + React.useEffect(() => { + getUserTools(); + }, []); + return ( +
+ {selectedTool ? ( + + ) : ( +
+
+
+
+ setSearchTerm(e.target.value)} + /> +
+ +
+
+ {userTools + .filter((tool) => + tool.displayName + .toLowerCase() + .includes(searchTerm.toLowerCase()), + ) + .map((tool, index) => ( +
+
+
+ + +
+
+

+ {tool.displayName} +

+

+ {tool.description} +

+
+
+
+ +
+
+ ))} +
+
+ +
+ )} +
+ ); +} diff --git a/frontend/src/settings/index.tsx b/frontend/src/settings/index.tsx index 15c7ce08..89f29a29 100644 --- a/frontend/src/settings/index.tsx +++ b/frontend/src/settings/index.tsx @@ -7,8 +7,8 @@ import SettingsBar from '../components/SettingsBar'; import i18n from '../locale/i18n'; import { Doc } from '../models/misc'; import { - selectSourceDocs, selectPaginatedDocuments, + selectSourceDocs, setPaginatedDocuments, setSourceDocs, } from '../preferences/preferenceSlice'; @@ -17,6 +17,7 @@ import APIKeys from './APIKeys'; import Documents from './Documents'; import General from './General'; import Logs from './Logs'; +import Tools from './Tools'; import Widgets from './Widgets'; export default function Settings() { @@ -100,6 +101,8 @@ export default function Settings() { return ; case t('settings.logs.label'): return ; + case t('settings.tools.label'): + return ; default: return null; } diff --git a/frontend/src/settings/types/index.ts b/frontend/src/settings/types/index.ts index 52a58f23..322bdfeb 100644 --- a/frontend/src/settings/types/index.ts +++ b/frontend/src/settings/types/index.ts @@ -18,3 +18,32 @@ export type LogData = { retriever_params: Record; timestamp: string; }; + +export type UserTool = { + id: string; + name: string; + displayName: string; + description: string; + status: boolean; + config: { + [key: string]: string; + }; + actions: { + name: string; + description: string; + parameters: { + properties: { + [key: string]: { + type: string; + description: string; + filled_by_llm: boolean; + value: string; + }; + }; + additionalProperties: boolean; + required: string[]; + type: string; + }; + active: boolean; + }[]; +}; From 89a2f249c156adba56ef47a529a96d4332eb3805 Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Thu, 19 Dec 2024 05:15:33 +0530 Subject: [PATCH 15/66] (feat:conv history) token limit from settings --- application/utils.py | 44 ++++++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/application/utils.py b/application/utils.py index 8b5ddf2c..a96e0c9a 100644 --- a/application/utils.py +++ b/application/utils.py @@ -46,17 +46,37 @@ def check_required_fields(data, required_fields): def get_hash(data): return hashlib.md5(data.encode()).hexdigest() -def limit_chat_history(history,max_token_limit = 500): - - cumulative_token_count = 0 +def limit_chat_history(history, max_token_limit=None, gpt_model="docsgpt"): + """ + Limits chat history based on token count. + Returns a list of messages that fit within the token limit. + """ + from application.core.settings import settings + + max_token_limit = ( + max_token_limit + if max_token_limit + and max_token_limit < settings.MODEL_TOKEN_LIMITS.get( + gpt_model, settings.DEFAULT_MAX_HISTORY + ) + else settings.MODEL_TOKEN_LIMITS.get(gpt_model, settings.DEFAULT_MAX_HISTORY) + ) + + if not history: + return [] + + tokens_current_history = 0 trimmed_history = [] - for i in reversed(history): - - if("prompt" in i and "response" in i): - cumulative_token_count += num_tokens_from_string(i["prompt"] + i["response"]) - if(cumulative_token_count > max_token_limit): - break - trimmed_history.insert(0,i) - - return trimmed_history \ No newline at end of file + for message in reversed(history): + if "prompt" in message and "response" in message: + tokens_batch = num_tokens_from_string(message["prompt"]) + num_tokens_from_string( + message["response"] + ) + if tokens_current_history + tokens_batch < max_token_limit: + tokens_current_history += tokens_batch + trimmed_history.insert(0, message) + else: + break + + return trimmed_history From 9096013e13797f455581b5386ff6017831de158c Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Thu, 19 Dec 2024 05:20:55 +0530 Subject: [PATCH 16/66] (refactor) remove preprocessing in retrieval --- application/api/answer/routes.py | 2 +- application/retriever/brave_search.py | 9 --------- application/retriever/classic_rag.py | 9 --------- application/retriever/duckduck_search.py | 11 +---------- frontend/package-lock.json | 2 +- 5 files changed, 3 insertions(+), 30 deletions(-) diff --git a/application/api/answer/routes.py b/application/api/answer/routes.py index 6a4a0929..aba2b88e 100644 --- a/application/api/answer/routes.py +++ b/application/api/answer/routes.py @@ -455,7 +455,7 @@ class Answer(Resource): try: question = data["question"] - history = data.get("history", []) + history = str(limit_chat_history(json.loads(data.get("history", [])))) conversation_id = data.get("conversation_id") prompt_id = data.get("prompt_id", "default") chunks = int(data.get("chunks", 2)) diff --git a/application/retriever/brave_search.py b/application/retriever/brave_search.py index 1fd844b2..4601d352 100644 --- a/application/retriever/brave_search.py +++ b/application/retriever/brave_search.py @@ -2,7 +2,6 @@ import json from application.retriever.base import BaseRetriever from application.core.settings import settings from application.llm.llm_creator import LLMCreator -from application.utils import num_tokens_from_string from langchain_community.tools import BraveSearch @@ -73,15 +72,7 @@ class BraveRetSearch(BaseRetriever): yield {"source": doc} if len(self.chat_history) > 1: - tokens_current_history = 0 - # count tokens in history for i in self.chat_history: - if "prompt" in i and "response" in i: - tokens_batch = num_tokens_from_string(i["prompt"]) + num_tokens_from_string( - i["response"] - ) - if tokens_current_history + tokens_batch < self.token_limit: - tokens_current_history += tokens_batch messages_combine.append( {"role": "user", "content": i["prompt"]} ) diff --git a/application/retriever/classic_rag.py b/application/retriever/classic_rag.py index 42e318d2..75b2b576 100644 --- a/application/retriever/classic_rag.py +++ b/application/retriever/classic_rag.py @@ -3,7 +3,6 @@ from application.core.settings import settings from application.vectorstore.vector_creator import VectorCreator from application.llm.llm_creator import LLMCreator -from application.utils import num_tokens_from_string class ClassicRAG(BaseRetriever): @@ -73,15 +72,7 @@ class ClassicRAG(BaseRetriever): yield {"source": doc} if len(self.chat_history) > 1: - tokens_current_history = 0 - # count tokens in history for i in self.chat_history: - if "prompt" in i and "response" in i: - tokens_batch = num_tokens_from_string(i["prompt"]) + num_tokens_from_string( - i["response"] - ) - if tokens_current_history + tokens_batch < self.token_limit: - tokens_current_history += tokens_batch messages_combine.append( {"role": "user", "content": i["prompt"]} ) diff --git a/application/retriever/duckduck_search.py b/application/retriever/duckduck_search.py index 6ae56226..80717e7d 100644 --- a/application/retriever/duckduck_search.py +++ b/application/retriever/duckduck_search.py @@ -1,7 +1,6 @@ from application.retriever.base import BaseRetriever from application.core.settings import settings from application.llm.llm_creator import LLMCreator -from application.utils import num_tokens_from_string from langchain_community.tools import DuckDuckGoSearchResults from langchain_community.utilities import DuckDuckGoSearchAPIWrapper @@ -89,16 +88,8 @@ class DuckDuckSearch(BaseRetriever): for doc in docs: yield {"source": doc} - if len(self.chat_history) > 1: - tokens_current_history = 0 - # count tokens in history + if len(self.chat_history) > 1: for i in self.chat_history: - if "prompt" in i and "response" in i: - tokens_batch = num_tokens_from_string(i["prompt"]) + num_tokens_from_string( - i["response"] - ) - if tokens_current_history + tokens_batch < self.token_limit: - tokens_current_history += tokens_batch messages_combine.append( {"role": "user", "content": i["prompt"]} ) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 7b6f11d6..f96a17d4 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1649,7 +1649,7 @@ "version": "18.3.0", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", - "devOptional": true, + "dev": true, "dependencies": { "@types/react": "*" } From 343569ba195f380c8b9e889dbffa8f44d4bdd86c Mon Sep 17 00:00:00 2001 From: Siddhant Rai Date: Thu, 19 Dec 2024 09:58:32 +0530 Subject: [PATCH 17/66] fix: create_tool endpoint for new fields --- application/api/user/routes.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/application/api/user/routes.py b/application/api/user/routes.py index 05248f97..bcbc2d95 100644 --- a/application/api/user/routes.py +++ b/application/api/user/routes.py @@ -1880,13 +1880,24 @@ class CreateTool(Resource): return missing_fields user = "local" + transformed_actions = [] + for action in data["actions"]: + action["active"] = True + if "parameters" in action: + if "properties" in action["parameters"]: + for param_name, param_details in action["parameters"][ + "properties" + ].items(): + param_details["filled_by_llm"] = True + param_details["value"] = "" + transformed_actions.append(action) try: new_tool = { "user": user, "name": data["name"], "displayName": data["displayName"], "description": data["description"], - "actions": data["actions"], + "actions": transformed_actions, "config": data["config"], "status": data["status"], } From c3f538c2f6d866d7be34a75abc32f49d2661910a Mon Sep 17 00:00:00 2001 From: Siddhant Rai Date: Thu, 19 Dec 2024 09:59:38 +0530 Subject: [PATCH 18/66] fix: merge errors --- application/api/user/routes.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/application/api/user/routes.py b/application/api/user/routes.py index 05256f28..bcbc2d95 100644 --- a/application/api/user/routes.py +++ b/application/api/user/routes.py @@ -1863,6 +1863,7 @@ class CreateTool(Resource): ), }, ) + ) @api.doc(description="Create a new tool") def post(self): data = request.get_json() @@ -2005,6 +2006,7 @@ class UpdateToolActions(Resource): ), }, ) + ) @api.doc(description="Update the actions of a tool") def post(self): data = request.get_json() From daa332aa20c23e66d25d0fd6e1c36204c66c5005 Mon Sep 17 00:00:00 2001 From: Siddhant Rai Date: Thu, 19 Dec 2024 10:06:06 +0530 Subject: [PATCH 19/66] fix: python lint errors --- application/cache.py | 30 ++++++++++++++++++---------- application/retriever/classic_rag.py | 24 ++++++++++++---------- application/tools/agent.py | 1 - application/tools/tool_manager.py | 1 - 4 files changed, 33 insertions(+), 23 deletions(-) diff --git a/application/cache.py b/application/cache.py index 7239abac..76b594c9 100644 --- a/application/cache.py +++ b/application/cache.py @@ -1,29 +1,34 @@ -import redis -import time import json import logging +import time from threading import Lock + +import redis + from application.core.settings import settings from application.utils import get_hash -import sys logger = logging.getLogger(__name__) _redis_instance = None _instance_lock = Lock() + def get_redis_instance(): global _redis_instance if _redis_instance is None: with _instance_lock: if _redis_instance is None: try: - _redis_instance = redis.Redis.from_url(settings.CACHE_REDIS_URL, socket_connect_timeout=2) + _redis_instance = redis.Redis.from_url( + settings.CACHE_REDIS_URL, socket_connect_timeout=2 + ) except redis.ConnectionError as e: logger.error(f"Redis connection error: {e}") _redis_instance = None return _redis_instance + def gen_cache_key(messages, model="docgpt", tools=None): if not all(isinstance(msg, dict) for msg in messages): raise ValueError("All messages must be dictionaries.") @@ -33,6 +38,7 @@ def gen_cache_key(messages, model="docgpt", tools=None): cache_key = get_hash(combined) return cache_key + def gen_cache(func): def wrapper(self, model, messages, stream, tools=None, *args, **kwargs): try: @@ -42,7 +48,7 @@ def gen_cache(func): try: cached_response = redis_client.get(cache_key) if cached_response: - return cached_response.decode('utf-8') + return cached_response.decode("utf-8") except redis.ConnectionError as e: logger.error(f"Redis connection error: {e}") @@ -57,20 +63,22 @@ def gen_cache(func): except ValueError as e: logger.error(e) return "Error: No user message found in the conversation to generate a cache key." + return wrapper + def stream_cache(func): def wrapper(self, model, messages, stream, *args, **kwargs): cache_key = gen_cache_key(messages) logger.info(f"Stream cache key: {cache_key}") - + redis_client = get_redis_instance() if redis_client: try: cached_response = redis_client.get(cache_key) if cached_response: logger.info(f"Cache hit for stream key: {cache_key}") - cached_response = json.loads(cached_response.decode('utf-8')) + cached_response = json.loads(cached_response.decode("utf-8")) for chunk in cached_response: yield chunk time.sleep(0.03) @@ -80,16 +88,16 @@ def stream_cache(func): result = func(self, model, messages, stream, *args, **kwargs) stream_cache_data = [] - + for chunk in result: stream_cache_data.append(chunk) yield chunk - + if redis_client: try: redis_client.set(cache_key, json.dumps(stream_cache_data), ex=1800) logger.info(f"Stream cache saved for key: {cache_key}") except redis.ConnectionError as e: logger.error(f"Redis connection error: {e}") - - return wrapper \ No newline at end of file + + return wrapper diff --git a/application/retriever/classic_rag.py b/application/retriever/classic_rag.py index 4ac52bc5..81a5985b 100644 --- a/application/retriever/classic_rag.py +++ b/application/retriever/classic_rag.py @@ -1,10 +1,9 @@ -from application.retriever.base import BaseRetriever from application.core.settings import settings -from application.vectorstore.vector_creator import VectorCreator -from application.llm.llm_creator import LLMCreator +from application.retriever.base import BaseRetriever from application.tools.agent import Agent from application.utils import num_tokens_from_string +from application.vectorstore.vector_creator import VectorCreator class ClassicRAG(BaseRetriever): @@ -21,7 +20,7 @@ class ClassicRAG(BaseRetriever): user_api_key=None, ): self.question = question - self.vectorstore = source['active_docs'] if 'active_docs' in source else None + self.vectorstore = source["active_docs"] if "active_docs" in source else None self.chat_history = chat_history self.prompt = prompt self.chunks = chunks @@ -78,9 +77,9 @@ class ClassicRAG(BaseRetriever): # count tokens in history for i in self.chat_history: if "prompt" in i and "response" in i: - tokens_batch = num_tokens_from_string(i["prompt"]) + num_tokens_from_string( - i["response"] - ) + tokens_batch = num_tokens_from_string( + i["prompt"] + ) + num_tokens_from_string(i["response"]) if tokens_current_history + tokens_batch < self.token_limit: tokens_current_history += tokens_batch messages_combine.append( @@ -95,14 +94,19 @@ class ClassicRAG(BaseRetriever): # settings.LLM_NAME, api_key=settings.API_KEY, user_api_key=self.user_api_key # ) # completion = llm.gen_stream(model=self.gpt_model, messages=messages_combine) - agent = Agent(llm_name=settings.LLM_NAME,gpt_model=self.gpt_model, api_key=settings.API_KEY, user_api_key=self.user_api_key) + agent = Agent( + llm_name=settings.LLM_NAME, + gpt_model=self.gpt_model, + api_key=settings.API_KEY, + user_api_key=self.user_api_key, + ) completion = agent.gen(messages_combine) for line in completion: yield {"answer": str(line)} def search(self): return self._get_data() - + def get_params(self): return { "question": self.question, @@ -112,5 +116,5 @@ class ClassicRAG(BaseRetriever): "chunks": self.chunks, "token_limit": self.token_limit, "gpt_model": self.gpt_model, - "user_api_key": self.user_api_key + "user_api_key": self.user_api_key, } diff --git a/application/tools/agent.py b/application/tools/agent.py index ffd14770..e02c40f7 100644 --- a/application/tools/agent.py +++ b/application/tools/agent.py @@ -1,7 +1,6 @@ import json from application.core.mongo_db import MongoDB -from application.core.settings import settings from application.llm.llm_creator import LLMCreator from application.tools.tool_manager import ToolManager diff --git a/application/tools/tool_manager.py b/application/tools/tool_manager.py index cc9a055a..3e0766cf 100644 --- a/application/tools/tool_manager.py +++ b/application/tools/tool_manager.py @@ -27,7 +27,6 @@ class ToolManager: def load_tool(self, tool_name, tool_config): self.config[tool_name] = tool_config - tools_dir = os.path.join(os.path.dirname(__file__), "implementations") module = importlib.import_module( f"application.tools.implementations.{tool_name}" ) From f67b79f00766df29dfda9faa8ae152c0b1f3e161 Mon Sep 17 00:00:00 2001 From: Siddhant Rai Date: Thu, 19 Dec 2024 17:55:58 +0530 Subject: [PATCH 20/66] fix: missing yield in tool agent --- application/tools/agent.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/application/tools/agent.py b/application/tools/agent.py index e02c40f7..ab797992 100644 --- a/application/tools/agent.py +++ b/application/tools/agent.py @@ -120,7 +120,9 @@ class Agent: def gen(self, messages): # Generate initial response from the LLM if self.llm.supports_tools(): - self._simple_tool_agent(messages) + resp = self._simple_tool_agent(messages) + for line in resp: + yield line else: resp = self.llm.gen_stream(model=self.gpt_model, messages=messages) for line in resp: From b19c14787e355520e83793e19adb4a36587e45c4 Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Thu, 19 Dec 2024 17:58:55 +0530 Subject: [PATCH 21/66] (fix) avoid stringifying list --- application/api/answer/routes.py | 4 ++-- application/retriever/brave_search.py | 1 + application/retriever/classic_rag.py | 3 ++- application/retriever/duckduck_search.py | 1 + application/utils.py | 17 ++++++++++------- 5 files changed, 16 insertions(+), 10 deletions(-) diff --git a/application/api/answer/routes.py b/application/api/answer/routes.py index aba2b88e..c55ffe72 100644 --- a/application/api/answer/routes.py +++ b/application/api/answer/routes.py @@ -324,7 +324,7 @@ class Stream(Resource): try: question = data["question"] - history = str(limit_chat_history(json.loads(data.get("history", [])))) + history = limit_chat_history(json.loads(data.get("history", [])), gpt_model=gpt_model) conversation_id = data.get("conversation_id") prompt_id = data.get("prompt_id", "default") @@ -455,7 +455,7 @@ class Answer(Resource): try: question = data["question"] - history = str(limit_chat_history(json.loads(data.get("history", [])))) + history = limit_chat_history(json.loads(data.get("history", [])), gpt_model=gpt_model) conversation_id = data.get("conversation_id") prompt_id = data.get("prompt_id", "default") chunks = int(data.get("chunks", 2)) diff --git a/application/retriever/brave_search.py b/application/retriever/brave_search.py index 4601d352..3d9ae89e 100644 --- a/application/retriever/brave_search.py +++ b/application/retriever/brave_search.py @@ -73,6 +73,7 @@ class BraveRetSearch(BaseRetriever): if len(self.chat_history) > 1: for i in self.chat_history: + if "prompt" in i and "response" in i: messages_combine.append( {"role": "user", "content": i["prompt"]} ) diff --git a/application/retriever/classic_rag.py b/application/retriever/classic_rag.py index 75b2b576..8de625dd 100644 --- a/application/retriever/classic_rag.py +++ b/application/retriever/classic_rag.py @@ -73,6 +73,7 @@ class ClassicRAG(BaseRetriever): if len(self.chat_history) > 1: for i in self.chat_history: + if "prompt" in i and "response" in i: messages_combine.append( {"role": "user", "content": i["prompt"]} ) @@ -80,7 +81,7 @@ class ClassicRAG(BaseRetriever): {"role": "system", "content": i["response"]} ) messages_combine.append({"role": "user", "content": self.question}) - + llm = LLMCreator.create_llm( settings.LLM_NAME, api_key=settings.API_KEY, user_api_key=self.user_api_key ) diff --git a/application/retriever/duckduck_search.py b/application/retriever/duckduck_search.py index 80717e7d..fa19ead0 100644 --- a/application/retriever/duckduck_search.py +++ b/application/retriever/duckduck_search.py @@ -90,6 +90,7 @@ class DuckDuckSearch(BaseRetriever): if len(self.chat_history) > 1: for i in self.chat_history: + if "prompt" in i and "response" in i: messages_combine.append( {"role": "user", "content": i["prompt"]} ) diff --git a/application/utils.py b/application/utils.py index a96e0c9a..7099a20a 100644 --- a/application/utils.py +++ b/application/utils.py @@ -54,13 +54,16 @@ def limit_chat_history(history, max_token_limit=None, gpt_model="docsgpt"): from application.core.settings import settings max_token_limit = ( - max_token_limit - if max_token_limit - and max_token_limit < settings.MODEL_TOKEN_LIMITS.get( - gpt_model, settings.DEFAULT_MAX_HISTORY + max_token_limit + if max_token_limit and + max_token_limit < settings.MODEL_TOKEN_LIMITS.get( + gpt_model, settings.DEFAULT_MAX_HISTORY + ) + else settings.MODEL_TOKEN_LIMITS.get( + gpt_model, settings.DEFAULT_MAX_HISTORY + ) ) - else settings.MODEL_TOKEN_LIMITS.get(gpt_model, settings.DEFAULT_MAX_HISTORY) - ) + if not history: return [] @@ -78,5 +81,5 @@ def limit_chat_history(history, max_token_limit=None, gpt_model="docsgpt"): trimmed_history.insert(0, message) else: break - + return trimmed_history From 4c3f990d4b86a962b56e5bd7bf8c6d03ecfb1cf3 Mon Sep 17 00:00:00 2001 From: Siddhant Rai Date: Thu, 19 Dec 2024 20:34:20 +0530 Subject: [PATCH 22/66] feat: tools agent refactor for custom fields and unique actions --- application/llm/groq.py | 2 +- application/tools/agent.py | 156 ++++++++++-------- application/tools/implementations/telegram.py | 27 +-- 3 files changed, 104 insertions(+), 81 deletions(-) diff --git a/application/llm/groq.py b/application/llm/groq.py index f2fcfbeb..282d7f47 100644 --- a/application/llm/groq.py +++ b/application/llm/groq.py @@ -19,7 +19,7 @@ class GroqLLM(BaseLLM): response = self.client.chat.completions.create( model=model, messages=messages, stream=stream, **kwargs ) - return response.choices[0].message.content + return response.choices[0].message.content def _raw_gen_stream( self, baseself, model, messages, stream=True, tools=None, **kwargs diff --git a/application/tools/agent.py b/application/tools/agent.py index ab797992..d4077e45 100644 --- a/application/tools/agent.py +++ b/application/tools/agent.py @@ -4,42 +4,6 @@ from application.core.mongo_db import MongoDB from application.llm.llm_creator import LLMCreator from application.tools.tool_manager import ToolManager -tool_tg = { - "name": "telegram_send_message", - "description": "Send a notification to telegram about current chat", - "parameters": { - "type": "object", - "properties": { - "text": { - "type": "string", - "description": "Text to send in the notification", - } - }, - "required": ["text"], - "additionalProperties": False, - }, -} - -tool_crypto = { - "name": "cryptoprice_get", - "description": "Retrieve the price of a specified cryptocurrency in a given currency", - "parameters": { - "type": "object", - "properties": { - "symbol": { - "type": "string", - "description": "The cryptocurrency symbol (e.g. BTC)", - }, - "currency": { - "type": "string", - "description": "The currency in which you want the price (e.g. USD)", - }, - }, - "required": ["symbol", "currency"], - "additionalProperties": False, - }, -} - class Agent: def __init__(self, llm_name, gpt_model, api_key, user_api_key=None): @@ -49,7 +13,7 @@ class Agent: ) self.gpt_model = gpt_model # Static tool configuration (to be replaced later) - self.tools = [{"type": "function", "function": tool_crypto}] + self.tools = [] self.tool_config = {} def _get_user_tools(self, user="local"): @@ -58,50 +22,102 @@ class Agent: user_tools_collection = db["user_tools"] user_tools = user_tools_collection.find({"user": user, "status": True}) user_tools = list(user_tools) - for tool in user_tools: - tool.pop("_id") - user_tools = {tool["name"]: tool for tool in user_tools} - return user_tools + tools_by_id = {str(tool["_id"]): tool for tool in user_tools} + return tools_by_id + + def _prepare_tools(self, tools_dict): + self.tools = [ + { + "type": "function", + "function": { + "name": f"{action['name']}_{tool_id}", + "description": action["description"], + "parameters": { + **action["parameters"], + "properties": { + k: { + key: value + for key, value in v.items() + if key != "filled_by_llm" and key != "value" + } + for k, v in action["parameters"]["properties"].items() + if v.get("filled_by_llm", False) + }, + "required": [ + key + for key in action["parameters"]["required"] + if key in action["parameters"]["properties"] + and action["parameters"]["properties"][key].get( + "filled_by_llm", False + ) + ], + }, + }, + } + for tool_id, tool in tools_dict.items() + for action in tool["actions"] + if action["active"] + ] + + def _execute_tool_action(self, tools_dict, call): + call_id = call.id + call_args = json.loads(call.function.arguments) + tool_id = call.function.name.split("_")[-1] + action_name = call.function.name.rsplit("_", 1)[0] + + tool_data = tools_dict[tool_id] + action_data = next( + action for action in tool_data["actions"] if action["name"] == action_name + ) + + for param, details in action_data["parameters"]["properties"].items(): + if param not in call_args and "value" in details: + call_args[param] = details["value"] + + tm = ToolManager(config={}) + tool = tm.load_tool(tool_data["name"], tool_config=tool_data["config"]) + print(f"Executing tool: {action_name} with args: {call_args}") + return tool.execute_action(action_name, **call_args), call_id def _simple_tool_agent(self, messages): tools_dict = self._get_user_tools() - # combine all tool_actions into one list - self.tools.extend( - [ - {"type": "function", "function": tool_action} - for tool in tools_dict.values() - for tool_action in tool["actions"] - ] - ) + self._prepare_tools(tools_dict) resp = self.llm.gen(model=self.gpt_model, messages=messages, tools=self.tools) if isinstance(resp, str): - # Yield the response if it's a string and exit yield resp return + if resp.message.content: + yield resp.message.content + return while resp.finish_reason == "tool_calls": - # Append the assistant's message to the conversation - messages.append(json.loads(resp.model_dump_json())["message"]) - # Handle each tool call + message = json.loads(resp.model_dump_json())["message"] + keys_to_remove = {"audio", "function_call", "refusal"} + filtered_data = { + k: v for k, v in message.items() if k not in keys_to_remove + } + messages.append(filtered_data) tool_calls = resp.message.tool_calls for call in tool_calls: - tm = ToolManager(config={}) - call_name = call.function.name - call_args = json.loads(call.function.arguments) - call_id = call.id - # Determine the tool name and load it - tool_name = call_name.split("_")[0] - tool = tm.load_tool( - tool_name, tool_config=tools_dict[tool_name]["config"] - ) - # Execute the tool's action - resp_tool = tool.execute_action(call_name, **call_args) - # Append the tool's response to the conversation - messages.append( - {"role": "tool", "content": str(resp_tool), "tool_call_id": call_id} - ) + try: + tool_response, call_id = self._execute_tool_action(tools_dict, call) + messages.append( + { + "role": "tool", + "content": str(tool_response), + "tool_call_id": call_id, + } + ) + except Exception as e: + messages.append( + { + "role": "tool", + "content": f"Error executing tool: {str(e)}", + "tool_call_id": call.id, + } + ) # Generate a new response from the LLM after processing tools resp = self.llm.gen( model=self.gpt_model, messages=messages, tools=self.tools @@ -110,6 +126,8 @@ class Agent: # If no tool calls are needed, generate the final response if isinstance(resp, str): yield resp + elif resp.message.content: + yield resp.message.content else: completion = self.llm.gen_stream( model=self.gpt_model, messages=messages, tools=self.tools @@ -117,6 +135,8 @@ class Agent: for line in completion: yield line + return + def gen(self, messages): # Generate initial response from the LLM if self.llm.supports_tools(): diff --git a/application/tools/implementations/telegram.py b/application/tools/implementations/telegram.py index a2b436b4..a32bbe88 100644 --- a/application/tools/implementations/telegram.py +++ b/application/tools/implementations/telegram.py @@ -12,7 +12,6 @@ class TelegramTool(Tool): def __init__(self, config): self.config = config self.token = config.get("token", "") - self.chat_id = config.get("chat_id", "") def execute_action(self, action_name, **kwargs): actions = { @@ -25,17 +24,17 @@ class TelegramTool(Tool): else: raise ValueError(f"Unknown action: {action_name}") - def _send_message(self, text): + def _send_message(self, text, chat_id): print(f"Sending message: {text}") url = f"https://api.telegram.org/bot{self.token}/sendMessage" - payload = {"chat_id": self.chat_id, "text": text} + payload = {"chat_id": chat_id, "text": text} response = requests.post(url, data=payload) return {"status_code": response.status_code, "message": "Message sent"} - def _send_image(self, image_url): + def _send_image(self, image_url, chat_id): print(f"Sending image: {image_url}") url = f"https://api.telegram.org/bot{self.token}/sendPhoto" - payload = {"chat_id": self.chat_id, "photo": image_url} + payload = {"chat_id": chat_id, "photo": image_url} response = requests.post(url, data=payload) return {"status_code": response.status_code, "message": "Image sent"} @@ -43,14 +42,18 @@ class TelegramTool(Tool): return [ { "name": "telegram_send_message", - "description": "Send a notification to telegram chat", + "description": "Send a notification to Telegram chat", "parameters": { "type": "object", "properties": { "text": { "type": "string", "description": "Text to send in the notification", - } + }, + "chat_id": { + "type": "string", + "description": "Chat ID to send the notification to", + }, }, "required": ["text"], "additionalProperties": False, @@ -65,7 +68,11 @@ class TelegramTool(Tool): "image_url": { "type": "string", "description": "URL of the image to send", - } + }, + "chat_id": { + "type": "string", + "description": "Chat ID to send the image to", + }, }, "required": ["image_url"], "additionalProperties": False, @@ -75,9 +82,5 @@ class TelegramTool(Tool): def get_config_requirements(self): return { - "chat_id": { - "type": "string", - "description": "Telegram chat ID to send messages to", - }, "token": {"type": "string", "description": "Bot token for authentication"}, } From 856419832146ecf9247298f9c14c66427d81d964 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 19 Dec 2024 16:02:27 +0000 Subject: [PATCH 23/66] mini-model as default --- application/api/answer/routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/api/answer/routes.py b/application/api/answer/routes.py index f109db26..94a17693 100644 --- a/application/api/answer/routes.py +++ b/application/api/answer/routes.py @@ -37,7 +37,7 @@ api.add_namespace(answer_ns) gpt_model = "" # to have some kind of default behaviour if settings.LLM_NAME == "openai": - gpt_model = "gpt-3.5-turbo" + gpt_model = "gpt-4o-mini" elif settings.LLM_NAME == "anthropic": gpt_model = "claude-2" elif settings.LLM_NAME == "groq": From 6fc4723d61b3f666649579b02b228bef13051bfb Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 19 Dec 2024 16:03:39 +0000 Subject: [PATCH 24/66] feat: flask debugger for vscode --- .vscode/launch.json | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index fc4b8128..5be1f711 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -11,6 +11,26 @@ "skipFiles": [ "/**" ] - } + }, + { + "name": "Python Debugger: Flask", + "type": "debugpy", + "request": "launch", + "module": "flask", + "env": { + "FLASK_APP": "application/app.py", + "PYTHONPATH": "${workspaceFolder}", + "FLASK_ENV": "development", + "FLASK_DEBUG": "1", + "FLASK_RUN_PORT": "7091", + "FLASK_RUN_HOST": "0.0.0.0" + + }, + "args": [ + "run", + "--no-debugger" + ], + "cwd": "${workspaceFolder}", + }, ] } \ No newline at end of file From 132326136aea3cbfc3f22de91805ea3e45563cd6 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 19 Dec 2024 18:17:12 +0000 Subject: [PATCH 25/66] added gpt-4o-mini model --- application/core/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/core/settings.py b/application/core/settings.py index a7811ec7..0bace432 100644 --- a/application/core/settings.py +++ b/application/core/settings.py @@ -16,7 +16,7 @@ class Settings(BaseSettings): MONGO_URI: str = "mongodb://localhost:27017/docsgpt" MODEL_PATH: str = os.path.join(current_dir, "models/docsgpt-7b-f16.gguf") DEFAULT_MAX_HISTORY: int = 150 - MODEL_TOKEN_LIMITS: dict = {"gpt-3.5-turbo": 4096, "claude-2": 1e5} + MODEL_TOKEN_LIMITS: dict = {"gpt-4o-mini": 128000, "gpt-3.5-turbo": 4096, "claude-2": 1e5} UPLOAD_FOLDER: str = "inputs" PARSE_PDF_AS_IMAGE: bool = False VECTOR_STORE: str = "faiss" # "faiss" or "elasticsearch" or "qdrant" or "milvus" or "lancedb" From 2aea24afddcf93dac0638a612e09781976352d10 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 20 Dec 2024 11:01:57 +0000 Subject: [PATCH 26/66] depriciate mock backend --- mock-backend/.gitignore | 5 - mock-backend/Dockerfile | 11 - mock-backend/package-lock.json | 1379 ---------------------------- mock-backend/package.json | 22 - mock-backend/src/mocks/db.json | 244 ----- mock-backend/src/mocks/routes.json | 7 - mock-backend/src/server.js | 131 --- 7 files changed, 1799 deletions(-) delete mode 100644 mock-backend/.gitignore delete mode 100644 mock-backend/Dockerfile delete mode 100644 mock-backend/package-lock.json delete mode 100644 mock-backend/package.json delete mode 100644 mock-backend/src/mocks/db.json delete mode 100644 mock-backend/src/mocks/routes.json delete mode 100644 mock-backend/src/server.js diff --git a/mock-backend/.gitignore b/mock-backend/.gitignore deleted file mode 100644 index bca646a7..00000000 --- a/mock-backend/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ - -# Elastic Beanstalk Files -.elasticbeanstalk/* -!.elasticbeanstalk/*.cfg.yml -!.elasticbeanstalk/*.global.yml diff --git a/mock-backend/Dockerfile b/mock-backend/Dockerfile deleted file mode 100644 index 588636a9..00000000 --- a/mock-backend/Dockerfile +++ /dev/null @@ -1,11 +0,0 @@ -FROM node:20.6.1-bullseye-slim - - -WORKDIR /app -COPY package*.json ./ -RUN npm install -COPY . . - -EXPOSE 8080 - -CMD [ "npm", "run", "start"] diff --git a/mock-backend/package-lock.json b/mock-backend/package-lock.json deleted file mode 100644 index 0671e4de..00000000 --- a/mock-backend/package-lock.json +++ /dev/null @@ -1,1379 +0,0 @@ -{ - "name": "mock-backend", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "mock-backend", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "cors": "^2.8.5", - "json-server": "^0.17.4", - "uuid": "^9.0.1" - }, - "devDependencies": { - "@types/json-server": "^0.14.5" - } - }, - "node_modules/@types/body-parser": { - "version": "1.19.3", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.3.tgz", - "integrity": "sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ==", - "dev": true, - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.36", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.36.tgz", - "integrity": "sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/express": { - "version": "4.17.18", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.18.tgz", - "integrity": "sha512-Sxv8BSLLgsBYmcnGdGjjEjqET2U+AKAdCRODmMiq02FgjwuV75Ut85DRpvFjyw/Mk0vgUOliGRU0UUmuuZHByQ==", - "dev": true, - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.17.37", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.37.tgz", - "integrity": "sha512-ZohaCYTgGFcOP7u6aJOhY9uIZQgZ2vxC2yWoArY+FeDXlqeH66ZVBjgvg+RLVAS/DWNq4Ap9ZXu1+SUQiiWYMg==", - "dev": true, - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/http-errors": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.2.tgz", - "integrity": "sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg==", - "dev": true - }, - "node_modules/@types/json-server": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/@types/json-server/-/json-server-0.14.5.tgz", - "integrity": "sha512-Eck8yX5a0PPPV5MhYg/1Xbklz0/BJ2ir874CReGiKsj22ZWD+XYP3ZXK6cTZ9Mqi099GmtIml/1X5aQJTcZr/Q==", - "dev": true, - "dependencies": { - "@types/connect": "*", - "@types/express": "*", - "@types/lowdb": "*" - } - }, - "node_modules/@types/lodash": { - "version": "4.14.199", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.199.tgz", - "integrity": "sha512-Vrjz5N5Ia4SEzWWgIVwnHNEnb1UE1XMkvY5DGXrAeOGE9imk0hgTHh5GyDjLDJi9OTCn9oo9dXH1uToK1VRfrg==", - "dev": true - }, - "node_modules/@types/lowdb": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@types/lowdb/-/lowdb-1.0.12.tgz", - "integrity": "sha512-m/hOfY7nuwo9V3yApvR6aJ3uZP6iNC74S7Bx5BWz0L7IrzjKyzUur/jEdlYWBWWVjmkCz+ECK9nk8UJoQa8aZw==", - "dev": true, - "dependencies": { - "@types/lodash": "*" - } - }, - "node_modules/@types/mime": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.3.tgz", - "integrity": "sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg==", - "dev": true - }, - "node_modules/@types/node": { - "version": "20.8.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.3.tgz", - "integrity": "sha512-jxiZQFpb+NlH5kjW49vXxvxTjeeqlbsnTAdBTKpzEdPs9itay7MscYXz3Fo9VYFEsfQ6LJFitHad3faerLAjCw==", - "dev": true - }, - "node_modules/@types/qs": { - "version": "6.9.8", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.8.tgz", - "integrity": "sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==", - "dev": true - }, - "node_modules/@types/range-parser": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.5.tgz", - "integrity": "sha512-xrO9OoVPqFuYyR/loIHjnbvvyRZREYKLjxV4+dY6v3FQR3stQ9ZxIGkaclF7YhI9hfjpuTbu14hZEy94qKLtOA==", - "dev": true - }, - "node_modules/@types/send": { - "version": "0.17.2", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.2.tgz", - "integrity": "sha512-aAG6yRf6r0wQ29bkS+x97BIs64ZLxeE/ARwyS6wrldMm3C1MdKwCcnnEwMC1slI8wuxJOpiUH9MioC0A0i+GJw==", - "dev": true, - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.3.tgz", - "integrity": "sha512-yVRvFsEMrv7s0lGhzrggJjNOSmZCdgCjw9xWrPr/kNNLp6FaDfMC1KaYl3TSJ0c58bECwNBMoQrZJ8hA8E1eFg==", - "dev": true, - "dependencies": { - "@types/http-errors": "*", - "@types/mime": "*", - "@types/node": "*" - } - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "node_modules/basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "dependencies": { - "safe-buffer": "5.1.2" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/compression/node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/connect-pause": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/connect-pause/-/connect-pause-0.1.1.tgz", - "integrity": "sha512-a1gSWQBQD73krFXdUEYJom2RTFrWUL3YvXDCRkyv//GVXc79cdW9MngtRuN9ih4FDKBtfJAJId+BbDuX+1rh2w==", - "engines": { - "node": "*" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-disposition/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/errorhandler": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/errorhandler/-/errorhandler-1.5.1.tgz", - "integrity": "sha512-rcOwbfvP1WTViVoUjcfZicVzjhjTuhSMntHh6mW3IrEiyE6mJyXvsToJUJGlGlw/2xU9P5whlWNGlIDVeCiT4A==", - "dependencies": { - "accepts": "~1.3.7", - "escape-html": "~1.0.3" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.6.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/express-urlrewrite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/express-urlrewrite/-/express-urlrewrite-1.4.0.tgz", - "integrity": "sha512-PI5h8JuzoweS26vFizwQl6UTF25CAHSggNv0J25Dn/IKZscJHWZzPrI5z2Y2jgOzIaw2qh8l6+/jUcig23Z2SA==", - "dependencies": { - "debug": "*", - "path-to-regexp": "^1.0.3" - } - }, - "node_modules/express-urlrewrite/node_modules/path-to-regexp": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.9.0.tgz", - "integrity": "sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g==", - "dependencies": { - "isarray": "0.0.1" - } - }, - "node_modules/express/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" - }, - "node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" - }, - "node_modules/jju": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", - "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==" - }, - "node_modules/json-parse-helpfulerror": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/json-parse-helpfulerror/-/json-parse-helpfulerror-1.0.3.tgz", - "integrity": "sha512-XgP0FGR77+QhUxjXkwOMkC94k3WtqEBfcnjWqhRd82qTat4SWKRE+9kUnynz/shm3I4ea2+qISvTIeGTNU7kJg==", - "dependencies": { - "jju": "^1.1.0" - } - }, - "node_modules/json-server": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/json-server/-/json-server-0.17.4.tgz", - "integrity": "sha512-bGBb0WtFuAKbgI7JV3A864irWnMZSvBYRJbohaOuatHwKSRFUfqtQlrYMrB6WbalXy/cJabyjlb7JkHli6dYjQ==", - "dependencies": { - "body-parser": "^1.19.0", - "chalk": "^4.1.2", - "compression": "^1.7.4", - "connect-pause": "^0.1.1", - "cors": "^2.8.5", - "errorhandler": "^1.5.1", - "express": "^4.17.1", - "express-urlrewrite": "^1.4.0", - "json-parse-helpfulerror": "^1.0.3", - "lodash": "^4.17.21", - "lodash-id": "^0.14.1", - "lowdb": "^1.0.0", - "method-override": "^3.0.0", - "morgan": "^1.10.0", - "nanoid": "^3.1.23", - "please-upgrade-node": "^3.2.0", - "pluralize": "^8.0.0", - "server-destroy": "^1.0.1", - "yargs": "^17.0.1" - }, - "bin": { - "json-server": "lib/cli/bin.js" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash-id": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/lodash-id/-/lodash-id-0.14.1.tgz", - "integrity": "sha512-ikQPBTiq/d5m6dfKQlFdIXFzvThPi2Be9/AHxktOnDSfSxE1j9ICbBT5Elk1ke7HSTgM38LHTpmJovo9/klnLg==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/lowdb": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-1.0.0.tgz", - "integrity": "sha512-2+x8esE/Wb9SQ1F9IHaYWfsC9FIecLOPrK4g17FGEayjUWH172H6nwicRovGvSE2CPZouc2MCIqCI7h9d+GftQ==", - "dependencies": { - "graceful-fs": "^4.1.3", - "is-promise": "^2.1.0", - "lodash": "4", - "pify": "^3.0.0", - "steno": "^0.4.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/method-override": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/method-override/-/method-override-3.0.0.tgz", - "integrity": "sha512-IJ2NNN/mSl9w3kzWB92rcdHpz+HjkxhDJWNDBqSlas+zQdP8wBiJzITPg08M/k2uVvMow7Sk41atndNtt/PHSA==", - "dependencies": { - "debug": "3.1.0", - "methods": "~1.1.2", - "parseurl": "~1.3.2", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/method-override/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.53.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz", - "integrity": "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/morgan": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", - "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", - "dependencies": { - "basic-auth": "~2.0.1", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-finished": "~2.3.0", - "on-headers": "~1.0.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/morgan/node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" - }, - "node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "engines": { - "node": ">=4" - } - }, - "node_modules/please-upgrade-node": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", - "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", - "dependencies": { - "semver-compare": "^1.0.0" - } - }, - "node_modules/pluralize": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", - "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/semver-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==" - }, - "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/server-destroy": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz", - "integrity": "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==" - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/steno": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/steno/-/steno-0.4.4.tgz", - "integrity": "sha512-EEHMVYHNXFHfGtgjNITnka0aHhiAlo93F7z2/Pwd+g0teG9CnM3JIINM7hVVB5/rhw9voufD7Wukwgtw2uqh6w==", - "dependencies": { - "graceful-fs": "^4.1.3" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "engines": { - "node": ">=12" - } - } - } -} diff --git a/mock-backend/package.json b/mock-backend/package.json deleted file mode 100644 index 9540fa0a..00000000 --- a/mock-backend/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "mock-backend", - "version": "1.0.0", - "description": "", - "main": "index.js", - "type": "module", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "start": "node src/server.js" - }, - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": { - "cors": "^2.8.5", - "json-server": "^0.17.4", - "uuid": "^9.0.1" - }, - "devDependencies": { - "@types/json-server": "^0.14.5" - } -} diff --git a/mock-backend/src/mocks/db.json b/mock-backend/src/mocks/db.json deleted file mode 100644 index 36947158..00000000 --- a/mock-backend/src/mocks/db.json +++ /dev/null @@ -1,244 +0,0 @@ -{ - "combine": [ - { - "date": "default", - "description": "default", - "docLink": "default", - "fullName": "default", - "language": "default", - "location": "local", - "model": "openai_text-embedding-ada-002", - "name": "default", - "version": "" - }, - { - "date": "13/02/2023", - "description": "Serverless Framework, the serverless application framework for building web, mobile and IoT applications on AWS Lambda, Azure Functions, Google CloudFunctions & more!", - "docLink": "https://serverless.com/framework/docs/", - "fullName": "Serverless Framework", - "language": "serverless", - "location": "remote", - "name": "serverless framework", - "version": "3.27.0" - }, - { - "date": "15/02/2023", - "description": "Machine Learning in Python", - "docLink": "https://scikit-learn.org/stable/", - "fullName": "scikit-learn", - "language": "python", - "location": "remote", - "model": "openai_text-embedding-ada-002", - "name": "scikit-learn", - "version": "1.2.1" - }, - { - "date": "07/02/2023", - "description": "Machine Learning in Python", - "docLink": "https://scikit-learn.org/stable/", - "fullName": "scikit-learn", - "language": "python", - "location": "remote", - "name": "scikit-learn", - "version": "1.2.1" - }, - { - "date": "07/02/2023", - "description": "Pandas is alibrary providing high-performance, easy-to-use data structures and data analysis tools for the Python programming language.", - "docLink": "https://pandas.pydata.org/docs/", - "fullName": "Pandas", - "language": "python", - "location": "remote", - "model": "openai_text-embedding-ada-002", - "name": "pandas", - "version": "1.5.3" - }, - { - "date": "07/02/2023", - "description": "Pandas is alibrary providing high-performance, easy-to-use data structures and data analysis tools for the Python programming language.", - "docLink": "https://pandas.pydata.org/docs/", - "fullName": "Pandas", - "language": "python", - "location": "remote", - "name": "pandas", - "version": "1.5.3" - }, - { - "date": "29/02/2023", - "description": "Python is a programming language that lets you work quickly and integrate systems more effectively.", - "docLink": "https://docs.python.org/3/", - "fullName": "Python", - "language": "python", - "location": "remote", - "model": "huggingface_sentence-transformers-all-mpnet-base-v2", - "name": "python", - "version": "3.11.1" - }, - { - "date": "15/02/2023", - "description": "Python is a programming language that lets you work quickly and integrate systems more effectively.", - "docLink": "https://docs.python.org/3/", - "fullName": "Python", - "language": "python", - "location": "remote", - "model": "openai_text-embedding-ada-002", - "name": "python", - "version": "3.11.1" - }, - { - "date": "07/02/2023", - "description": "Python is a programming language that lets you work quickly and integrate systems more effectively.", - "docLink": "https://docs.python.org/3/", - "fullName": "Python", - "language": "python", - "location": "remote", - "name": "python", - "version": "3.11.1" - }, - { - "date": "08/02/2023", - "description": "GPT Index is a project consisting of a set of data structures designed to make it easier to use large external knowledge bases with LLMs.", - "docLink": "https://gpt-index.readthedocs.io/en/latest/index.html", - "fullName": "LangChain", - "language": "python", - "location": "remote", - "name": "gpt-index", - "version": "0.4.0" - }, - { - "date": "15/02/2023", - "description": "Large language models (LLMs) are emerging as a transformative technology, enabling developers to build applications that they previously could not.", - "docLink": "https://langchain.readthedocs.io/en/latest/index.html", - "fullName": "LangChain", - "language": "python", - "location": "remote", - "model": "openai_text-embedding-ada-002", - "name": "langchain", - "version": "0.0.87" - }, - { - "date": "07/02/2023", - "description": "Large language models (LLMs) are emerging as a transformative technology, enabling developers to build applications that they previously could not.", - "docLink": "https://langchain.readthedocs.io/en/latest/index.html", - "fullName": "LangChain", - "language": "python", - "location": "remote", - "name": "langchain", - "version": "0.0.79" - }, - { - "date": "13/03/2023", - "description": "Large language models (LLMs) are emerging as a transformative technology, enabling developers to build applications that they previously could not.", - "docLink": "https://langchain.readthedocs.io/en/latest/index.html", - "fullName": "LangChain", - "language": "python", - "location": "remote", - "model": "openai_text-embedding-ada-002", - "name": "langchain", - "version": "0.0.109" - }, - { - "date": "16/03/2023", - "description": "A JavaScript library for building user interfaces\nGet Started\n", - "docLink": "https://reactjs.org/", - "fullName": "React", - "language": "javascript", - "location": "remote", - "model": "openai_text-embedding-ada-002", - "name": "react", - "version": "v18.2.0" - }, - { - "date": "15/02/2023", - "description": "is a lightweight, interpreted, or just-in-time compiled programming language with first-class functions.", - "docLink": "https://developer.mozilla.org/en-US/docs/Web/JavaScript", - "fullName": "JavaScript", - "language": "javascript", - "location": "remote", - "model": "openai_text-embedding-ada-002", - "name": "javascript", - "version": "ES2015" - }, - { - "date": "16/03/2023", - "description": "An approachable, performant and versatile framework for building web user interfaces. ", - "docLink": "https://vuejs.org/", - "fullName": "Vue.js", - "language": "javascript", - "location": "remote", - "model": "openai_text-embedding-ada-002", - "name": "vuejs", - "version": "v3.3.0" - }, - { - "date": "16/03/2023", - "description": "Get ready for a development environment that can finally catch up with you.", - "docLink": "https://vitejs.dev/", - "fullName": "Vite", - "language": "javascript", - "location": "remote", - "model": "openai_text-embedding-ada-002", - "name": "vitejs", - "version": "v4.2.0" - }, - { - "date": "15/02/2023", - "description": "Solidity is an object-oriented, high-level language for implementing smart contracts.", - "docLink": "https://docs.soliditylang.org/en/v0.8.18/", - "fullName": "Solidity", - "language": "ethereum", - "location": "remote", - "model": "openai_text-embedding-ada-002", - "name": "solidity", - "version": "0.8.18" - }, - { - "date": "07/02/2023", - "description": "Solidity is an object-oriented, high-level language for implementing smart contracts.", - "docLink": "https://docs.soliditylang.org/en/v0.8.18/", - "fullName": "Solidity", - "language": "ethereum", - "location": "remote", - "name": "solidity", - "version": "0.8.18" - }, - { - "date": "28/02/2023", - "description": "GPT-powered chat for documentation search & assistance. ", - "docLink": "https://github.com/arc53/DocsGPT/wiki", - "fullName": "DocsGPT", - "language": "docsgpt", - "location": "remote", - "model": "huggingface_sentence-transformers-all-mpnet-base-v2", - "name": "docsgpt", - "version": "0.1.0" - }, - { - "date": "28/02/2023", - "description": "GPT-powered chat for documentation search & assistance. ", - "docLink": "https://github.com/arc53/DocsGPT/wiki", - "fullName": "DocsGPT", - "language": "docsgpt", - "location": "remote", - "model": "openai_text-embedding-ada-002", - "name": "docsgpt", - "version": "0.1.0" - } - ], - "conversations": [ - { - "id": "65cf39c936523eea21ebe117", - "name": "Request clarification" - }, - { - "id": "65cf39ba36523eea21ebe116", - "name": "Clarification request" - }, - { - "id": "65cf37e97d527c332bbac933", - "name": "Greetings, assistance inquiry." - }], - "docs_check": { - "status": "loaded" - } -} diff --git a/mock-backend/src/mocks/routes.json b/mock-backend/src/mocks/routes.json deleted file mode 100644 index 5bcccf0f..00000000 --- a/mock-backend/src/mocks/routes.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "/api/*": "/$1", - "/get_conversations": "/conversations", - "/get_single_conversation?id=:id": "/conversations/:id", - "/delete_conversation?id=:id": "/conversations/:id", - "/conversations?id=:id": "/conversations/:id" -} \ No newline at end of file diff --git a/mock-backend/src/server.js b/mock-backend/src/server.js deleted file mode 100644 index 93c326b1..00000000 --- a/mock-backend/src/server.js +++ /dev/null @@ -1,131 +0,0 @@ -import jsonServer from "json-server"; -import routes from "./mocks/routes.json" assert { type: "json" }; -import { v4 as uuid } from "uuid"; -import cors from 'cors' -const server = jsonServer.create(); -const router = jsonServer.router("./src/mocks/db.json"); -const middlewares = jsonServer.defaults(); - -const localStorage = []; - -server.use(middlewares); -server.use(cors({ origin: ['*'] })) -server.use(jsonServer.rewriter(routes)); - -server.use((req, res, next) => { - if (req.method === "POST") { - if (req.url.includes("/delete_conversation")) { - req.method = "DELETE"; - } else if (req.url === "/upload") { - const taskId = uuid(); - localStorage.push(taskId); - } - } - next(); -}); - -router.render = (req, res) => { - if (req.url === "/feedback") { - res.status(200).jsonp({ status: "ok" }); - } else if (req.url === "/upload") { - res.status(200).jsonp({ - status: "ok", - task_id: localStorage[localStorage.length - 1], - }); - } else if (req.url.includes("/task_status")) { - const taskId = req.query["task_id"]; - const taskIdExists = localStorage.includes(taskId); - if (taskIdExists) { - res.status(200).jsonp({ - result: { - directory: "temp", - filename: "install.rst", - formats: [".rst", ".md", ".pdf"], - name_job: "somename", - user: "local", - }, - status: "SUCCESS", - }); - } else { - res.status(404).jsonp({}); - } - } else if (req.url === "/stream" && req.method === "POST") { - res.writeHead(200, { - 'Content-Type': 'text/event-stream', - 'Cache-Control': 'no-cache', - 'Connection': 'keep-alive' - }); - const message = ('Hi, How are you today?').split(' '); - let index = 0; - const interval = setInterval(() => { - if (index < message.length) { - res.write(`data: {"answer": "${message[index++]} "}\n`); - } else { - res.write(`data: {"type": "id", "id": "65cbc39d11f077b9eeb06d26"}\n`) - res.write(`data: {"type": "end"}\n`) - clearInterval(interval); // Stop the interval once the message is fully streamed - res.end(); // End the response - } - }, 500); // Send a word every 1 second - } - else if (req.url === '/search' && req.method === 'POST') { - res.status(200).json( - [ - { - "text": "\n\n/api/answer\nIt's a POST request that sends a JSON in body with 4 values. It will receive an answer for a user provided question.\n", - "title": "API-docs.md" - }, - { - "text": "\n\nOur Standards\n\nExamples of behavior that contribute to a positive environment for our\ncommunity include:\n* Demonstrating empathy and kindness towards other people\n", - "title": "How-to-use-different-LLM.md" - } - ] - ) - } - else if (req.url === '/get_prompts' && req.method === 'GET') { - res.status(200).json([ - { - "id": "default", - "name": "default", - "type": "public" - }, - { - "id": "creative", - "name": "creative", - "type": "public" - }, - { - "id": "strict", - "name": "strict", - "type": "public" - } - ]); - } - else if (req.url.startsWith('/get_single_prompt') && req.method==='GET') { - const id = req.query.id; - console.log('hre'); - if (id === 'creative') - res.status(200).json({ - "content": "You are a DocsGPT, friendly and helpful AI assistant by Arc53 that provides help with documents. You give thorough answers with code examples if possible." - }) - else if (id === 'strict') { - res.status(200).json({ - "content": "You are an AI Assistant, DocsGPT, adept at offering document assistance. \nYour expertise lies in providing answer on top of provided context." - }) - } - else { - res.status(200).json({ - "content": "You are a helpful AI assistant, DocsGPT, specializing in document assistance, designed to offer detailed and informative responses." - }) - } - } - else { - res.status(res.statusCode).jsonp(res.locals.data); - } -}; - -server.use(router); - -server.listen(8080, () => { - console.log("JSON Server is running"); -}); From c4f3dc4434402b7223e01feb8a8f2dde63018ac4 Mon Sep 17 00:00:00 2001 From: Pavel Date: Fri, 20 Dec 2024 18:41:47 +0300 Subject: [PATCH 27/66] test version --- application/parser/chunking.py | 118 +++++++++++++++++++++++ application/parser/embedding_pipeline.py | 86 +++++++++++++++++ application/parser/open_ai_func.py | 75 -------------- application/parser/token_func.py | 79 --------------- application/worker.py | 30 +++--- 5 files changed, 221 insertions(+), 167 deletions(-) create mode 100644 application/parser/chunking.py create mode 100755 application/parser/embedding_pipeline.py delete mode 100755 application/parser/open_ai_func.py delete mode 100644 application/parser/token_func.py diff --git a/application/parser/chunking.py b/application/parser/chunking.py new file mode 100644 index 00000000..26f05dba --- /dev/null +++ b/application/parser/chunking.py @@ -0,0 +1,118 @@ +import re +from typing import List, Tuple, Union +import logging +from application.parser.schema.base import Document +from application.utils import get_encoding + +logger = logging.getLogger(__name__) + +class Chunker: + def __init__( + self, + chunking_strategy: str = "classic_chunk", + max_tokens: int = 2000, + min_tokens: int = 150, + duplicate_headers: bool = False, + ): + if chunking_strategy not in ["classic_chunk"]: + raise ValueError(f"Unsupported chunking strategy: {chunking_strategy}") + self.chunking_strategy = chunking_strategy + self.max_tokens = max_tokens + self.min_tokens = min_tokens + self.duplicate_headers = duplicate_headers + self.encoding = get_encoding() + + def separate_header_and_body(self, text: str) -> Tuple[str, str]: + header_pattern = r"^(.*?\n){3}" + match = re.match(header_pattern, text) + if match: + header = match.group(0) + body = text[len(header):] + else: + header, body = "", text # No header, treat entire text as body + return header, body + + def combine_documents(self, doc: Document, next_doc: Document) -> Document: + combined_text = doc.text + " " + next_doc.text + combined_token_count = len(self.encoding.encode(combined_text)) + new_doc = Document( + text=combined_text, + doc_id=doc.doc_id, + embedding=doc.embedding, + extra_info={**(doc.extra_info or {}), "token_count": combined_token_count} + ) + return new_doc + + def split_document(self, doc: Document) -> List[Document]: + split_docs = [] + header, body = self.separate_header_and_body(doc.text) + header_tokens = self.encoding.encode(header) if header else [] + body_tokens = self.encoding.encode(body) + + current_position = 0 + part_index = 0 + while current_position < len(body_tokens): + end_position = current_position + self.max_tokens - len(header_tokens) + chunk_tokens = (header_tokens + body_tokens[current_position:end_position] + if self.duplicate_headers or part_index == 0 else body_tokens[current_position:end_position]) + chunk_text = self.encoding.decode(chunk_tokens) + new_doc = Document( + text=chunk_text, + doc_id=f"{doc.doc_id}-{part_index}", + embedding=doc.embedding, + extra_info={**(doc.extra_info or {}), "token_count": len(chunk_tokens)} + ) + split_docs.append(new_doc) + current_position = end_position + part_index += 1 + header_tokens = [] + return split_docs + + def classic_chunk(self, documents: List[Document]) -> List[Document]: + processed_docs = [] + i = 0 + while i < len(documents): + doc = documents[i] + tokens = self.encoding.encode(doc.text) + token_count = len(tokens) + + if self.min_tokens <= token_count <= self.max_tokens: + doc.extra_info = doc.extra_info or {} + doc.extra_info["token_count"] = token_count + processed_docs.append(doc) + i += 1 + elif token_count < self.min_tokens: + if i + 1 < len(documents): + next_doc = documents[i + 1] + next_tokens = self.encoding.encode(next_doc.text) + if token_count + len(next_tokens) <= self.max_tokens: + # Combine small documents + combined_doc = self.combine_documents(doc, next_doc) + processed_docs.append(combined_doc) + i += 2 + else: + # Keep the small document as is if adding next_doc would exceed max_tokens + doc.extra_info = doc.extra_info or {} + doc.extra_info["token_count"] = token_count + processed_docs.append(doc) + i += 1 + else: + # No next document to combine with; add the small document as is + doc.extra_info = doc.extra_info or {} + doc.extra_info["token_count"] = token_count + processed_docs.append(doc) + i += 1 + else: + # Split large documents + processed_docs.extend(self.split_document(doc)) + i += 1 + return processed_docs + + def chunk( + self, + documents: List[Document] + ) -> List[Document]: + if self.chunking_strategy == "classic_chunk": + return self.classic_chunk(documents) + else: + raise ValueError("Unsupported chunking strategy") diff --git a/application/parser/embedding_pipeline.py b/application/parser/embedding_pipeline.py new file mode 100755 index 00000000..6cf40048 --- /dev/null +++ b/application/parser/embedding_pipeline.py @@ -0,0 +1,86 @@ +import os +import logging +from retry import retry +from tqdm import tqdm +from application.core.settings import settings +from application.vectorstore.vector_creator import VectorCreator + + +@retry(tries=10, delay=60) +def add_text_to_store_with_retry(store, doc, source_id): + """ + Add a document's text and metadata to the vector store with retry logic. + Args: + store: The vector store object. + doc: The document to be added. + source_id: Unique identifier for the source. + """ + try: + doc.metadata["source_id"] = str(source_id) + store.add_texts([doc.page_content], metadatas=[doc.metadata]) + except Exception as e: + logging.error(f"Failed to add document with retry: {e}") + raise + + +def embed_and_store_documents(docs, folder_name, source_id, task_status): + """ + Embeds documents and stores them in a vector store. + + Args: + docs (list): List of documents to be embedded and stored. + folder_name (str): Directory to save the vector store. + source_id (str): Unique identifier for the source. + task_status: Task state manager for progress updates. + + Returns: + None + """ + # Ensure the folder exists + if not os.path.exists(folder_name): + os.makedirs(folder_name) + + # Initialize vector store + if settings.VECTOR_STORE == "faiss": + docs_init = [docs.pop(0)] + store = VectorCreator.create_vectorstore( + settings.VECTOR_STORE, + docs_init=docs_init, + source_id=folder_name, + embeddings_key=os.getenv("EMBEDDINGS_KEY"), + ) + else: + store = VectorCreator.create_vectorstore( + settings.VECTOR_STORE, + source_id=source_id, + embeddings_key=os.getenv("EMBEDDINGS_KEY"), + ) + store.delete_index() + + total_docs = len(docs) + + # Process and embed documents + for idx, doc in tqdm( + docs, + desc="Embedding 🦖", + unit="docs", + total=total_docs, + bar_format="{l_bar}{bar}| Time Left: {remaining}", + ): + try: + # Update task status for progress tracking + progress = int((idx / total_docs) * 100) + task_status.update_state(state="PROGRESS", meta={"current": progress}) + + # Add document to vector store + add_text_to_store_with_retry(store, doc, source_id) + except Exception as e: + logging.error(f"Error embedding document {idx}: {e}") + logging.info(f"Saving progress at document {idx} out of {total_docs}") + store.save_local(folder_name) + break + + # Save the vector store + if settings.VECTOR_STORE == "faiss": + store.save_local(folder_name) + logging.info("Vector store saved successfully.") diff --git a/application/parser/open_ai_func.py b/application/parser/open_ai_func.py deleted file mode 100755 index 3109f583..00000000 --- a/application/parser/open_ai_func.py +++ /dev/null @@ -1,75 +0,0 @@ -import os - -from retry import retry - -from application.core.settings import settings - -from application.vectorstore.vector_creator import VectorCreator - - -# from langchain_community.embeddings import HuggingFaceEmbeddings -# from langchain_community.embeddings import HuggingFaceInstructEmbeddings -# from langchain_community.embeddings import CohereEmbeddings - - -@retry(tries=10, delay=60) -def store_add_texts_with_retry(store, i, id): - # add source_id to the metadata - i.metadata["source_id"] = str(id) - store.add_texts([i.page_content], metadatas=[i.metadata]) - # store_pine.add_texts([i.page_content], metadatas=[i.metadata]) - - -def call_openai_api(docs, folder_name, id, task_status): - # Function to create a vector store from the documents and save it to disk - - if not os.path.exists(f"{folder_name}"): - os.makedirs(f"{folder_name}") - - from tqdm import tqdm - - c1 = 0 - if settings.VECTOR_STORE == "faiss": - docs_init = [docs[0]] - docs.pop(0) - - store = VectorCreator.create_vectorstore( - settings.VECTOR_STORE, - docs_init=docs_init, - source_id=f"{folder_name}", - embeddings_key=os.getenv("EMBEDDINGS_KEY"), - ) - else: - store = VectorCreator.create_vectorstore( - settings.VECTOR_STORE, - source_id=str(id), - embeddings_key=os.getenv("EMBEDDINGS_KEY"), - ) - store.delete_index() - # Uncomment for MPNet embeddings - # model_name = "sentence-transformers/all-mpnet-base-v2" - # hf = HuggingFaceEmbeddings(model_name=model_name) - # store = FAISS.from_documents(docs_test, hf) - s1 = len(docs) - for i in tqdm( - docs, - desc="Embedding 🦖", - unit="docs", - total=len(docs), - bar_format="{l_bar}{bar}| Time Left: {remaining}", - ): - try: - task_status.update_state( - state="PROGRESS", meta={"current": int((c1 / s1) * 100)} - ) - store_add_texts_with_retry(store, i, id) - except Exception as e: - print(e) - print("Error on ", i) - print("Saving progress") - print(f"stopped at {c1} out of {len(docs)}") - store.save_local(f"{folder_name}") - break - c1 += 1 - if settings.VECTOR_STORE == "faiss": - store.save_local(f"{folder_name}") diff --git a/application/parser/token_func.py b/application/parser/token_func.py deleted file mode 100644 index 7511cde0..00000000 --- a/application/parser/token_func.py +++ /dev/null @@ -1,79 +0,0 @@ -import re -from math import ceil -from typing import List - -import tiktoken -from application.parser.schema.base import Document - - -def separate_header_and_body(text): - header_pattern = r"^(.*?\n){3}" - match = re.match(header_pattern, text) - header = match.group(0) - body = text[len(header):] - return header, body - - -def group_documents(documents: List[Document], min_tokens: int, max_tokens: int) -> List[Document]: - docs = [] - current_group = None - - for doc in documents: - doc_len = len(tiktoken.get_encoding("cl100k_base").encode(doc.text)) - - # Check if current group is empty or if the document can be added based on token count and matching metadata - if (current_group is None or - (len(tiktoken.get_encoding("cl100k_base").encode(current_group.text)) + doc_len < max_tokens and - doc_len < min_tokens and - current_group.extra_info == doc.extra_info)): - if current_group is None: - current_group = doc # Use the document directly to retain its metadata - else: - current_group.text += " " + doc.text # Append text to the current group - else: - docs.append(current_group) - current_group = doc # Start a new group with the current document - - if current_group is not None: - docs.append(current_group) - - return docs - - -def split_documents(documents: List[Document], max_tokens: int) -> List[Document]: - docs = [] - for doc in documents: - token_length = len(tiktoken.get_encoding("cl100k_base").encode(doc.text)) - if token_length <= max_tokens: - docs.append(doc) - else: - header, body = separate_header_and_body(doc.text) - if len(tiktoken.get_encoding("cl100k_base").encode(header)) > max_tokens: - body = doc.text - header = "" - num_body_parts = ceil(token_length / max_tokens) - part_length = ceil(len(body) / num_body_parts) - body_parts = [body[i:i + part_length] for i in range(0, len(body), part_length)] - for i, body_part in enumerate(body_parts): - new_doc = Document(text=header + body_part.strip(), - doc_id=f"{doc.doc_id}-{i}", - embedding=doc.embedding, - extra_info=doc.extra_info) - docs.append(new_doc) - return docs - - -def group_split(documents: List[Document], max_tokens: int = 2000, min_tokens: int = 150, token_check: bool = True): - if not token_check: - return documents - print("Grouping small documents") - try: - documents = group_documents(documents=documents, min_tokens=min_tokens, max_tokens=max_tokens) - except Exception: - print("Grouping failed, try running without token_check") - print("Separating large documents") - try: - documents = split_documents(documents=documents, max_tokens=max_tokens) - except Exception: - print("Grouping failed, try running without token_check") - return documents diff --git a/application/worker.py b/application/worker.py index 33cd90e5..0edb46ff 100755 --- a/application/worker.py +++ b/application/worker.py @@ -12,10 +12,10 @@ from bson.objectid import ObjectId from application.core.mongo_db import MongoDB from application.core.settings import settings from application.parser.file.bulk import SimpleDirectoryReader -from application.parser.open_ai_func import call_openai_api +from application.parser.embedding_pipeline import embed_and_store_documents from application.parser.remote.remote_creator import RemoteCreator from application.parser.schema.base import Document -from application.parser.token_func import group_split +from application.parser.chunking import Chunker from application.utils import count_tokens_docs mongo = MongoDB.get_client() @@ -153,17 +153,19 @@ def ingest_worker( exclude_hidden=exclude, file_metadata=metadata_from_filename, ).load_data() - raw_docs = group_split( - documents=raw_docs, - min_tokens=MIN_TOKENS, + + chunker = Chunker( + chunking_strategy="classic_chunk", max_tokens=MAX_TOKENS, - token_check=token_check, + min_tokens=MIN_TOKENS, + duplicate_headers=False ) + raw_docs = chunker.chunk(documents=raw_docs) docs = [Document.to_langchain_format(raw_doc) for raw_doc in raw_docs] id = ObjectId() - call_openai_api(docs, full_path, id, self) + embed_and_store_documents(docs, full_path, id, self) tokens = count_tokens_docs(docs) self.update_state(state="PROGRESS", meta={"current": 100}) @@ -217,21 +219,23 @@ def remote_worker( remote_loader = RemoteCreator.create_loader(loader) raw_docs = remote_loader.load_data(source_data) - docs = group_split( - documents=raw_docs, - min_tokens=MIN_TOKENS, + chunker = Chunker( + chunking_strategy="classic_chunk", max_tokens=MAX_TOKENS, - token_check=token_check, + min_tokens=MIN_TOKENS, + duplicate_headers=False ) + docs = chunker.chunk(documents=raw_docs) + tokens = count_tokens_docs(docs) if operation_mode == "upload": id = ObjectId() - call_openai_api(docs, full_path, id, self) + embed_and_store_documents(docs, full_path, id, self) elif operation_mode == "sync": if not doc_id or not ObjectId.is_valid(doc_id): raise ValueError("doc_id must be provided for sync operation.") id = ObjectId(doc_id) - call_openai_api(docs, full_path, id, self) + embed_and_store_documents(docs, full_path, id, self) self.update_state(state="PROGRESS", meta={"current": 100}) file_data = { From c2a95b5bec24e07d8eed3a42553d1f21f45cfdbc Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 20 Dec 2024 17:32:58 +0000 Subject: [PATCH 28/66] lint: fixing index and classc rag --- application/retriever/classic_rag.py | 1 - frontend/src/modals/types/index.ts | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/application/retriever/classic_rag.py b/application/retriever/classic_rag.py index a00e191f..2e355513 100644 --- a/application/retriever/classic_rag.py +++ b/application/retriever/classic_rag.py @@ -2,7 +2,6 @@ from application.core.settings import settings from application.retriever.base import BaseRetriever from application.tools.agent import Agent -from application.utils import num_tokens_from_string from application.vectorstore.vector_creator import VectorCreator diff --git a/frontend/src/modals/types/index.ts b/frontend/src/modals/types/index.ts index c018e44c..458496d2 100644 --- a/frontend/src/modals/types/index.ts +++ b/frontend/src/modals/types/index.ts @@ -8,6 +8,7 @@ export type AvailableTool = { description: string; parameters: object; }[]; +}; export type WrapperModalProps = { children?: React.ReactNode; From 1f75f0c0821ca413a6617404a3c94f9c1d86c5ce Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 20 Dec 2024 18:13:37 +0000 Subject: [PATCH 29/66] fix: tests --- application/llm/anthropic.py | 4 ++-- application/llm/sagemaker.py | 4 ++-- tests/llm/test_anthropic.py | 3 ++- tests/llm/test_sagemaker.py | 2 +- tests/test_cache.py | 25 ++++++++++++++----------- 5 files changed, 21 insertions(+), 17 deletions(-) diff --git a/application/llm/anthropic.py b/application/llm/anthropic.py index 4081bcd0..1fa3b5b2 100644 --- a/application/llm/anthropic.py +++ b/application/llm/anthropic.py @@ -17,7 +17,7 @@ class AnthropicLLM(BaseLLM): self.AI_PROMPT = AI_PROMPT def _raw_gen( - self, baseself, model, messages, stream=False, max_tokens=300, **kwargs + self, baseself, model, messages, stream=False, tools=None, max_tokens=300, **kwargs ): context = messages[0]["content"] user_question = messages[-1]["content"] @@ -34,7 +34,7 @@ class AnthropicLLM(BaseLLM): return completion.completion def _raw_gen_stream( - self, baseself, model, messages, stream=True, max_tokens=300, **kwargs + self, baseself, model, messages, stream=True, tools=None, max_tokens=300, **kwargs ): context = messages[0]["content"] user_question = messages[-1]["content"] diff --git a/application/llm/sagemaker.py b/application/llm/sagemaker.py index 63947430..aaf99a12 100644 --- a/application/llm/sagemaker.py +++ b/application/llm/sagemaker.py @@ -76,7 +76,7 @@ class SagemakerAPILLM(BaseLLM): self.endpoint = settings.SAGEMAKER_ENDPOINT self.runtime = runtime - def _raw_gen(self, baseself, model, messages, stream=False, **kwargs): + def _raw_gen(self, baseself, model, messages, stream=False, tools=None, **kwargs): context = messages[0]["content"] user_question = messages[-1]["content"] prompt = f"### Instruction \n {user_question} \n ### Context \n {context} \n ### Answer \n" @@ -105,7 +105,7 @@ class SagemakerAPILLM(BaseLLM): print(result[0]["generated_text"], file=sys.stderr) return result[0]["generated_text"][len(prompt) :] - def _raw_gen_stream(self, baseself, model, messages, stream=True, **kwargs): + def _raw_gen_stream(self, baseself, model, messages, stream=True, tools=None, **kwargs): context = messages[0]["content"] user_question = messages[-1]["content"] prompt = f"### Instruction \n {user_question} \n ### Context \n {context} \n ### Answer \n" diff --git a/tests/llm/test_anthropic.py b/tests/llm/test_anthropic.py index 689013c0..50ddbe29 100644 --- a/tests/llm/test_anthropic.py +++ b/tests/llm/test_anthropic.py @@ -46,6 +46,7 @@ class TestAnthropicLLM(unittest.TestCase): {"content": "question"} ] mock_responses = [Mock(completion="response_1"), Mock(completion="response_2")] + mock_tools = Mock() with patch("application.cache.get_redis_instance") as mock_make_redis: mock_redis_instance = mock_make_redis.return_value @@ -53,7 +54,7 @@ class TestAnthropicLLM(unittest.TestCase): mock_redis_instance.set = Mock() with patch.object(self.llm.anthropic.completions, "create", return_value=iter(mock_responses)) as mock_create: - responses = list(self.llm.gen_stream("test_model", messages)) + responses = list(self.llm.gen_stream("test_model", messages, tools=mock_tools)) self.assertListEqual(responses, ["response_1", "response_2"]) prompt_expected = "### Context \n context \n ### Question \n question" diff --git a/tests/llm/test_sagemaker.py b/tests/llm/test_sagemaker.py index d659d498..2b893a9a 100644 --- a/tests/llm/test_sagemaker.py +++ b/tests/llm/test_sagemaker.py @@ -76,7 +76,7 @@ class TestSagemakerAPILLM(unittest.TestCase): with patch.object(self.sagemaker.runtime, 'invoke_endpoint_with_response_stream', return_value=self.response) as mock_invoke_endpoint: - output = list(self.sagemaker.gen_stream(None, self.messages)) + output = list(self.sagemaker.gen_stream(None, self.messages, tools=None)) mock_invoke_endpoint.assert_called_once_with( EndpointName=self.sagemaker.endpoint, ContentType='application/json', diff --git a/tests/test_cache.py b/tests/test_cache.py index 4270a181..af2b5e00 100644 --- a/tests/test_cache.py +++ b/tests/test_cache.py @@ -12,18 +12,21 @@ def test_make_gen_cache_key(): {'role': 'system', 'content': 'test_system_message'}, ] model = "test_docgpt" + tools = None # Manually calculate the expected hash - expected_combined = f"{model}_{json.dumps(messages, sort_keys=True)}" + messages_str = json.dumps(messages) + tools_str = json.dumps(tools) if tools else "" + expected_combined = f"{model}_{messages_str}_{tools_str}" expected_hash = get_hash(expected_combined) - cache_key = gen_cache_key(*messages, model=model) + cache_key = gen_cache_key(messages, model=model, tools=None) assert cache_key == expected_hash def test_gen_cache_key_invalid_message_format(): # Test when messages is not a list with unittest.TestCase.assertRaises(unittest.TestCase, ValueError) as context: - gen_cache_key("This is not a list", model="docgpt") + gen_cache_key("This is not a list", model="docgpt", tools=None) assert str(context.exception) == "All messages must be dictionaries." # Test for gen_cache decorator @@ -35,14 +38,14 @@ def test_gen_cache_hit(mock_make_redis): mock_redis_instance.get.return_value = b"cached_result" # Simulate a cache hit @gen_cache - def mock_function(self, model, messages): + def mock_function(self, model, messages, stream, tools): return "new_result" messages = [{'role': 'user', 'content': 'test_user_message'}] model = "test_docgpt" # Act - result = mock_function(None, model, messages) + result = mock_function(None, model, messages, stream=False, tools=None) # Assert assert result == "cached_result" # Should return cached result @@ -58,7 +61,7 @@ def test_gen_cache_miss(mock_make_redis): mock_redis_instance.get.return_value = None # Simulate a cache miss @gen_cache - def mock_function(self, model, messages): + def mock_function(self, model, messages, steam, tools): return "new_result" messages = [ @@ -67,7 +70,7 @@ def test_gen_cache_miss(mock_make_redis): ] model = "test_docgpt" # Act - result = mock_function(None, model, messages) + result = mock_function(None, model, messages, stream=False, tools=None) # Assert assert result == "new_result" @@ -83,14 +86,14 @@ def test_stream_cache_hit(mock_make_redis): mock_redis_instance.get.return_value = cached_chunk @stream_cache - def mock_function(self, model, messages, stream): + def mock_function(self, model, messages, stream, tools): yield "new_chunk" messages = [{'role': 'user', 'content': 'test_user_message'}] model = "test_docgpt" # Act - result = list(mock_function(None, model, messages, stream=True)) + result = list(mock_function(None, model, messages, stream=True, tools=None)) # Assert assert result == ["chunk1", "chunk2"] # Should return cached chunks @@ -106,7 +109,7 @@ def test_stream_cache_miss(mock_make_redis): mock_redis_instance.get.return_value = None # Simulate a cache miss @stream_cache - def mock_function(self, model, messages, stream): + def mock_function(self, model, messages, stream, tools): yield "new_chunk" messages = [ @@ -117,7 +120,7 @@ def test_stream_cache_miss(mock_make_redis): model = "test_docgpt" # Act - result = list(mock_function(None, model, messages, stream=True)) + result = list(mock_function(None, model, messages, stream=True, tools=None)) # Assert assert result == ["new_chunk"] From b41a989051b437112f7a05ee36d8cb5df877d84b Mon Sep 17 00:00:00 2001 From: Pavel Date: Fri, 20 Dec 2024 18:41:47 +0300 Subject: [PATCH 30/66] test version --- application/parser/chunking.py | 118 +++++++++++++++++++++++ application/parser/embedding_pipeline.py | 86 +++++++++++++++++ application/parser/open_ai_func.py | 75 -------------- application/parser/token_func.py | 79 --------------- application/worker.py | 30 +++--- 5 files changed, 221 insertions(+), 167 deletions(-) create mode 100644 application/parser/chunking.py create mode 100755 application/parser/embedding_pipeline.py delete mode 100755 application/parser/open_ai_func.py delete mode 100644 application/parser/token_func.py diff --git a/application/parser/chunking.py b/application/parser/chunking.py new file mode 100644 index 00000000..26f05dba --- /dev/null +++ b/application/parser/chunking.py @@ -0,0 +1,118 @@ +import re +from typing import List, Tuple, Union +import logging +from application.parser.schema.base import Document +from application.utils import get_encoding + +logger = logging.getLogger(__name__) + +class Chunker: + def __init__( + self, + chunking_strategy: str = "classic_chunk", + max_tokens: int = 2000, + min_tokens: int = 150, + duplicate_headers: bool = False, + ): + if chunking_strategy not in ["classic_chunk"]: + raise ValueError(f"Unsupported chunking strategy: {chunking_strategy}") + self.chunking_strategy = chunking_strategy + self.max_tokens = max_tokens + self.min_tokens = min_tokens + self.duplicate_headers = duplicate_headers + self.encoding = get_encoding() + + def separate_header_and_body(self, text: str) -> Tuple[str, str]: + header_pattern = r"^(.*?\n){3}" + match = re.match(header_pattern, text) + if match: + header = match.group(0) + body = text[len(header):] + else: + header, body = "", text # No header, treat entire text as body + return header, body + + def combine_documents(self, doc: Document, next_doc: Document) -> Document: + combined_text = doc.text + " " + next_doc.text + combined_token_count = len(self.encoding.encode(combined_text)) + new_doc = Document( + text=combined_text, + doc_id=doc.doc_id, + embedding=doc.embedding, + extra_info={**(doc.extra_info or {}), "token_count": combined_token_count} + ) + return new_doc + + def split_document(self, doc: Document) -> List[Document]: + split_docs = [] + header, body = self.separate_header_and_body(doc.text) + header_tokens = self.encoding.encode(header) if header else [] + body_tokens = self.encoding.encode(body) + + current_position = 0 + part_index = 0 + while current_position < len(body_tokens): + end_position = current_position + self.max_tokens - len(header_tokens) + chunk_tokens = (header_tokens + body_tokens[current_position:end_position] + if self.duplicate_headers or part_index == 0 else body_tokens[current_position:end_position]) + chunk_text = self.encoding.decode(chunk_tokens) + new_doc = Document( + text=chunk_text, + doc_id=f"{doc.doc_id}-{part_index}", + embedding=doc.embedding, + extra_info={**(doc.extra_info or {}), "token_count": len(chunk_tokens)} + ) + split_docs.append(new_doc) + current_position = end_position + part_index += 1 + header_tokens = [] + return split_docs + + def classic_chunk(self, documents: List[Document]) -> List[Document]: + processed_docs = [] + i = 0 + while i < len(documents): + doc = documents[i] + tokens = self.encoding.encode(doc.text) + token_count = len(tokens) + + if self.min_tokens <= token_count <= self.max_tokens: + doc.extra_info = doc.extra_info or {} + doc.extra_info["token_count"] = token_count + processed_docs.append(doc) + i += 1 + elif token_count < self.min_tokens: + if i + 1 < len(documents): + next_doc = documents[i + 1] + next_tokens = self.encoding.encode(next_doc.text) + if token_count + len(next_tokens) <= self.max_tokens: + # Combine small documents + combined_doc = self.combine_documents(doc, next_doc) + processed_docs.append(combined_doc) + i += 2 + else: + # Keep the small document as is if adding next_doc would exceed max_tokens + doc.extra_info = doc.extra_info or {} + doc.extra_info["token_count"] = token_count + processed_docs.append(doc) + i += 1 + else: + # No next document to combine with; add the small document as is + doc.extra_info = doc.extra_info or {} + doc.extra_info["token_count"] = token_count + processed_docs.append(doc) + i += 1 + else: + # Split large documents + processed_docs.extend(self.split_document(doc)) + i += 1 + return processed_docs + + def chunk( + self, + documents: List[Document] + ) -> List[Document]: + if self.chunking_strategy == "classic_chunk": + return self.classic_chunk(documents) + else: + raise ValueError("Unsupported chunking strategy") diff --git a/application/parser/embedding_pipeline.py b/application/parser/embedding_pipeline.py new file mode 100755 index 00000000..6cf40048 --- /dev/null +++ b/application/parser/embedding_pipeline.py @@ -0,0 +1,86 @@ +import os +import logging +from retry import retry +from tqdm import tqdm +from application.core.settings import settings +from application.vectorstore.vector_creator import VectorCreator + + +@retry(tries=10, delay=60) +def add_text_to_store_with_retry(store, doc, source_id): + """ + Add a document's text and metadata to the vector store with retry logic. + Args: + store: The vector store object. + doc: The document to be added. + source_id: Unique identifier for the source. + """ + try: + doc.metadata["source_id"] = str(source_id) + store.add_texts([doc.page_content], metadatas=[doc.metadata]) + except Exception as e: + logging.error(f"Failed to add document with retry: {e}") + raise + + +def embed_and_store_documents(docs, folder_name, source_id, task_status): + """ + Embeds documents and stores them in a vector store. + + Args: + docs (list): List of documents to be embedded and stored. + folder_name (str): Directory to save the vector store. + source_id (str): Unique identifier for the source. + task_status: Task state manager for progress updates. + + Returns: + None + """ + # Ensure the folder exists + if not os.path.exists(folder_name): + os.makedirs(folder_name) + + # Initialize vector store + if settings.VECTOR_STORE == "faiss": + docs_init = [docs.pop(0)] + store = VectorCreator.create_vectorstore( + settings.VECTOR_STORE, + docs_init=docs_init, + source_id=folder_name, + embeddings_key=os.getenv("EMBEDDINGS_KEY"), + ) + else: + store = VectorCreator.create_vectorstore( + settings.VECTOR_STORE, + source_id=source_id, + embeddings_key=os.getenv("EMBEDDINGS_KEY"), + ) + store.delete_index() + + total_docs = len(docs) + + # Process and embed documents + for idx, doc in tqdm( + docs, + desc="Embedding 🦖", + unit="docs", + total=total_docs, + bar_format="{l_bar}{bar}| Time Left: {remaining}", + ): + try: + # Update task status for progress tracking + progress = int((idx / total_docs) * 100) + task_status.update_state(state="PROGRESS", meta={"current": progress}) + + # Add document to vector store + add_text_to_store_with_retry(store, doc, source_id) + except Exception as e: + logging.error(f"Error embedding document {idx}: {e}") + logging.info(f"Saving progress at document {idx} out of {total_docs}") + store.save_local(folder_name) + break + + # Save the vector store + if settings.VECTOR_STORE == "faiss": + store.save_local(folder_name) + logging.info("Vector store saved successfully.") diff --git a/application/parser/open_ai_func.py b/application/parser/open_ai_func.py deleted file mode 100755 index 3109f583..00000000 --- a/application/parser/open_ai_func.py +++ /dev/null @@ -1,75 +0,0 @@ -import os - -from retry import retry - -from application.core.settings import settings - -from application.vectorstore.vector_creator import VectorCreator - - -# from langchain_community.embeddings import HuggingFaceEmbeddings -# from langchain_community.embeddings import HuggingFaceInstructEmbeddings -# from langchain_community.embeddings import CohereEmbeddings - - -@retry(tries=10, delay=60) -def store_add_texts_with_retry(store, i, id): - # add source_id to the metadata - i.metadata["source_id"] = str(id) - store.add_texts([i.page_content], metadatas=[i.metadata]) - # store_pine.add_texts([i.page_content], metadatas=[i.metadata]) - - -def call_openai_api(docs, folder_name, id, task_status): - # Function to create a vector store from the documents and save it to disk - - if not os.path.exists(f"{folder_name}"): - os.makedirs(f"{folder_name}") - - from tqdm import tqdm - - c1 = 0 - if settings.VECTOR_STORE == "faiss": - docs_init = [docs[0]] - docs.pop(0) - - store = VectorCreator.create_vectorstore( - settings.VECTOR_STORE, - docs_init=docs_init, - source_id=f"{folder_name}", - embeddings_key=os.getenv("EMBEDDINGS_KEY"), - ) - else: - store = VectorCreator.create_vectorstore( - settings.VECTOR_STORE, - source_id=str(id), - embeddings_key=os.getenv("EMBEDDINGS_KEY"), - ) - store.delete_index() - # Uncomment for MPNet embeddings - # model_name = "sentence-transformers/all-mpnet-base-v2" - # hf = HuggingFaceEmbeddings(model_name=model_name) - # store = FAISS.from_documents(docs_test, hf) - s1 = len(docs) - for i in tqdm( - docs, - desc="Embedding 🦖", - unit="docs", - total=len(docs), - bar_format="{l_bar}{bar}| Time Left: {remaining}", - ): - try: - task_status.update_state( - state="PROGRESS", meta={"current": int((c1 / s1) * 100)} - ) - store_add_texts_with_retry(store, i, id) - except Exception as e: - print(e) - print("Error on ", i) - print("Saving progress") - print(f"stopped at {c1} out of {len(docs)}") - store.save_local(f"{folder_name}") - break - c1 += 1 - if settings.VECTOR_STORE == "faiss": - store.save_local(f"{folder_name}") diff --git a/application/parser/token_func.py b/application/parser/token_func.py deleted file mode 100644 index 7511cde0..00000000 --- a/application/parser/token_func.py +++ /dev/null @@ -1,79 +0,0 @@ -import re -from math import ceil -from typing import List - -import tiktoken -from application.parser.schema.base import Document - - -def separate_header_and_body(text): - header_pattern = r"^(.*?\n){3}" - match = re.match(header_pattern, text) - header = match.group(0) - body = text[len(header):] - return header, body - - -def group_documents(documents: List[Document], min_tokens: int, max_tokens: int) -> List[Document]: - docs = [] - current_group = None - - for doc in documents: - doc_len = len(tiktoken.get_encoding("cl100k_base").encode(doc.text)) - - # Check if current group is empty or if the document can be added based on token count and matching metadata - if (current_group is None or - (len(tiktoken.get_encoding("cl100k_base").encode(current_group.text)) + doc_len < max_tokens and - doc_len < min_tokens and - current_group.extra_info == doc.extra_info)): - if current_group is None: - current_group = doc # Use the document directly to retain its metadata - else: - current_group.text += " " + doc.text # Append text to the current group - else: - docs.append(current_group) - current_group = doc # Start a new group with the current document - - if current_group is not None: - docs.append(current_group) - - return docs - - -def split_documents(documents: List[Document], max_tokens: int) -> List[Document]: - docs = [] - for doc in documents: - token_length = len(tiktoken.get_encoding("cl100k_base").encode(doc.text)) - if token_length <= max_tokens: - docs.append(doc) - else: - header, body = separate_header_and_body(doc.text) - if len(tiktoken.get_encoding("cl100k_base").encode(header)) > max_tokens: - body = doc.text - header = "" - num_body_parts = ceil(token_length / max_tokens) - part_length = ceil(len(body) / num_body_parts) - body_parts = [body[i:i + part_length] for i in range(0, len(body), part_length)] - for i, body_part in enumerate(body_parts): - new_doc = Document(text=header + body_part.strip(), - doc_id=f"{doc.doc_id}-{i}", - embedding=doc.embedding, - extra_info=doc.extra_info) - docs.append(new_doc) - return docs - - -def group_split(documents: List[Document], max_tokens: int = 2000, min_tokens: int = 150, token_check: bool = True): - if not token_check: - return documents - print("Grouping small documents") - try: - documents = group_documents(documents=documents, min_tokens=min_tokens, max_tokens=max_tokens) - except Exception: - print("Grouping failed, try running without token_check") - print("Separating large documents") - try: - documents = split_documents(documents=documents, max_tokens=max_tokens) - except Exception: - print("Grouping failed, try running without token_check") - return documents diff --git a/application/worker.py b/application/worker.py index 33cd90e5..0edb46ff 100755 --- a/application/worker.py +++ b/application/worker.py @@ -12,10 +12,10 @@ from bson.objectid import ObjectId from application.core.mongo_db import MongoDB from application.core.settings import settings from application.parser.file.bulk import SimpleDirectoryReader -from application.parser.open_ai_func import call_openai_api +from application.parser.embedding_pipeline import embed_and_store_documents from application.parser.remote.remote_creator import RemoteCreator from application.parser.schema.base import Document -from application.parser.token_func import group_split +from application.parser.chunking import Chunker from application.utils import count_tokens_docs mongo = MongoDB.get_client() @@ -153,17 +153,19 @@ def ingest_worker( exclude_hidden=exclude, file_metadata=metadata_from_filename, ).load_data() - raw_docs = group_split( - documents=raw_docs, - min_tokens=MIN_TOKENS, + + chunker = Chunker( + chunking_strategy="classic_chunk", max_tokens=MAX_TOKENS, - token_check=token_check, + min_tokens=MIN_TOKENS, + duplicate_headers=False ) + raw_docs = chunker.chunk(documents=raw_docs) docs = [Document.to_langchain_format(raw_doc) for raw_doc in raw_docs] id = ObjectId() - call_openai_api(docs, full_path, id, self) + embed_and_store_documents(docs, full_path, id, self) tokens = count_tokens_docs(docs) self.update_state(state="PROGRESS", meta={"current": 100}) @@ -217,21 +219,23 @@ def remote_worker( remote_loader = RemoteCreator.create_loader(loader) raw_docs = remote_loader.load_data(source_data) - docs = group_split( - documents=raw_docs, - min_tokens=MIN_TOKENS, + chunker = Chunker( + chunking_strategy="classic_chunk", max_tokens=MAX_TOKENS, - token_check=token_check, + min_tokens=MIN_TOKENS, + duplicate_headers=False ) + docs = chunker.chunk(documents=raw_docs) + tokens = count_tokens_docs(docs) if operation_mode == "upload": id = ObjectId() - call_openai_api(docs, full_path, id, self) + embed_and_store_documents(docs, full_path, id, self) elif operation_mode == "sync": if not doc_id or not ObjectId.is_valid(doc_id): raise ValueError("doc_id must be provided for sync operation.") id = ObjectId(doc_id) - call_openai_api(docs, full_path, id, self) + embed_and_store_documents(docs, full_path, id, self) self.update_state(state="PROGRESS", meta={"current": 100}) file_data = { From 90962ee056df8d97d7d9284665d28fd0fe655ff8 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 23 Dec 2024 17:41:13 +0000 Subject: [PATCH 31/66] fix: debugger in launch json --- .vscode/launch.json | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 5be1f711..5083d977 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -13,7 +13,7 @@ ] }, { - "name": "Python Debugger: Flask", + "name": "Flask Debugger", "type": "debugpy", "request": "launch", "module": "flask", @@ -32,5 +32,23 @@ ], "cwd": "${workspaceFolder}", }, + { + "name": "Celery Debugger", + "type": "debugpy", + "request": "launch", + "module": "celery", + "env": { + "PYTHONPATH": "${workspaceFolder}", + }, + "args": [ + "-A", + "application.app.celery", + "worker", + "-l", + "INFO", + "--pool=solo" + ], + "cwd": "${workspaceFolder}" + } ] } \ No newline at end of file From 41b4c28430ae29eded99b62e82e1ad4b863e99a3 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 23 Dec 2024 17:41:44 +0000 Subject: [PATCH 32/66] fix: linting --- application/parser/chunking.py | 2 +- application/parser/embedding_pipeline.py | 4 ++-- application/worker.py | 2 -- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/application/parser/chunking.py b/application/parser/chunking.py index 26f05dba..aae14898 100644 --- a/application/parser/chunking.py +++ b/application/parser/chunking.py @@ -1,5 +1,5 @@ import re -from typing import List, Tuple, Union +from typing import List, Tuple import logging from application.parser.schema.base import Document from application.utils import get_encoding diff --git a/application/parser/embedding_pipeline.py b/application/parser/embedding_pipeline.py index 6cf40048..0435cd14 100755 --- a/application/parser/embedding_pipeline.py +++ b/application/parser/embedding_pipeline.py @@ -61,7 +61,7 @@ def embed_and_store_documents(docs, folder_name, source_id, task_status): # Process and embed documents for idx, doc in tqdm( - docs, + enumerate(docs), desc="Embedding 🦖", unit="docs", total=total_docs, @@ -69,7 +69,7 @@ def embed_and_store_documents(docs, folder_name, source_id, task_status): ): try: # Update task status for progress tracking - progress = int((idx / total_docs) * 100) + progress = int(((idx + 1) / total_docs) * 100) task_status.update_state(state="PROGRESS", meta={"current": progress}) # Add document to vector store diff --git a/application/worker.py b/application/worker.py index 0edb46ff..f4f181e5 100755 --- a/application/worker.py +++ b/application/worker.py @@ -126,7 +126,6 @@ def ingest_worker( limit = None exclude = True sample = False - token_check = True full_path = os.path.join(directory, user, name_job) logging.info(f"Ingest file: {full_path}", extra={"user": user, "job": name_job}) @@ -205,7 +204,6 @@ def remote_worker( operation_mode="upload", doc_id=None, ): - token_check = True full_path = os.path.join(directory, user, name_job) if not os.path.exists(full_path): From b2a013c02739d818fcd2f16513286815b7c04905 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 23 Dec 2024 18:11:15 +0000 Subject: [PATCH 33/66] fix: remove reqs from scripts folder --- scripts/requirements.txt | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 scripts/requirements.txt diff --git a/scripts/requirements.txt b/scripts/requirements.txt deleted file mode 100644 index d90af2c3..00000000 --- a/scripts/requirements.txt +++ /dev/null @@ -1,22 +0,0 @@ -dataclasses_json==0.6.3 -docx2txt==0.8 -EbookLib==0.18 -escodegen==1.0.11 -esprima==4.0.1 -faiss_cpu==1.7.4 -html2text==2020.1.16 -javalang==0.13.0 -langchain==0.2.10 -langchain_community==0.2.9 -langchain-openai==0.0.5 -nltk==3.9 -openapi3_parser==1.1.16 -pandas==2.2.0 -PyPDF2==3.0.1 -python-dotenv==1.0.1 -retry==0.9.2 -Sphinx==7.2.6 -tiktoken==0.5.2 -tqdm==4.66.3 -typer==0.9.0 -unstructured==0.12.2 From 474298c969e0f08779dbaddf6bfae7a8b2dff261 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Dec 2024 18:12:13 +0000 Subject: [PATCH 34/66] build(deps): bump jinja2 from 3.1.4 to 3.1.5 in /application Bumps [jinja2](https://github.com/pallets/jinja) from 3.1.4 to 3.1.5. - [Release notes](https://github.com/pallets/jinja/releases) - [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets/jinja/compare/3.1.4...3.1.5) --- updated-dependencies: - dependency-name: jinja2 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- application/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/requirements.txt b/application/requirements.txt index b9d2c33c..015eb545 100644 --- a/application/requirements.txt +++ b/application/requirements.txt @@ -18,7 +18,7 @@ gTTS==2.3.2 gunicorn==23.0.0 html2text==2024.2.26 javalang==0.13.0 -jinja2==3.1.4 +jinja2==3.1.5 jiter==0.5.0 jmespath==1.0.1 joblib==1.4.2 From 7760e779aeee4cffceb4cc157ac75e32cfc8650a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Dec 2024 20:14:33 +0000 Subject: [PATCH 35/66] build(deps): bump i18next from 23.15.1 to 24.2.0 in /frontend Bumps [i18next](https://github.com/i18next/i18next) from 23.15.1 to 24.2.0. - [Release notes](https://github.com/i18next/i18next/releases) - [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md) - [Commits](https://github.com/i18next/i18next/compare/v23.15.1...v24.2.0) --- updated-dependencies: - dependency-name: i18next dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 20 ++++++++++++++------ frontend/package.json | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index f96a17d4..4371d7c3 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "@reduxjs/toolkit": "^2.2.7", "chart.js": "^4.4.4", - "i18next": "^23.15.1", + "i18next": "^24.2.0", "i18next-browser-languagedetector": "^8.0.0", "prop-types": "^15.8.1", "react": "^18.2.0", @@ -1649,7 +1649,7 @@ "version": "18.3.0", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", - "dev": true, + "devOptional": true, "dependencies": { "@types/react": "*" } @@ -4921,9 +4921,9 @@ } }, "node_modules/i18next": { - "version": "23.15.1", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.15.1.tgz", - "integrity": "sha512-wB4abZ3uK7EWodYisHl/asf8UYEhrI/vj/8aoSsrj/ZDxj4/UXPOa1KvFt1Fq5hkUHquNqwFlDprmjZ8iySgYA==", + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-24.2.0.tgz", + "integrity": "sha512-ArJJTS1lV6lgKH7yEf4EpgNZ7+THl7bsGxxougPYiXRTJ/Fe1j08/TBpV9QsXCIYVfdE/HWG/xLezJ5DOlfBOA==", "funding": [ { "type": "individual", @@ -4940,6 +4940,14 @@ ], "dependencies": { "@babel/runtime": "^7.23.2" + }, + "peerDependencies": { + "typescript": "^5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/i18next-browser-languagedetector": { @@ -9250,7 +9258,7 @@ "version": "5.6.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/frontend/package.json b/frontend/package.json index 868a72ae..ca6ca518 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -21,7 +21,7 @@ "dependencies": { "@reduxjs/toolkit": "^2.2.7", "chart.js": "^4.4.4", - "i18next": "^23.15.1", + "i18next": "^24.2.0", "i18next-browser-languagedetector": "^8.0.0", "prop-types": "^15.8.1", "react": "^18.2.0", From 502d82e1c9e7f003b485e34f81d44221e875677e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Dec 2024 20:58:59 +0000 Subject: [PATCH 36/66] build(deps): bump langchain-openai from 0.2.0 to 0.2.14 in /application Bumps [langchain-openai](https://github.com/langchain-ai/langchain) from 0.2.0 to 0.2.14. - [Release notes](https://github.com/langchain-ai/langchain/releases) - [Commits](https://github.com/langchain-ai/langchain/compare/langchain-openai==0.2.0...langchain-openai==0.2.14) --- updated-dependencies: - dependency-name: langchain-openai dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- application/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/requirements.txt b/application/requirements.txt index b9d2c33c..dfd1898e 100644 --- a/application/requirements.txt +++ b/application/requirements.txt @@ -31,7 +31,7 @@ kombu==5.4.2 langchain==0.3.11 langchain-community==0.3.11 langchain-core==0.3.25 -langchain-openai==0.2.0 +langchain-openai==0.2.14 langchain-text-splitters==0.3.0 langsmith==0.2.3 lazy-object-proxy==1.10.0 From 52dd3f798a59932661fdf374460c60ec94a430f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Dec 2024 21:06:38 +0000 Subject: [PATCH 37/66] build(deps): bump jinja2 from 3.1.4 to 3.1.5 in /application Bumps [jinja2](https://github.com/pallets/jinja) from 3.1.4 to 3.1.5. - [Release notes](https://github.com/pallets/jinja/releases) - [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets/jinja/compare/3.1.4...3.1.5) --- updated-dependencies: - dependency-name: jinja2 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- application/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/requirements.txt b/application/requirements.txt index a8b909e5..e4e3f232 100644 --- a/application/requirements.txt +++ b/application/requirements.txt @@ -18,7 +18,7 @@ gTTS==2.3.2 gunicorn==23.0.0 html2text==2024.2.26 javalang==0.13.0 -jinja2==3.1.4 +jinja2==3.1.5 jiter==0.5.0 jmespath==1.0.1 joblib==1.4.2 From ab90a93eec9f7cd5b505b62356a3ab6391f8c1b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Dec 2024 21:15:46 +0000 Subject: [PATCH 38/66] build(deps): bump numpy from 1.26.4 to 2.2.1 in /application Bumps [numpy](https://github.com/numpy/numpy) from 1.26.4 to 2.2.1. - [Release notes](https://github.com/numpy/numpy/releases) - [Changelog](https://github.com/numpy/numpy/blob/main/doc/RELEASE_WALKTHROUGH.rst) - [Commits](https://github.com/numpy/numpy/compare/v1.26.4...v2.2.1) --- updated-dependencies: - dependency-name: numpy dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- application/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/requirements.txt b/application/requirements.txt index e4e3f232..57da7d9a 100644 --- a/application/requirements.txt +++ b/application/requirements.txt @@ -42,7 +42,7 @@ mpmath==1.3.0 multidict==6.1.0 mypy-extensions==1.0.0 networkx==3.3 -numpy==1.26.4 +numpy==2.2.1 openai==1.57.0 openapi-schema-validator==0.6.2 openapi-spec-validator==0.6.0 From fb2df05e3feb21ec763aa283facce70e45aad9ac Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 23 Dec 2024 21:23:54 +0000 Subject: [PATCH 39/66] feat: upgrade python and bump faiss-cpu --- application/Dockerfile | 14 +++++++------- application/requirements.txt | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/application/Dockerfile b/application/Dockerfile index d076bc41..d422db45 100644 --- a/application/Dockerfile +++ b/application/Dockerfile @@ -8,14 +8,14 @@ RUN apt-get update && \ add-apt-repository ppa:deadsnakes/ppa && \ # Install necessary packages and Python apt-get update && \ - apt-get install -y --no-install-recommends gcc wget unzip libc6-dev python3.11 python3.11-distutils python3.11-venv && \ + apt-get install -y --no-install-recommends gcc wget unzip libc6-dev python3.12 python3.12-venv && \ rm -rf /var/lib/apt/lists/* # Verify Python installation and setup symlink -RUN if [ -f /usr/bin/python3.11 ]; then \ - ln -s /usr/bin/python3.11 /usr/bin/python; \ +RUN if [ -f /usr/bin/python3.12 ]; then \ + ln -s /usr/bin/python3.12 /usr/bin/python; \ else \ - echo "Python 3.11 not found"; exit 1; \ + echo "Python 3.12 not found"; exit 1; \ fi # Download and unzip the model @@ -33,7 +33,7 @@ RUN apt-get remove --purge -y wget unzip && apt-get autoremove -y && rm -rf /var COPY requirements.txt . # Setup Python virtual environment -RUN python3.11 -m venv /venv +RUN python3.12 -m venv /venv # Activate virtual environment and install Python packages ENV PATH="/venv/bin:$PATH" @@ -50,8 +50,8 @@ RUN apt-get update && \ apt-get install -y software-properties-common && \ add-apt-repository ppa:deadsnakes/ppa && \ # Install Python - apt-get update && apt-get install -y --no-install-recommends python3.11 && \ - ln -s /usr/bin/python3.11 /usr/bin/python && \ + apt-get update && apt-get install -y --no-install-recommends python3.12 && \ + ln -s /usr/bin/python3.12 /usr/bin/python && \ rm -rf /var/lib/apt/lists/* # Set working directory diff --git a/application/requirements.txt b/application/requirements.txt index 57da7d9a..787be450 100644 --- a/application/requirements.txt +++ b/application/requirements.txt @@ -12,7 +12,7 @@ escodegen==1.0.11 esprima==4.0.1 esutils==1.0.1 Flask==3.0.3 -faiss-cpu==1.8.0.post1 +faiss-cpu==1.9.0.post1 flask-restx==1.3.0 gTTS==2.3.2 gunicorn==23.0.0 From 4927b64d273a34d56f74cebf2bcfb1a25357de99 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 23 Dec 2024 21:26:22 +0000 Subject: [PATCH 40/66] bump pytest --- .github/workflows/pytest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index cf68ff9c..d5b31109 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -6,7 +6,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.11"] + python-version: ["3.12"] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} @@ -23,7 +23,7 @@ jobs: run: | python -m pytest --cov=application --cov-report=xml - name: Upload coverage reports to Codecov - if: github.event_name == 'pull_request' && matrix.python-version == '3.11' + if: github.event_name == 'pull_request' && matrix.python-version == '3.12' uses: codecov/codecov-action@v5 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} From fdd2300517cec29836ec687d9dd49ee2715c3163 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Dec 2024 21:31:04 +0000 Subject: [PATCH 41/66] build(deps): bump langchain-openai from 0.2.0 to 0.2.14 in /application Bumps [langchain-openai](https://github.com/langchain-ai/langchain) from 0.2.0 to 0.2.14. - [Release notes](https://github.com/langchain-ai/langchain/releases) - [Commits](https://github.com/langchain-ai/langchain/compare/langchain-openai==0.2.0...langchain-openai==0.2.14) --- updated-dependencies: - dependency-name: langchain-openai dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- application/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/requirements.txt b/application/requirements.txt index 787be450..f189a300 100644 --- a/application/requirements.txt +++ b/application/requirements.txt @@ -31,7 +31,7 @@ kombu==5.4.2 langchain==0.3.11 langchain-community==0.3.11 langchain-core==0.3.25 -langchain-openai==0.2.0 +langchain-openai==0.2.14 langchain-text-splitters==0.3.0 langsmith==0.2.3 lazy-object-proxy==1.10.0 From 36e4398bcb2fef487c17ce0530e5d93b45023abc Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 23 Dec 2024 21:39:33 +0000 Subject: [PATCH 42/66] fix: bump deps --- application/requirements.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/application/requirements.txt b/application/requirements.txt index f189a300..da9d114e 100644 --- a/application/requirements.txt +++ b/application/requirements.txt @@ -28,11 +28,11 @@ jsonschema==4.23.0 jsonschema-spec==0.2.4 jsonschema-specifications==2023.7.1 kombu==5.4.2 -langchain==0.3.11 -langchain-community==0.3.11 -langchain-core==0.3.25 +langchain==0.3.13 +langchain-community==0.3.13 +langchain-core==0.3.28 langchain-openai==0.2.14 -langchain-text-splitters==0.3.0 +langchain-text-splitters==0.3.4 langsmith==0.2.3 lazy-object-proxy==1.10.0 lxml==5.3.0 @@ -43,7 +43,7 @@ multidict==6.1.0 mypy-extensions==1.0.0 networkx==3.3 numpy==2.2.1 -openai==1.57.0 +openai==1.58.1 openapi-schema-validator==0.6.2 openapi-spec-validator==0.6.0 openapi3-parser==1.1.18 From 2536bd098826a90b9f6582ccf58a98c73ae88e02 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Dec 2024 21:44:39 +0000 Subject: [PATCH 43/66] build(deps): bump elasticsearch from 8.15.1 to 8.17.0 in /application Bumps [elasticsearch](https://github.com/elastic/elasticsearch-py) from 8.15.1 to 8.17.0. - [Release notes](https://github.com/elastic/elasticsearch-py/releases) - [Commits](https://github.com/elastic/elasticsearch-py/compare/v8.15.1...v8.17.0) --- updated-dependencies: - dependency-name: elasticsearch dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- application/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/requirements.txt b/application/requirements.txt index da9d114e..20e90232 100644 --- a/application/requirements.txt +++ b/application/requirements.txt @@ -7,7 +7,7 @@ docx2txt==0.8 duckduckgo-search==6.3.0 ebooklib==0.18 elastic-transport==8.15.0 -elasticsearch==8.15.1 +elasticsearch==8.17.0 escodegen==1.0.11 esprima==4.0.1 esutils==1.0.1 From e30291966a9761aa39f46296ccb4699bcaf44185 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 23 Dec 2024 21:47:31 +0000 Subject: [PATCH 44/66] fix: bump elastic transport --- application/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/requirements.txt b/application/requirements.txt index 20e90232..08990ab2 100644 --- a/application/requirements.txt +++ b/application/requirements.txt @@ -6,7 +6,7 @@ dataclasses-json==0.6.7 docx2txt==0.8 duckduckgo-search==6.3.0 ebooklib==0.18 -elastic-transport==8.15.0 +elastic-transport==8.15.1 elasticsearch==8.17.0 escodegen==1.0.11 esprima==4.0.1 From ba9e2101bbf81629af4093df36d36b85b3995d0a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Dec 2024 21:52:27 +0000 Subject: [PATCH 45/66] build(deps-dev): bump postcss from 8.4.47 to 8.4.49 in /frontend Bumps [postcss](https://github.com/postcss/postcss) from 8.4.47 to 8.4.49. - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/postcss/postcss/compare/8.4.47...8.4.49) --- updated-dependencies: - dependency-name: postcss dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 10 +++++----- frontend/package.json | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 4371d7c3..4d6759da 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -48,7 +48,7 @@ "eslint-plugin-unused-imports": "^4.1.4", "husky": "^8.0.0", "lint-staged": "^15.2.10", - "postcss": "^8.4.41", + "postcss": "^8.4.49", "prettier": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.8", "tailwindcss": "^3.4.15", @@ -7466,9 +7466,9 @@ } }, "node_modules/postcss": { - "version": "8.4.47", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", - "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", "dev": true, "funding": [ { @@ -7486,7 +7486,7 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.1.0", + "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, "engines": { diff --git a/frontend/package.json b/frontend/package.json index ca6ca518..220f9759 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -59,7 +59,7 @@ "eslint-plugin-unused-imports": "^4.1.4", "husky": "^8.0.0", "lint-staged": "^15.2.10", - "postcss": "^8.4.41", + "postcss": "^8.4.49", "prettier": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.8", "tailwindcss": "^3.4.15", From 636ac2a56c9b1974132fc24e77f0b785dc2b95ad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Dec 2024 21:58:40 +0000 Subject: [PATCH 46/66] build(deps-dev): bump typescript from 5.6.2 to 5.7.2 in /frontend Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.6.2 to 5.7.2. - [Release notes](https://github.com/microsoft/TypeScript/releases) - [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release.yml) - [Commits](https://github.com/microsoft/TypeScript/compare/v5.6.2...v5.7.2) --- updated-dependencies: - dependency-name: typescript dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 8 ++++---- frontend/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 4d6759da..d6ea061e 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -52,7 +52,7 @@ "prettier": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.8", "tailwindcss": "^3.4.15", - "typescript": "^5.6.2", + "typescript": "^5.7.2", "vite": "^5.4.11", "vite-plugin-svgr": "^4.2.0" } @@ -9255,9 +9255,9 @@ } }, "node_modules/typescript": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", - "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "devOptional": true, "bin": { "tsc": "bin/tsc", diff --git a/frontend/package.json b/frontend/package.json index 220f9759..83237b07 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -63,7 +63,7 @@ "prettier": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.8", "tailwindcss": "^3.4.15", - "typescript": "^5.6.2", + "typescript": "^5.7.2", "vite": "^5.4.11", "vite-plugin-svgr": "^4.2.0" } From 868ea1a1e24e0691a88917a3ff6299bf057b1de5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Dec 2024 22:03:05 +0000 Subject: [PATCH 47/66] build(deps-dev): bump lint-staged from 15.2.10 to 15.2.11 in /frontend Bumps [lint-staged](https://github.com/lint-staged/lint-staged) from 15.2.10 to 15.2.11. - [Release notes](https://github.com/lint-staged/lint-staged/releases) - [Changelog](https://github.com/lint-staged/lint-staged/blob/master/CHANGELOG.md) - [Commits](https://github.com/lint-staged/lint-staged/compare/v15.2.10...v15.2.11) --- updated-dependencies: - dependency-name: lint-staged dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 138 ++++++++++++++----------------------- frontend/package.json | 2 +- 2 files changed, 52 insertions(+), 88 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index d6ea061e..1fc37133 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -47,7 +47,7 @@ "eslint-plugin-react": "^7.37.2", "eslint-plugin-unused-imports": "^4.1.4", "husky": "^8.0.0", - "lint-staged": "^15.2.10", + "lint-staged": "^15.2.11", "postcss": "^8.4.49", "prettier": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.8", @@ -864,18 +864,6 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/@isaacs/cliui/node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -2152,6 +2140,18 @@ "node": ">=8" } }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", @@ -2902,11 +2902,11 @@ } }, "node_modules/debug": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", - "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -3068,9 +3068,9 @@ "dev": true }, "node_modules/emoji-regex": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", - "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", "dev": true }, "node_modules/entities": { @@ -4304,9 +4304,9 @@ } }, "node_modules/get-east-asian-width": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz", - "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", "dev": true, "engines": { "node": ">=18" @@ -5671,21 +5671,21 @@ "dev": true }, "node_modules/lint-staged": { - "version": "15.2.10", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.10.tgz", - "integrity": "sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==", + "version": "15.2.11", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.11.tgz", + "integrity": "sha512-Ev6ivCTYRTGs9ychvpVw35m/bcNDuBN+mnTeObCL5h+boS5WzBEC6LHI4I9F/++sZm1m+J2LEiy0gxL/R9TBqQ==", "dev": true, "dependencies": { "chalk": "~5.3.0", "commander": "~12.1.0", - "debug": "~4.3.6", + "debug": "~4.4.0", "execa": "~8.0.1", - "lilconfig": "~3.1.2", - "listr2": "~8.2.4", + "lilconfig": "~3.1.3", + "listr2": "~8.2.5", "micromatch": "~4.0.8", "pidtree": "~0.6.0", "string-argv": "~0.3.2", - "yaml": "~2.5.0" + "yaml": "~2.6.1" }, "bin": { "lint-staged": "bin/lint-staged.js" @@ -5710,9 +5710,9 @@ } }, "node_modules/lint-staged/node_modules/lilconfig": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", - "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "dev": true, "engines": { "node": ">=14" @@ -5722,9 +5722,9 @@ } }, "node_modules/listr2": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.4.tgz", - "integrity": "sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==", + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.5.tgz", + "integrity": "sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==", "dev": true, "dependencies": { "cli-truncate": "^4.0.0", @@ -5779,9 +5779,9 @@ } }, "node_modules/log-update/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "engines": { "node": ">=12" @@ -5790,18 +5790,6 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/log-update/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/log-update/node_modules/is-fullwidth-code-point": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", @@ -6968,9 +6956,9 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/mz": { "version": "2.7.0", @@ -8576,18 +8564,6 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/snake-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", @@ -8679,9 +8655,9 @@ } }, "node_modules/string-width/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "engines": { "node": ">=12" @@ -9775,9 +9751,9 @@ } }, "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "engines": { "node": ">=12" @@ -9786,18 +9762,6 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/wrap-ansi/node_modules/strip-ansi": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", @@ -9834,9 +9798,9 @@ "dev": true }, "node_modules/yaml": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", - "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.1.tgz", + "integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==", "dev": true, "bin": { "yaml": "bin.mjs" diff --git a/frontend/package.json b/frontend/package.json index 83237b07..0547d68d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -58,7 +58,7 @@ "eslint-plugin-react": "^7.37.2", "eslint-plugin-unused-imports": "^4.1.4", "husky": "^8.0.0", - "lint-staged": "^15.2.10", + "lint-staged": "^15.2.11", "postcss": "^8.4.49", "prettier": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.8", From 753832d701cc127887adf933b8a6f48ab0818a9d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Dec 2024 22:06:40 +0000 Subject: [PATCH 48/66] build(deps): bump react-router-dom from 6.8.1 to 7.1.1 in /frontend Bumps [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) from 6.8.1 to 7.1.1. - [Release notes](https://github.com/remix-run/react-router/releases) - [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/CHANGELOG.md) - [Commits](https://github.com/remix-run/react-router/commits/react-router-dom@7.1.1/packages/react-router-dom) --- updated-dependencies: - dependency-name: react-router-dom dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 69 +++++++++++++++++++++++++------------- frontend/package.json | 2 +- 2 files changed, 47 insertions(+), 24 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 1fc37133..9a3dbf31 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -22,7 +22,7 @@ "react-i18next": "^15.0.2", "react-markdown": "^9.0.1", "react-redux": "^8.0.5", - "react-router-dom": "^6.8.1", + "react-router-dom": "^7.1.1", "react-syntax-highlighter": "^15.5.0", "rehype-katex": "^7.0.1", "remark-gfm": "^4.0.0", @@ -1051,14 +1051,6 @@ } } }, - "node_modules/@remix-run/router": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.3.2.tgz", - "integrity": "sha512-t54ONhl/h75X94SWsHGQ4G/ZrCEguKSRQr7DrjTciJXW0YU1QhlwYeycvK5JgkzlxmvrK7wq1NB/PLtHxoiDcA==", - "engines": { - "node": ">=14" - } - }, "node_modules/@rollup/pluginutils": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", @@ -1549,6 +1541,11 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==" + }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -2784,6 +2781,14 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "engines": { + "node": ">=18" + } + }, "node_modules/copy-to-clipboard": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", @@ -7972,33 +7977,41 @@ } }, "node_modules/react-router": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.8.1.tgz", - "integrity": "sha512-Jgi8BzAJQ8MkPt8ipXnR73rnD7EmZ0HFFb7jdQU24TynGW1Ooqin2KVDN9voSC+7xhqbbCd2cjGUepb6RObnyg==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.1.1.tgz", + "integrity": "sha512-39sXJkftkKWRZ2oJtHhCxmoCrBCULr/HAH4IT5DHlgu/Q0FCPV0S4Lx+abjDTx/74xoZzNYDYbOZWlJjruyuDQ==", "dependencies": { - "@remix-run/router": "1.3.2" + "@types/cookie": "^0.6.0", + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0", + "turbo-stream": "2.4.0" }, "engines": { - "node": ">=14" + "node": ">=20.0.0" }, "peerDependencies": { - "react": ">=16.8" + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } } }, "node_modules/react-router-dom": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.8.1.tgz", - "integrity": "sha512-67EXNfkQgf34P7+PSb6VlBuaacGhkKn3kpE51+P6zYSG2kiRoumXEL6e27zTa9+PGF2MNXbgIUHTVlleLbIcHQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.1.1.tgz", + "integrity": "sha512-vSrQHWlJ5DCfyrhgo0k6zViOe9ToK8uT5XGSmnuC2R3/g261IdIMpZVqfjD6vWSXdnf5Czs4VA/V60oVR6/jnA==", "dependencies": { - "@remix-run/router": "1.3.2", - "react-router": "6.8.1" + "react-router": "7.1.1" }, "engines": { - "node": ">=14" + "node": ">=20.0.0" }, "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" + "react": ">=18", + "react-dom": ">=18" } }, "node_modules/react-side-effect": { @@ -8456,6 +8469,11 @@ "semver": "bin/semver.js" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -9133,6 +9151,11 @@ "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, + "node_modules/turbo-stream": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz", + "integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 0547d68d..ff98e94c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -33,7 +33,7 @@ "react-i18next": "^15.0.2", "react-markdown": "^9.0.1", "react-redux": "^8.0.5", - "react-router-dom": "^6.8.1", + "react-router-dom": "^7.1.1", "react-syntax-highlighter": "^15.5.0", "rehype-katex": "^7.0.1", "remark-gfm": "^4.0.0", From b8fade251bb5af4750c70ff7dc79607ca501fe7f Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Tue, 24 Dec 2024 17:15:17 +0530 Subject: [PATCH 49/66] (feat: searchResults): adding utility to preprocess markdown --- extensions/react-widget/src/utils/helper.ts | 191 ++++++++++++++++++-- 1 file changed, 180 insertions(+), 11 deletions(-) diff --git a/extensions/react-widget/src/utils/helper.ts b/extensions/react-widget/src/utils/helper.ts index d9aa19c3..511b39fc 100644 --- a/extensions/react-widget/src/utils/helper.ts +++ b/extensions/react-widget/src/utils/helper.ts @@ -27,22 +27,169 @@ export const getOS = () => { return 'other'; }; -export const preprocessSearchResultsToHTML = (text: string, keyword: string) => { - const md = new MarkdownIt(); - const htmlString = md.render(text); +interface MarkdownElement { + type: 'heading' | 'paragraph' | 'code' | 'list' | 'other'; + content: string; + level?: number; +} - // Container for processed HTML - const filteredResults = document.createElement("div"); - filteredResults.innerHTML = htmlString; +interface ParsedElement { + content: string; + tag: string; +} - if (!processNode(filteredResults, keyword.trim())) return null; +export const processMarkdownString = (markdown: string): ParsedElement[] => { + const result: ParsedElement[] = []; + const lines = markdown.trim().split('\n'); + + let isInCodeBlock = false; + let currentCodeBlock = ''; - return filteredResults.innerHTML.trim() ? filteredResults.outerHTML : null; + for (let i = 0; i < lines.length; i++) { + const trimmedLine = lines[i].trim(); + if (!trimmedLine) continue; + + if (trimmedLine.startsWith('```')) { + if (isInCodeBlock) { + if (currentCodeBlock.trim()) { + result.push({ + content: currentCodeBlock.trim(), + tag: 'code' + }); + } + currentCodeBlock = ''; + isInCodeBlock = false; + } else { + isInCodeBlock = true; + } + continue; + } + + if (isInCodeBlock) { + currentCodeBlock += trimmedLine + '\n'; + continue; + } + + const headingMatch = trimmedLine.match(/^(#{1,6})\s+(.+)$/); + if (headingMatch) { + result.push({ + content: headingMatch[2], + tag: 'heading' + }); + continue; + } + + const bulletMatch = trimmedLine.match(/^[-*]\s+(.+)$/); + if (bulletMatch) { + result.push({ + content: bulletMatch[1], + tag: 'bulletList' + }); + continue; + } + + const numberedMatch = trimmedLine.match(/^\d+\.\s+(.+)$/); + if (numberedMatch) { + result.push({ + content: numberedMatch[1], + tag: 'numberedList' + }); + continue; + } + + result.push({ + content: trimmedLine, + tag: 'text' + }); + } + + if (isInCodeBlock && currentCodeBlock.trim()) { + result.push({ + content: currentCodeBlock.trim(), + tag: 'code' + }); + } + + return result; }; +export const preprocessSearchResultsToHTML = (text: string, keyword: string): MarkdownElement[] | null => { + const md = new MarkdownIt(); + const tokens = md.parse(text, {}); + const results: MarkdownElement[] = []; + + for (let i = 0; i < tokens.length; i++) { + const token = tokens[i]; + + if (token.type.endsWith('_close') || !token.content) continue; + const content = token.content.toLowerCase(); + const keywordLower = keyword.trim().toLowerCase(); + + if (!content.includes(keywordLower)) continue; + + switch (token.type) { + case 'heading_open': + const level = parseInt(token.tag.charAt(1)); + const headingContent = tokens[i + 1].content; + results.push({ + type: 'heading', + content: headingContent, + level + }); + break; + + case 'paragraph_open': + const paragraphContent = tokens[i + 1].content; + results.push({ + type: 'paragraph', + content: paragraphContent + }); + break; + + case 'fence': + case 'code_block': + results.push({ + type: 'code', + content: token.content + }); + break; + + case 'bullet_list_open': + case 'ordered_list_open': + let listItems = []; + i++; + while (i < tokens.length && !tokens[i].type.includes('list_close')) { + if (tokens[i].type === 'list_item_open') { + i++; + if (tokens[i].content) { + listItems.push(tokens[i].content); + } + } + i++; + } + if (listItems.length > 0) { + results.push({ + type: 'list', + content: listItems.join('\n') + }); + } + break; + + default: + if (token.content) { + results.push({ + type: 'other', + content: token.content + }); + } + break; + } + } + + return results.length > 0 ? results : null; +}; -// Recursive function to process nodes const processNode = (node: Node, keyword: string): boolean => { const keywordRegex = new RegExp(`(${keyword})`, "gi"); @@ -57,7 +204,6 @@ const processNode = (node: Node, keyword: string): boolean => { const tempContainer = document.createElement("div"); tempContainer.innerHTML = highlightedHTML; - // Replace the text node with highlighted content while (tempContainer.firstChild) { node.parentNode?.insertBefore(tempContainer.firstChild, node); } @@ -84,4 +230,27 @@ const processNode = (node: Node, keyword: string): boolean => { } return false; -}; \ No newline at end of file +}; + +const markdownString = ` +# Title +This is a paragraph. + +## Subtitle +- Bullet item 1 +* Bullet item 2 +1. Numbered item 1 +2. Numbered item 2 + +\`\`\`javascript +const hello = "world"; +console.log(hello); +// This is a multi-line +// code block +\`\`\` + +Regular text after code block +`; + +const parsed = processMarkdownString(markdownString); +console.log(parsed); From 2420af3b6d3c4c98a75b3f624e204c9514cac7d5 Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Fri, 27 Dec 2024 16:19:33 +0530 Subject: [PATCH 50/66] (feat:Search) highlight keywords on searching --- extensions/react-widget/src/utils/helper.ts | 251 +++++++------------- 1 file changed, 84 insertions(+), 167 deletions(-) diff --git a/extensions/react-widget/src/utils/helper.ts b/extensions/react-widget/src/utils/helper.ts index 511b39fc..13d2bf7a 100644 --- a/extensions/react-widget/src/utils/helper.ts +++ b/extensions/react-widget/src/utils/helper.ts @@ -1,5 +1,3 @@ -import MarkdownIt from "markdown-it"; -import DOMPurify from "dompurify"; export const getOS = () => { const platform = window.navigator.platform; const userAgent = window.navigator.userAgent || window.navigator.vendor; @@ -27,211 +25,130 @@ export const getOS = () => { return 'other'; }; -interface MarkdownElement { - type: 'heading' | 'paragraph' | 'code' | 'list' | 'other'; - content: string; - level?: number; -} interface ParsedElement { content: string; tag: string; } -export const processMarkdownString = (markdown: string): ParsedElement[] => { - const result: ParsedElement[] = []; +export const processMarkdownString = (markdown: string, keyword?: string): ParsedElement[] => { const lines = markdown.trim().split('\n'); - + const keywordLower = keyword?.toLowerCase(); + + const escapeRegExp = (str: string) => str.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); + const escapedKeyword = keyword ? escapeRegExp(keyword) : ''; + const keywordRegex = keyword ? new RegExp(`(${escapedKeyword})`, 'gi') : null; + let isInCodeBlock = false; - let currentCodeBlock = ''; + let codeBlockContent: string[] = []; + let matchingLines: ParsedElement[] = []; + let firstLine: ParsedElement | null = null; for (let i = 0; i < lines.length; i++) { const trimmedLine = lines[i].trim(); if (!trimmedLine) continue; + // Handle code block start/end if (trimmedLine.startsWith('```')) { - if (isInCodeBlock) { - if (currentCodeBlock.trim()) { - result.push({ - content: currentCodeBlock.trim(), - tag: 'code' - }); - } - currentCodeBlock = ''; - isInCodeBlock = false; - } else { + if (!isInCodeBlock) { + // Start of code block isInCodeBlock = true; + codeBlockContent = []; + } else { + // End of code block - process the collected content + isInCodeBlock = false; + const codeContent = codeBlockContent.join('\n'); + const parsedElement: ParsedElement = { + content: codeContent, + tag: 'code' + }; + + if (!firstLine) { + firstLine = parsedElement; + } + + if (keywordLower && codeContent.toLowerCase().includes(keywordLower)) { + parsedElement.content = parsedElement.content.replace(keywordRegex!, '$1'); + matchingLines.push(parsedElement); + } } continue; } + // Collect code block content if (isInCodeBlock) { - currentCodeBlock += trimmedLine + '\n'; + codeBlockContent.push(trimmedLine); continue; } + let parsedElement: ParsedElement | null = null; + const headingMatch = trimmedLine.match(/^(#{1,6})\s+(.+)$/); - if (headingMatch) { - result.push({ - content: headingMatch[2], - tag: 'heading' - }); - continue; - } - const bulletMatch = trimmedLine.match(/^[-*]\s+(.+)$/); - if (bulletMatch) { - result.push({ - content: bulletMatch[1], - tag: 'bulletList' - }); - continue; - } - const numberedMatch = trimmedLine.match(/^\d+\.\s+(.+)$/); - if (numberedMatch) { - result.push({ - content: numberedMatch[1], + + let content = trimmedLine; + + if (headingMatch) { + content = headingMatch[2]; + parsedElement = { + content: content, + tag: 'heading' + }; + } else if (bulletMatch) { + content = bulletMatch[1]; + parsedElement = { + content: content, + tag: 'bulletList' + }; + } else if (numberedMatch) { + content = numberedMatch[1]; + parsedElement = { + content: content, tag: 'numberedList' - }); - continue; + }; + } else { + parsedElement = { + content: content, + tag: 'text' + }; } - result.push({ - content: trimmedLine, - tag: 'text' - }); + if (!firstLine) { + firstLine = parsedElement; + } + + if (keywordLower && parsedElement.content.toLowerCase().includes(keywordLower)) { + parsedElement.content = parsedElement.content.replace(keywordRegex!, '$1'); + matchingLines.push(parsedElement); + } } - if (isInCodeBlock && currentCodeBlock.trim()) { - result.push({ - content: currentCodeBlock.trim(), + if (isInCodeBlock && codeBlockContent.length > 0) { + const codeContent = codeBlockContent.join('\n'); + const parsedElement: ParsedElement = { + content: codeContent, tag: 'code' - }); - } + }; - return result; -}; + if (!firstLine) { + firstLine = parsedElement; + } -export const preprocessSearchResultsToHTML = (text: string, keyword: string): MarkdownElement[] | null => { - const md = new MarkdownIt(); - const tokens = md.parse(text, {}); - const results: MarkdownElement[] = []; - - for (let i = 0; i < tokens.length; i++) { - const token = tokens[i]; - - if (token.type.endsWith('_close') || !token.content) continue; - - const content = token.content.toLowerCase(); - const keywordLower = keyword.trim().toLowerCase(); - - if (!content.includes(keywordLower)) continue; - - switch (token.type) { - case 'heading_open': - const level = parseInt(token.tag.charAt(1)); - const headingContent = tokens[i + 1].content; - results.push({ - type: 'heading', - content: headingContent, - level - }); - break; - - case 'paragraph_open': - const paragraphContent = tokens[i + 1].content; - results.push({ - type: 'paragraph', - content: paragraphContent - }); - break; - - case 'fence': - case 'code_block': - results.push({ - type: 'code', - content: token.content - }); - break; - - case 'bullet_list_open': - case 'ordered_list_open': - let listItems = []; - i++; - while (i < tokens.length && !tokens[i].type.includes('list_close')) { - if (tokens[i].type === 'list_item_open') { - i++; - if (tokens[i].content) { - listItems.push(tokens[i].content); - } - } - i++; - } - if (listItems.length > 0) { - results.push({ - type: 'list', - content: listItems.join('\n') - }); - } - break; - - default: - if (token.content) { - results.push({ - type: 'other', - content: token.content - }); - } - break; + if (keywordLower && codeContent.toLowerCase().includes(keywordLower)) { + parsedElement.content = parsedElement.content.replace(keywordRegex!, '$1'); + matchingLines.push(parsedElement); } } - return results.length > 0 ? results : null; -}; - -const processNode = (node: Node, keyword: string): boolean => { - - const keywordRegex = new RegExp(`(${keyword})`, "gi"); - if (node.nodeType === Node.TEXT_NODE) { - const textContent = node.textContent || ""; - - if (textContent.toLowerCase().includes(keyword.toLowerCase())) { - const highlightedHTML = textContent.replace( - keywordRegex, - `$1` - ); - const tempContainer = document.createElement("div"); - tempContainer.innerHTML = highlightedHTML; - - while (tempContainer.firstChild) { - node.parentNode?.insertBefore(tempContainer.firstChild, node); - } - node.parentNode?.removeChild(node); - - return true; - } - - return false; - } else if (node.nodeType === Node.ELEMENT_NODE) { - - const children = Array.from(node.childNodes); - let hasKeyword = false; - - children.forEach((child) => { - if (!processNode(child, keyword)) { - node.removeChild(child); - } else { - hasKeyword = true; - } - }); - - return hasKeyword; + if (keywordLower && matchingLines.length > 0) { + return matchingLines; } - return false; + return firstLine ? [firstLine] : []; }; + const markdownString = ` # Title This is a paragraph. @@ -252,5 +169,5 @@ console.log(hello); Regular text after code block `; -const parsed = processMarkdownString(markdownString); +const parsed = processMarkdownString(markdownString, 'world'); console.log(parsed); From 5ddf9bd7ecb36e3ca1cf39c3ecff69bd3fad482e Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Fri, 27 Dec 2024 17:40:04 +0530 Subject: [PATCH 51/66] (feat:search) handle blockquotes in markdown --- extensions/react-widget/src/utils/helper.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/extensions/react-widget/src/utils/helper.ts b/extensions/react-widget/src/utils/helper.ts index 13d2bf7a..ac257c91 100644 --- a/extensions/react-widget/src/utils/helper.ts +++ b/extensions/react-widget/src/utils/helper.ts @@ -86,6 +86,7 @@ export const processMarkdownString = (markdown: string, keyword?: string): Parse const headingMatch = trimmedLine.match(/^(#{1,6})\s+(.+)$/); const bulletMatch = trimmedLine.match(/^[-*]\s+(.+)$/); const numberedMatch = trimmedLine.match(/^\d+\.\s+(.+)$/); + const blockquoteMatch = trimmedLine.match(/^>+\s*(.+)$/); // Updated regex to handle multiple '>' symbols let content = trimmedLine; @@ -107,6 +108,12 @@ export const processMarkdownString = (markdown: string, keyword?: string): Parse content: content, tag: 'numberedList' }; + } else if (blockquoteMatch) { + content = blockquoteMatch[1]; + parsedElement = { + content: content, + tag: 'blockquote' + }; } else { parsedElement = { content: content, From e45648b389544fa59f24f65ea1d58efc58c35ef4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Dec 2024 20:17:15 +0000 Subject: [PATCH 52/66] build(deps): bump langsmith from 0.2.3 to 0.2.6 in /application Bumps [langsmith](https://github.com/langchain-ai/langsmith-sdk) from 0.2.3 to 0.2.6. - [Release notes](https://github.com/langchain-ai/langsmith-sdk/releases) - [Commits](https://github.com/langchain-ai/langsmith-sdk/compare/v0.2.3...v0.2.6) --- updated-dependencies: - dependency-name: langsmith dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- application/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/requirements.txt b/application/requirements.txt index 08990ab2..754fb271 100644 --- a/application/requirements.txt +++ b/application/requirements.txt @@ -33,7 +33,7 @@ langchain-community==0.3.13 langchain-core==0.3.28 langchain-openai==0.2.14 langchain-text-splitters==0.3.4 -langsmith==0.2.3 +langsmith==0.2.6 lazy-object-proxy==1.10.0 lxml==5.3.0 markupsafe==2.1.5 From e42fc97d03e88a31f676ad8daffc912b6210e7da Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Dec 2024 20:17:25 +0000 Subject: [PATCH 53/66] build(deps): bump celery from 5.3.6 to 5.4.0 in /application Bumps [celery](https://github.com/celery/celery) from 5.3.6 to 5.4.0. - [Release notes](https://github.com/celery/celery/releases) - [Changelog](https://github.com/celery/celery/blob/main/Changelog.rst) - [Commits](https://github.com/celery/celery/compare/v5.3.6...v5.4.0) --- updated-dependencies: - dependency-name: celery dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- application/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/requirements.txt b/application/requirements.txt index 08990ab2..41297624 100644 --- a/application/requirements.txt +++ b/application/requirements.txt @@ -1,7 +1,7 @@ anthropic==0.40.0 boto3==1.34.153 beautifulsoup4==4.12.3 -celery==5.3.6 +celery==5.4.0 dataclasses-json==0.6.7 docx2txt==0.8 duckduckgo-search==6.3.0 From b4d77080e8a6f6c96aba6546bfc69f196c76286e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Dec 2024 20:22:47 +0000 Subject: [PATCH 54/66] build(deps): bump i18next-browser-languagedetector in /frontend Bumps [i18next-browser-languagedetector](https://github.com/i18next/i18next-browser-languageDetector) from 8.0.0 to 8.0.2. - [Changelog](https://github.com/i18next/i18next-browser-languageDetector/blob/master/CHANGELOG.md) - [Commits](https://github.com/i18next/i18next-browser-languageDetector/compare/v8.0.0...v8.0.2) --- updated-dependencies: - dependency-name: i18next-browser-languagedetector dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 9 ++++----- frontend/package.json | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 9a3dbf31..7ad503c4 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -11,7 +11,7 @@ "@reduxjs/toolkit": "^2.2.7", "chart.js": "^4.4.4", "i18next": "^24.2.0", - "i18next-browser-languagedetector": "^8.0.0", + "i18next-browser-languagedetector": "^8.0.2", "prop-types": "^15.8.1", "react": "^18.2.0", "react-chartjs-2": "^5.2.0", @@ -4956,10 +4956,9 @@ } }, "node_modules/i18next-browser-languagedetector": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.0.0.tgz", - "integrity": "sha512-zhXdJXTTCoG39QsrOCiOabnWj2jecouOqbchu3EfhtSHxIB5Uugnm9JaizenOy39h7ne3+fLikIjeW88+rgszw==", - "license": "MIT", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.0.2.tgz", + "integrity": "sha512-shBvPmnIyZeD2VU5jVGIOWP7u9qNG3Lj7mpaiPFpbJ3LVfHZJvVzKR4v1Cb91wAOFpNw442N+LGPzHOHsten2g==", "dependencies": { "@babel/runtime": "^7.23.2" } diff --git a/frontend/package.json b/frontend/package.json index ff98e94c..bb8ff171 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -22,7 +22,7 @@ "@reduxjs/toolkit": "^2.2.7", "chart.js": "^4.4.4", "i18next": "^24.2.0", - "i18next-browser-languagedetector": "^8.0.0", + "i18next-browser-languagedetector": "^8.0.2", "prop-types": "^15.8.1", "react": "^18.2.0", "react-chartjs-2": "^5.2.0", From 8724c12c11e081aba4976f683c81e7d4e071b72b Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Tue, 31 Dec 2024 15:30:24 +0530 Subject: [PATCH 55/66] (feat:search) new UI --- .../react-widget/src/components/SearchBar.tsx | 442 ++++++++++++------ extensions/react-widget/src/utils/helper.ts | 31 +- 2 files changed, 308 insertions(+), 165 deletions(-) diff --git a/extensions/react-widget/src/components/SearchBar.tsx b/extensions/react-widget/src/components/SearchBar.tsx index 42262e08..5982f6f6 100644 --- a/extensions/react-widget/src/components/SearchBar.tsx +++ b/extensions/react-widget/src/components/SearchBar.tsx @@ -1,11 +1,20 @@ -import React from 'react' -import styled, { ThemeProvider } from 'styled-components'; +import React, { useRef } from 'react'; +import styled, { ThemeProvider, createGlobalStyle } from 'styled-components'; import { WidgetCore } from './DocsGPTWidget'; import { SearchBarProps } from '@/types'; -import { getSearchResults } from '../requests/searchAPI' +import { getSearchResults } from '../requests/searchAPI'; import { Result } from '@/types'; import MarkdownIt from 'markdown-it'; -import { getOS, preprocessSearchResultsToHTML } from '../utils/helper' +import { getOS, processMarkdownString } from '../utils/helper'; +import DOMPurify from 'dompurify'; +import { + CodeIcon, + TextAlignLeftIcon, + HeadingIcon, + ReaderIcon, + ListBulletIcon, + QuoteIcon +} from '@radix-ui/react-icons'; const themes = { dark: { bg: '#000', @@ -33,12 +42,20 @@ const themes = { } } +const GlobalStyle = createGlobalStyle` + .highlight { + color:#007EE6; + } +`; + const Main = styled.div` - all:initial; - font-family: sans-serif; + all: initial; + * { + font-family: 'Geist', sans-serif; + } ` -const TextField = styled.input<{ inputWidth: string }>` - padding: 6px 6px; +const SearchButton = styled.button<{ inputWidth: string }>` + padding: 6px 6px; width: ${({ inputWidth }) => inputWidth}; border-radius: 8px; display: inline; @@ -50,14 +67,15 @@ const TextField = styled.input<{ inputWidth: string }>` -moz-appearance: none; appearance: none; transition: background-color 128ms linear; + text-align: left; &:focus { - outline: none; - box-shadow: - 0px 0px 0px 2px rgba(0, 109, 199), - 0px 0px 6px rgb(0, 90, 163), - 0px 2px 6px rgba(0, 0, 0, 0.1) ; - background-color: ${props => props.theme.primary.bg}; - } + outline: none; + box-shadow: + 0px 0px 0px 2px rgba(0, 109, 199), + 0px 0px 6px rgb(0, 90, 163), + 0px 2px 6px rgba(0, 0, 0, 0.1); + background-color: ${props => props.theme.primary.bg}; + } ` const Container = styled.div` @@ -65,51 +83,122 @@ const Container = styled.div` display: inline-block; ` const SearchResults = styled.div` - position: absolute; - display: block; + position: fixed; + display: flex; + flex-direction: column; background-color: ${props => props.theme.primary.bg}; - border: 1px solid rgba(0, 0, 0, .1); + border: 1px solid ${props => props.theme.secondary.text}; border-radius: 12px; padding: 8px; - width: 576px; - min-width: 96%; + width: 792px; + max-width: 90vw; + height: 70vh; z-index: 100; - height: 25vh; - overflow-y: auto; - top: 32px; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); color: ${props => props.theme.primary.text}; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.1); + backdrop-filter: blur(16px); + box-sizing: border-box; + + @media only screen and (max-width: 768px) { + height: 80vh; + width: 90vw; + } +`; + +const SearchResultsScroll = styled.div` + flex: 1; + overflow-y: auto; + overflow-x: hidden; scrollbar-color: lab(48.438 0 0 / 0.4) rgba(0, 0, 0, 0); scrollbar-gutter: stable; scrollbar-width: thin; - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05), 0 2px 4px rgba(0, 0, 0, 0.1); - backdrop-filter: blur(16px); - @media only screen and (max-width: 768px) { - max-height: 100vh; - max-width: 80vw; - overflow: auto; + padding: 0 16px; +`; + +const ResultHeader = styled.div` + display: flex; + align-items: center; +`; + +const IconContainer = styled.div` + display: flex; + gap: 20px; + align-items: center; + margin-right: 20px; + position: relative; + + &::after { + content: ''; + position: absolute; + top: 24px; + bottom: 0; + left: 50%; + width: 1px; + background-color: ${props => props.theme.secondary.text}; } -` +`; + +const IconTitleWrapper = styled.div` + display: flex; + align-items: center; + gap: 8px; +`; + const Title = styled.h3` - font-size: 14px; + font-size: 17.32px; + font-weight: 400; color: ${props => props.theme.primary.text}; - opacity: 0.8; - padding-bottom: 6px; - font-weight: 600; - text-transform: uppercase; - border-bottom: 1px solid ${(props) => props.theme.secondary.text}; -` + margin: 0; +`; +const ContentWrapper = styled.div` + display: flex; + flex-direction: column; + gap: 8px; // Reduced from 1 +`; const Content = styled.div` - font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; + display: flex; + margin-left: 10px; + flex-direction: column; + gap: 8px; + padding: 4px 0 0px 20px; + font-size: 17.32px; + color: ${props => props.theme.primary.text}; + line-height: 1.6; + border-left: 2px solid #585858; ` +const ContentSegment = styled.div` + display: flex; + align-items: flex-start; + gap: 8px; + padding-right: 16px; +` +const TextContent = styled.div` + display: flex; + flex-direction: column; + gap: 16px; + flex: 1; + padding-top: 3px; +`; + const ResultWrapper = styled.div` - padding: 4px 8px 4px 8px; - border-radius: 8px; + display: flex; + align-items: flex-start; + width: 100%; + box-sizing: border-box; + padding: 12px 16px 0 16px; cursor: pointer; - &.contains-source:hover{ + margin-bottom: 8px; + background-color: ${props => props.theme.primary.bg}; + transition: background-color 0.2s; + + &.contains-source:hover { background-color: rgba(0, 92, 197, 0.15); ${Title} { - color: rgb(0, 126, 230); - } + color: rgb(0, 126, 230); + } } ` const Markdown = styled.div` @@ -200,19 +289,71 @@ const NoResults = styled.div` font-size: 1rem; color: #888; `; -const InfoButton = styled.button` - cursor: pointer; - padding: 10px 4px 10px 4px; - display: block; +const AskAIButton = styled.button` + display: flex; + align-items: center; + justify-content: flex-start; + gap: 12px; width: 100%; - color: inherit; + box-sizing: border-box; + height: 50px; + padding: 8px 24px; + border: none; border-radius: 6px; - background-color: ${(props) => props.theme.bg}; - text-align: center; + background-color: ${props => props.theme.secondary.bg}; + color: ${props => props.theme.bg === '#000' ? '#EDEDED' : props.theme.secondary.text}; + cursor: pointer; + transition: background-color 0.2s, box-shadow 0.2s; + font-size: 18px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + margin-bottom: 16px; + + &:hover { + opacity: 0.8; + } +` +const SearchHeader = styled.div` + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 12px; + padding-bottom: 12px; + border-bottom: 1px solid ${props => props.theme.secondary.text}; +` + +const TextField = styled.input` + width: calc(100% - 32px); + margin: 0 16px; + padding: 12px 16px; + border: none; + background-color: transparent; + color: #EDEDED; + font-size: 22px; + font-weight: 400; + outline: none; + + &:focus { + border-color: none; + } +` + +const EscapeInstruction = styled.kbd` + display: flex; + align-items: center; + justify-content: center; + margin: 12px 16px 0; + padding: 4px 8px; + border-radius: 4px; + background-color: transparent; + border: 1px solid ${props => props.theme.secondary.text}; + color: ${props => props.theme.secondary.text}; font-size: 14px; - margin-bottom: 8px; - border:1px solid ${(props) => props.theme.secondary.text}; - + white-space: nowrap; + cursor: pointer; + width: fit-content; + &:hover { + background-color: rgba(255, 255, 255, 0.1); + } ` export const SearchBar = ({ apiKey = "74039c6d-bff7-44ce-ae55-2973cbf13837", @@ -226,47 +367,48 @@ export const SearchBar = ({ const [isWidgetOpen, setIsWidgetOpen] = React.useState(false); const inputRef = React.useRef(null); const containerRef = React.useRef(null); - const [isResultVisible, setIsResultVisible] = React.useState(true); + const [isResultVisible, setIsResultVisible] = React.useState(false); const [results, setResults] = React.useState([]); const debounceTimeout = React.useRef | null>(null); - const abortControllerRef = React.useRef(null) + const abortControllerRef = React.useRef(null); const browserOS = getOS(); - function isTouchDevice() { - return 'ontouchstart' in window; - } - const isTouch = isTouchDevice(); + const isTouch = 'ontouchstart' in window; + const md = new MarkdownIt(); + const getKeyboardInstruction = () => { - if (isResultVisible) return "Enter" - if (browserOS === 'mac') - return "⌘ K" - else - return "Ctrl K" - } + if (isResultVisible) return "Enter"; + return browserOS === 'mac' ? '⌘ + K' : 'Ctrl + K'; + }; + React.useEffect(() => { - const handleFocusSearch = (event: KeyboardEvent) => { + const handleClickOutside = (event: MouseEvent) => { + if (containerRef.current && !containerRef.current.contains(event.target as Node)) { + setIsResultVisible(false); + } + }; + + const handleKeyDown = (event: KeyboardEvent) => { if ( ((browserOS === 'win' || browserOS === 'linux') && event.ctrlKey && event.key === 'k') || (browserOS === 'mac' && event.metaKey && event.key === 'k') ) { event.preventDefault(); inputRef.current?.focus(); - } - } - const handleClickOutside = (event: MouseEvent) => { - if ( - containerRef.current && - !containerRef.current.contains(event.target as Node) - ) { + setIsResultVisible(true); + } else if (event.key === 'Escape') { setIsResultVisible(false); } }; + + document.addEventListener('mousedown', handleClickOutside); - document.addEventListener('keydown', handleFocusSearch); + document.addEventListener('keydown', handleKeyDown); return () => { - setIsResultVisible(true); document.removeEventListener('mousedown', handleClickOutside); + document.removeEventListener('keydown', handleKeyDown); }; - }, []) + }, []); + React.useEffect(() => { if (!input) { setResults([]); @@ -291,8 +433,6 @@ export const SearchBar = ({ }, 500); return () => { - console.log(results); - abortController.abort(); clearTimeout(debounceTimeout.current ?? undefined); }; @@ -304,73 +444,105 @@ export const SearchBar = ({ openWidget(); } }; + const openWidget = () => { setIsWidgetOpen(true); - setIsResultVisible(false) - } + setIsResultVisible(false); + }; + const handleClose = () => { setIsWidgetOpen(false); - } - const md = new MarkdownIt(); + }; + return (
+ - setIsResultVisible(true)} inputWidth={width} - onFocus={() => setIsResultVisible(true)} - ref={inputRef} - onSubmit={() => setIsWidgetOpen(true)} - onKeyDown={(e) => handleKeyDown(e)} - placeholder={placeholder} - value={input} - onChange={(e) => setInput(e.target.value)} - /> + > + Search here + { - input.length > 0 && isResultVisible && ( + isResultVisible && ( - - { - isTouch ? - "Ask the AI" : - <> - Press Enter to ask the AI - - } - - {!loading ? - (results.length > 0 ? - results.map((res, key) => { - const containsSource = res.source !== 'local'; - const filteredResults = preprocessSearchResultsToHTML(res.text,input) - if (filteredResults) - return ( - { - if (!containsSource) return; - window.open(res.source, '_blank', 'noopener, noreferrer') - }} - className={containsSource ? "contains-source" : ""}> - {res.title} - - - - - ) - else { - setResults((prevItems) => prevItems.filter((_, index) => index !== key)); - } - }) - : - No results - ) - : - - } + + setInput(e.target.value)} + onKeyDown={(e) => handleKeyDown(e)} + placeholder={placeholder} + autoFocus + /> + setIsResultVisible(false)}> + Esc + + + + DocsGPT + Ask the AI + + + {!loading ? ( + results.length > 0 ? ( + results.map((res, key) => { + const containsSource = res.source !== 'local'; + const processedResults = processMarkdownString(res.text, input); + if (processedResults) + return ( + { + if (!containsSource) return; + window.open(res.source, '_blank', 'noopener, noreferrer'); + }} + > +
+ + + + {res.title} + + + {processedResults.map((element, index) => ( + + + {element.tag === 'code' && } + {(element.tag === 'bulletList' || element.tag === 'numberedList') && } + {element.tag === 'text' && } + {element.tag === 'heading' && } + {element.tag === 'blockquote' && } + +
+ + ))} + + +
+ + ); + return null; + }) + ) : ( + No results found + ) + ) : ( + + )} + ) } @@ -402,4 +574,4 @@ export const SearchBar = ({
) -} \ No newline at end of file +} diff --git a/extensions/react-widget/src/utils/helper.ts b/extensions/react-widget/src/utils/helper.ts index ac257c91..9f92fdcb 100644 --- a/extensions/react-widget/src/utils/helper.ts +++ b/extensions/react-widget/src/utils/helper.ts @@ -25,7 +25,6 @@ export const getOS = () => { return 'other'; }; - interface ParsedElement { content: string; tag: string; @@ -48,14 +47,11 @@ export const processMarkdownString = (markdown: string, keyword?: string): Parse const trimmedLine = lines[i].trim(); if (!trimmedLine) continue; - // Handle code block start/end if (trimmedLine.startsWith('```')) { if (!isInCodeBlock) { - // Start of code block isInCodeBlock = true; codeBlockContent = []; } else { - // End of code block - process the collected content isInCodeBlock = false; const codeContent = codeBlockContent.join('\n'); const parsedElement: ParsedElement = { @@ -75,7 +71,6 @@ export const processMarkdownString = (markdown: string, keyword?: string): Parse continue; } - // Collect code block content if (isInCodeBlock) { codeBlockContent.push(trimmedLine); continue; @@ -86,7 +81,7 @@ export const processMarkdownString = (markdown: string, keyword?: string): Parse const headingMatch = trimmedLine.match(/^(#{1,6})\s+(.+)$/); const bulletMatch = trimmedLine.match(/^[-*]\s+(.+)$/); const numberedMatch = trimmedLine.match(/^\d+\.\s+(.+)$/); - const blockquoteMatch = trimmedLine.match(/^>+\s*(.+)$/); // Updated regex to handle multiple '>' symbols + const blockquoteMatch = trimmedLine.match(/^>+\s*(.+)$/); let content = trimmedLine; @@ -154,27 +149,3 @@ export const processMarkdownString = (markdown: string, keyword?: string): Parse return firstLine ? [firstLine] : []; }; - - -const markdownString = ` -# Title -This is a paragraph. - -## Subtitle -- Bullet item 1 -* Bullet item 2 -1. Numbered item 1 -2. Numbered item 2 - -\`\`\`javascript -const hello = "world"; -console.log(hello); -// This is a multi-line -// code block -\`\`\` - -Regular text after code block -`; - -const parsed = processMarkdownString(markdownString, 'world'); -console.log(parsed); From 598c7a5d76829b71dc24fdaa6500407a81b1d5eb Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Tue, 31 Dec 2024 16:25:44 +0530 Subject: [PATCH 56/66] (feat:search) load geist font --- .../react-widget/src/components/SearchBar.tsx | 40 ++++++------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/extensions/react-widget/src/components/SearchBar.tsx b/extensions/react-widget/src/components/SearchBar.tsx index 5982f6f6..b887c17d 100644 --- a/extensions/react-widget/src/components/SearchBar.tsx +++ b/extensions/react-widget/src/components/SearchBar.tsx @@ -1,4 +1,4 @@ -import React, { useRef } from 'react'; +import React from 'react'; import styled, { ThemeProvider, createGlobalStyle } from 'styled-components'; import { WidgetCore } from './DocsGPTWidget'; import { SearchBarProps } from '@/types'; @@ -48,11 +48,16 @@ const GlobalStyle = createGlobalStyle` } `; +const loadGeistFont = () => { + const link = document.createElement('link'); + link.href = 'https://fonts.googleapis.com/css2?family=Geist:wght@100..900&display=swap'; // Replace with the actual CDN URL + link.rel = 'stylesheet'; + document.head.appendChild(link); +}; + const Main = styled.div` all: initial; - * { - font-family: 'Geist', sans-serif; - } + font-family: 'Geist', sans-serif; ` const SearchButton = styled.button<{ inputWidth: string }>` padding: 6px 6px; @@ -118,29 +123,6 @@ const SearchResultsScroll = styled.div` padding: 0 16px; `; -const ResultHeader = styled.div` - display: flex; - align-items: center; -`; - -const IconContainer = styled.div` - display: flex; - gap: 20px; - align-items: center; - margin-right: 20px; - position: relative; - - &::after { - content: ''; - position: absolute; - top: 24px; - bottom: 0; - left: 50%; - width: 1px; - background-color: ${props => props.theme.secondary.text}; - } -`; - const IconTitleWrapper = styled.div` display: flex; align-items: center; @@ -156,7 +138,7 @@ const Title = styled.h3` const ContentWrapper = styled.div` display: flex; flex-direction: column; - gap: 8px; // Reduced from 1 + gap: 8px; `; const Content = styled.div` display: flex; @@ -192,6 +174,7 @@ const ResultWrapper = styled.div` cursor: pointer; margin-bottom: 8px; background-color: ${props => props.theme.primary.bg}; + font-family: 'Geist',sans-serif; transition: background-color 0.2s; &.contains-source:hover { @@ -381,6 +364,7 @@ export const SearchBar = ({ }; React.useEffect(() => { + loadGeistFont() const handleClickOutside = (event: MouseEvent) => { if (containerRef.current && !containerRef.current.contains(event.target as Node)) { setIsResultVisible(false); From 2f33a46e89f76aa3e18807264ba87c006c1f4f53 Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Tue, 31 Dec 2024 17:33:06 +0530 Subject: [PATCH 57/66] (feat:search/UX) enhance function --- .../react-widget/src/components/SearchBar.tsx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/extensions/react-widget/src/components/SearchBar.tsx b/extensions/react-widget/src/components/SearchBar.tsx index b887c17d..18ac88a8 100644 --- a/extensions/react-widget/src/components/SearchBar.tsx +++ b/extensions/react-widget/src/components/SearchBar.tsx @@ -50,7 +50,7 @@ const GlobalStyle = createGlobalStyle` const loadGeistFont = () => { const link = document.createElement('link'); - link.href = 'https://fonts.googleapis.com/css2?family=Geist:wght@100..900&display=swap'; // Replace with the actual CDN URL + link.href = 'https://fonts.googleapis.com/css2?family=Geist:wght@100..900&display=swap'; link.rel = 'stylesheet'; document.head.appendChild(link); }; @@ -61,6 +61,7 @@ const Main = styled.div` ` const SearchButton = styled.button<{ inputWidth: string }>` padding: 6px 6px; + font-family: inherit; width: ${({ inputWidth }) => inputWidth}; border-radius: 8px; display: inline; @@ -93,11 +94,11 @@ const SearchResults = styled.div` flex-direction: column; background-color: ${props => props.theme.primary.bg}; border: 1px solid ${props => props.theme.secondary.text}; - border-radius: 12px; - padding: 8px; + border-radius: 15px; + padding: 8px 0px 8px 0px; width: 792px; max-width: 90vw; - height: 70vh; + height: 415px; z-index: 100; left: 50%; top: 50%; @@ -277,7 +278,8 @@ const AskAIButton = styled.button` align-items: center; justify-content: flex-start; gap: 12px; - width: 100%; + width: calc(100% - 32px); + margin: 0 16px 16px 16px; box-sizing: border-box; height: 50px; padding: 8px 24px; @@ -289,7 +291,6 @@ const AskAIButton = styled.button` transition: background-color 0.2s, box-shadow 0.2s; font-size: 18px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - margin-bottom: 16px; &:hover { opacity: 0.8; @@ -331,6 +332,7 @@ const EscapeInstruction = styled.kbd` border: 1px solid ${props => props.theme.secondary.text}; color: ${props => props.theme.secondary.text}; font-size: 14px; + font-family: 'Geist', sans-serif; white-space: nowrap; cursor: pointer; width: fit-content; @@ -356,8 +358,7 @@ export const SearchBar = ({ const abortControllerRef = React.useRef(null); const browserOS = getOS(); const isTouch = 'ontouchstart' in window; - const md = new MarkdownIt(); - + const getKeyboardInstruction = () => { if (isResultVisible) return "Enter"; return browserOS === 'mac' ? '⌘ + K' : 'Ctrl + K'; @@ -436,6 +437,7 @@ export const SearchBar = ({ const handleClose = () => { setIsWidgetOpen(false); + setIsResultVisible(true); }; return ( From 0f611eb87bf038154afb20b31350027eecf23ae0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 Dec 2024 14:52:21 +0000 Subject: [PATCH 58/66] build(deps): bump redis from 5.0.1 to 5.2.1 in /application Bumps [redis](https://github.com/redis/redis-py) from 5.0.1 to 5.2.1. - [Release notes](https://github.com/redis/redis-py/releases) - [Changelog](https://github.com/redis/redis-py/blob/master/CHANGES) - [Commits](https://github.com/redis/redis-py/compare/v5.0.1...v5.2.1) --- updated-dependencies: - dependency-name: redis dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- application/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/requirements.txt b/application/requirements.txt index b7660c42..6a318338 100644 --- a/application/requirements.txt +++ b/application/requirements.txt @@ -68,7 +68,7 @@ python-dateutil==2.9.0.post0 python-dotenv==1.0.1 python-pptx==1.0.2 qdrant-client==1.11.0 -redis==5.0.1 +redis==5.2.1 referencing==0.30.2 regex==2024.9.11 requests==2.32.3 From 3daeab5186e41289656b5a5d71b86c38a526a62e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 Dec 2024 14:57:29 +0000 Subject: [PATCH 59/66] build(deps): bump tiktoken from 0.7.0 to 0.8.0 in /application Bumps [tiktoken](https://github.com/openai/tiktoken) from 0.7.0 to 0.8.0. - [Release notes](https://github.com/openai/tiktoken/releases) - [Changelog](https://github.com/openai/tiktoken/blob/main/CHANGELOG.md) - [Commits](https://github.com/openai/tiktoken/compare/0.7.0...0.8.0) --- updated-dependencies: - dependency-name: tiktoken dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- application/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/requirements.txt b/application/requirements.txt index 6a318338..7c9f8101 100644 --- a/application/requirements.txt +++ b/application/requirements.txt @@ -74,7 +74,7 @@ regex==2024.9.11 requests==2.32.3 retry==0.9.2 sentence-transformers==3.3.1 -tiktoken==0.7.0 +tiktoken==0.8.0 tokenizers==0.21.0 torch==2.4.1 tqdm==4.66.5 From efb018d2b068a88dd99b8b32bb08f106517294e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 Dec 2024 14:58:26 +0000 Subject: [PATCH 60/66] build(deps): bump marshmallow from 3.22.0 to 3.23.2 in /application Bumps [marshmallow](https://github.com/marshmallow-code/marshmallow) from 3.22.0 to 3.23.2. - [Changelog](https://github.com/marshmallow-code/marshmallow/blob/dev/CHANGELOG.rst) - [Commits](https://github.com/marshmallow-code/marshmallow/compare/3.22.0...3.23.2) --- updated-dependencies: - dependency-name: marshmallow dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- application/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/requirements.txt b/application/requirements.txt index 7c9f8101..362fdd45 100644 --- a/application/requirements.txt +++ b/application/requirements.txt @@ -37,7 +37,7 @@ langsmith==0.2.6 lazy-object-proxy==1.10.0 lxml==5.3.0 markupsafe==2.1.5 -marshmallow==3.22.0 +marshmallow==3.23.2 mpmath==1.3.0 multidict==6.1.0 mypy-extensions==1.0.0 From 190f57171875bcbbc0f31ff8e9ceec5d5a733a37 Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Wed, 1 Jan 2025 15:14:03 +0530 Subject: [PATCH 61/66] (feat/search) exacting ui --- .../react-widget/src/components/SearchBar.tsx | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/extensions/react-widget/src/components/SearchBar.tsx b/extensions/react-widget/src/components/SearchBar.tsx index 18ac88a8..3a7f2ddd 100644 --- a/extensions/react-widget/src/components/SearchBar.tsx +++ b/extensions/react-widget/src/components/SearchBar.tsx @@ -93,7 +93,7 @@ const SearchResults = styled.div` display: flex; flex-direction: column; background-color: ${props => props.theme.primary.bg}; - border: 1px solid ${props => props.theme.secondary.text}; + border: 1px solid ${props => props.theme.secondary.bg}; border-radius: 15px; padding: 8px 0px 8px 0px; width: 792px; @@ -128,6 +128,10 @@ const IconTitleWrapper = styled.div` display: flex; align-items: center; gap: 8px; + + .element-icon{ + margin: 4px; + } `; const Title = styled.h3` @@ -143,10 +147,10 @@ const ContentWrapper = styled.div` `; const Content = styled.div` display: flex; - margin-left: 10px; + margin-left: 8px; flex-direction: column; gap: 8px; - padding: 4px 0 0px 20px; + padding: 4px 0px 0px 12px; font-size: 17.32px; color: ${props => props.theme.primary.text}; line-height: 1.6; @@ -158,13 +162,6 @@ const ContentSegment = styled.div` gap: 8px; padding-right: 16px; ` -const TextContent = styled.div` - display: flex; - flex-direction: column; - gap: 16px; - flex: 1; - padding-top: 3px; -`; const ResultWrapper = styled.div` display: flex; @@ -173,7 +170,6 @@ const ResultWrapper = styled.div` box-sizing: border-box; padding: 12px 16px 0 16px; cursor: pointer; - margin-bottom: 8px; background-color: ${props => props.theme.primary.bg}; font-family: 'Geist',sans-serif; transition: background-color 0.2s; @@ -302,7 +298,7 @@ const SearchHeader = styled.div` gap: 8px; margin-bottom: 12px; padding-bottom: 12px; - border-bottom: 1px solid ${props => props.theme.secondary.text}; + border-bottom: 1px solid ${props => props.theme.secondary.bg}; ` const TextField = styled.input` From 8b206b087c782aac7c944e9dedfb7ee74a0e554b Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Thu, 2 Jan 2025 19:36:07 +0530 Subject: [PATCH 62/66] (feat:search) adding buttonTextt prop, minor ui --- .../react-widget/src/components/SearchBar.tsx | 55 ++++++++----------- extensions/react-widget/src/types/index.ts | 7 ++- 2 files changed, 28 insertions(+), 34 deletions(-) diff --git a/extensions/react-widget/src/components/SearchBar.tsx b/extensions/react-widget/src/components/SearchBar.tsx index 3a7f2ddd..a9e43972 100644 --- a/extensions/react-widget/src/components/SearchBar.tsx +++ b/extensions/react-widget/src/components/SearchBar.tsx @@ -18,7 +18,7 @@ import { const themes = { dark: { bg: '#000', - text: '#fff', + text: '#EDEDED', primary: { text: "#FAFAFA", bg: '#111111' @@ -30,7 +30,7 @@ const themes = { }, light: { bg: '#fff', - text: '#000', + text: '#171717', primary: { text: "#222327", bg: "#fff" @@ -65,7 +65,7 @@ const SearchButton = styled.button<{ inputWidth: string }>` width: ${({ inputWidth }) => inputWidth}; border-radius: 8px; display: inline; - color: ${props => props.theme.primary.text}; + color: ${props => props.theme.secondary.text}; outline: none; border: none; background-color: ${props => props.theme.secondary.bg}; @@ -74,14 +74,6 @@ const SearchButton = styled.button<{ inputWidth: string }>` appearance: none; transition: background-color 128ms linear; text-align: left; - &:focus { - outline: none; - box-shadow: - 0px 0px 0px 2px rgba(0, 109, 199), - 0px 0px 6px rgb(0, 90, 163), - 0px 2px 6px rgba(0, 0, 0, 0.1); - background-color: ${props => props.theme.primary.bg}; - } ` const Container = styled.div` @@ -98,7 +90,7 @@ const SearchResults = styled.div` padding: 8px 0px 8px 0px; width: 792px; max-width: 90vw; - height: 415px; + height: 396px; z-index: 100; left: 50%; top: 50%; @@ -118,9 +110,9 @@ const SearchResultsScroll = styled.div` flex: 1; overflow-y: auto; overflow-x: hidden; - scrollbar-color: lab(48.438 0 0 / 0.4) rgba(0, 0, 0, 0); scrollbar-gutter: stable; scrollbar-width: thin; + scrollbar-color: #383838 transparent; padding: 0 16px; `; @@ -135,7 +127,7 @@ const IconTitleWrapper = styled.div` `; const Title = styled.h3` - font-size: 17.32px; + font-size: 15px; font-weight: 400; color: ${props => props.theme.primary.text}; margin: 0; @@ -151,7 +143,7 @@ const Content = styled.div` flex-direction: column; gap: 8px; padding: 4px 0px 0px 12px; - font-size: 17.32px; + font-size: 15px; color: ${props => props.theme.primary.text}; line-height: 1.6; border-left: 2px solid #585858; @@ -182,13 +174,13 @@ const ResultWrapper = styled.div` } ` const Markdown = styled.div` -line-height:20px; -font-size: 12px; +line-height:18px; +font-size: 11px; white-space: pre-wrap; pre { padding: 8px; width: 90%; - font-size: 12px; + font-size: 11px; border-radius: 6px; overflow-x: auto; background-color: #1B1C1F; @@ -196,7 +188,7 @@ white-space: pre-wrap; } h1,h2 { - font-size: 16px; + font-size: 14px; font-weight: 600; color: ${(props) => props.theme.text}; opacity: 0.8; @@ -204,20 +196,20 @@ white-space: pre-wrap; h3 { - font-size: 14px; + font-size: 12px; } p { margin: 0px; line-height: 1.35rem; - font-size: 12px; + font-size: 11px; } code:not(pre code) { border-radius: 6px; padding: 2px 2px; margin: 2px; - font-size: 10px; + font-size: 9px; display: inline; background-color: #646464; color: #fff ; @@ -266,7 +258,7 @@ const Loader = styled.div` const NoResults = styled.div` margin-top: 2rem; text-align: center; - font-size: 1rem; + font-size: 14px; color: #888; `; const AskAIButton = styled.button` @@ -282,10 +274,10 @@ const AskAIButton = styled.button` border: none; border-radius: 6px; background-color: ${props => props.theme.secondary.bg}; - color: ${props => props.theme.bg === '#000' ? '#EDEDED' : props.theme.secondary.text}; + color: ${props => props.theme.text}; cursor: pointer; transition: background-color 0.2s, box-shadow 0.2s; - font-size: 18px; + font-size: 16px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); &:hover { @@ -307,8 +299,8 @@ const TextField = styled.input` padding: 12px 16px; border: none; background-color: transparent; - color: #EDEDED; - font-size: 22px; + color: ${props => props.theme.text}; + font-size: 20px; font-weight: 400; outline: none; @@ -326,8 +318,8 @@ const EscapeInstruction = styled.kbd` border-radius: 4px; background-color: transparent; border: 1px solid ${props => props.theme.secondary.text}; - color: ${props => props.theme.secondary.text}; - font-size: 14px; + color: ${props => props.theme.text}; + font-size: 12px; font-family: 'Geist', sans-serif; white-space: nowrap; cursor: pointer; @@ -341,7 +333,8 @@ export const SearchBar = ({ apiHost = "https://gptcloud.arc53.com", theme = "dark", placeholder = "Search or Ask AI...", - width = "256px" + width = "256px", + buttonText = "Search here" }: SearchBarProps) => { const [input, setInput] = React.useState(""); const [loading, setLoading] = React.useState(false); @@ -445,7 +438,7 @@ export const SearchBar = ({ onClick={() => setIsResultVisible(true)} inputWidth={width} > - Search here + {buttonText} { isResultVisible && ( diff --git a/extensions/react-widget/src/types/index.ts b/extensions/react-widget/src/types/index.ts index cea9e43a..5438cec7 100644 --- a/extensions/react-widget/src/types/index.ts +++ b/extensions/react-widget/src/types/index.ts @@ -44,9 +44,10 @@ export interface WidgetCoreProps extends WidgetProps { export interface SearchBarProps { apiHost?: string; apiKey?: string; - theme?:THEME; - placeholder?:string; - width?:string; + theme?: THEME; + placeholder?: string; + width?: string; + buttonText?: string; } export interface Result { From 411115523ee0bceba3cc731a154aab92820a9612 Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Fri, 3 Jan 2025 02:49:40 +0530 Subject: [PATCH 63/66] (fix:search) ui adjustments --- .../react-widget/src/components/SearchBar.tsx | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/extensions/react-widget/src/components/SearchBar.tsx b/extensions/react-widget/src/components/SearchBar.tsx index a9e43972..7ab9c0ce 100644 --- a/extensions/react-widget/src/components/SearchBar.tsx +++ b/extensions/react-widget/src/components/SearchBar.tsx @@ -74,6 +74,7 @@ const SearchButton = styled.button<{ inputWidth: string }>` appearance: none; transition: background-color 128ms linear; text-align: left; + cursor: pointer; ` const Container = styled.div` @@ -131,6 +132,10 @@ const Title = styled.h3` font-weight: 400; color: ${props => props.theme.primary.text}; margin: 0; + overflow-wrap: break-word; + white-space: normal; + overflow: hidden; + text-overflow: ellipsis; `; const ContentWrapper = styled.div` display: flex; @@ -147,12 +152,17 @@ const Content = styled.div` color: ${props => props.theme.primary.text}; line-height: 1.6; border-left: 2px solid #585858; + overflow: hidden; ` const ContentSegment = styled.div` display: flex; align-items: flex-start; gap: 8px; padding-right: 16px; + overflow-wrap: break-word; + white-space: normal; + overflow: hidden; + text-overflow: ellipsis; ` const ResultWrapper = styled.div` @@ -166,6 +176,13 @@ const ResultWrapper = styled.div` font-family: 'Geist',sans-serif; transition: background-color 0.2s; + word-wrap: break-word; + overflow-wrap: break-word; + word-break: break-word; + white-space: normal; + overflow: hidden; + text-overflow: ellipsis; + &.contains-source:hover { background-color: rgba(0, 92, 197, 0.15); ${Title} { @@ -303,7 +320,7 @@ const TextField = styled.input` font-size: 20px; font-weight: 400; outline: none; - + &:focus { border-color: none; } From 53154291953cd1991ea1d456aa00ab467efba00c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Jan 2025 11:45:19 +0000 Subject: [PATCH 64/66] build(deps): bump react-i18next from 15.0.2 to 15.4.0 in /frontend Bumps [react-i18next](https://github.com/i18next/react-i18next) from 15.0.2 to 15.4.0. - [Changelog](https://github.com/i18next/react-i18next/blob/master/CHANGELOG.md) - [Commits](https://github.com/i18next/react-i18next/compare/v15.0.2...v15.4.0) --- updated-dependencies: - dependency-name: react-i18next dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 8 ++++---- frontend/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 7ad503c4..9e3fb399 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -19,7 +19,7 @@ "react-dom": "^18.3.1", "react-dropzone": "^14.3.5", "react-helmet": "^6.1.0", - "react-i18next": "^15.0.2", + "react-i18next": "^15.4.0", "react-markdown": "^9.0.1", "react-redux": "^8.0.5", "react-router-dom": "^7.1.1", @@ -7865,9 +7865,9 @@ } }, "node_modules/react-i18next": { - "version": "15.0.2", - "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.0.2.tgz", - "integrity": "sha512-z0W3/RES9Idv3MmJUcf0mDNeeMOUXe+xoL0kPfQPbDoZHmni/XsIoq5zgT2MCFUiau283GuBUK578uD/mkAbLQ==", + "version": "15.4.0", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.4.0.tgz", + "integrity": "sha512-Py6UkX3zV08RTvL6ZANRoBh9sL/ne6rQq79XlkHEdd82cZr2H9usbWpUNVadJntIZP2pu3M2rL1CN+5rQYfYFw==", "dependencies": { "@babel/runtime": "^7.25.0", "html-parse-stringify": "^3.0.1" diff --git a/frontend/package.json b/frontend/package.json index bb8ff171..6b4f3989 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -30,7 +30,7 @@ "react-dom": "^18.3.1", "react-helmet": "^6.1.0", "react-dropzone": "^14.3.5", - "react-i18next": "^15.0.2", + "react-i18next": "^15.4.0", "react-markdown": "^9.0.1", "react-redux": "^8.0.5", "react-router-dom": "^7.1.1", From 4ae6a8e25d46fe77ee5a8eda4c91385016e47d09 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Jan 2025 11:50:25 +0000 Subject: [PATCH 65/66] build(deps-dev): bump eslint-plugin-react in /frontend Bumps [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) from 7.37.2 to 7.37.3. - [Release notes](https://github.com/jsx-eslint/eslint-plugin-react/releases) - [Changelog](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/CHANGELOG.md) - [Commits](https://github.com/jsx-eslint/eslint-plugin-react/compare/v7.37.2...v7.37.3) --- updated-dependencies: - dependency-name: eslint-plugin-react dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 741 ++++++++++++++++++++++--------------- frontend/package.json | 2 +- 2 files changed, 444 insertions(+), 299 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 9e3fb399..822685a3 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -44,7 +44,7 @@ "eslint-plugin-n": "^15.7.0", "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-promise": "^6.6.0", - "eslint-plugin-react": "^7.37.2", + "eslint-plugin-react": "^7.37.3", "eslint-plugin-unused-imports": "^4.1.4", "husky": "^8.0.0", "lint-staged": "^15.2.11", @@ -2181,13 +2181,13 @@ "dev": true }, "node_modules/array-buffer-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", "dev": true, "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" }, "engines": { "node": ">= 0.4" @@ -2284,15 +2284,15 @@ } }, "node_modules/array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -2318,19 +2318,18 @@ } }, "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" }, "engines": { "node": ">= 0.4" @@ -2550,13 +2549,13 @@ } }, "node_modules/call-bound": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.2.tgz", - "integrity": "sha512-0lk0PHFe/uz0vl527fG9CgdE9WdafjDbCXvBbs+LUv000TVt2Jjhqbs4Jwm8gz070w8xXyEAxrPOMullsxXeGg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", "dev": true, "dependencies": { - "call-bind": "^1.0.8", - "get-intrinsic": "^1.2.5" + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -2856,14 +2855,14 @@ "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" }, "node_modules/data-view-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", - "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "is-data-view": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -2873,29 +2872,29 @@ } }, "node_modules/data-view-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", - "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "is-data-view": "^1.0.2" }, "engines": { "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/inspect-js" } }, "node_modules/data-view-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", - "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" }, @@ -3047,12 +3046,12 @@ "dev": true }, "node_modules/dunder-proto": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.0.tgz", - "integrity": "sha512-9+Sj30DIu+4KvHqMfLUGLFYL2PkURSYMVXJyXe92nFRvlYq5hBjLEhblKB+vkd/WVlUYMWigiY07T91Fkk0+4A==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "dev": true, "dependencies": { - "call-bind-apply-helpers": "^1.0.0", + "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" }, @@ -3111,57 +3110,62 @@ } }, "node_modules/es-abstract": { - "version": "1.23.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.5.tgz", - "integrity": "sha512-vlmniQ0WNPwXqA0BnmwV3Ng7HxiGlh6r5U6JcTMNx8OilcAGqVJBHJcPjqOMaczU9fRuRK5Px2BdVyPRnKMMVQ==", + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", "dev": true, "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "data-view-buffer": "^1.0.1", - "data-view-byte-length": "^1.0.1", - "data-view-byte-offset": "^1.0.0", - "es-define-property": "^1.0.0", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", + "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", - "gopd": "^1.0.1", + "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", "hasown": "^2.0.2", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", - "is-data-view": "^1.0.1", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.3", "object-keys": "^1.1.1", - "object.assign": "^4.1.5", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.3", - "safe-array-concat": "^1.1.2", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.9", - "string.prototype.trimend": "^1.0.8", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.6", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.15" + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" }, "engines": { "node": ">= 0.4" @@ -3189,26 +3193,27 @@ } }, "node_modules/es-iterator-helpers": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.0.tgz", - "integrity": "sha512-tpxqxncxnpw3c93u8n3VOzACmRFoVmWJqbWXvX/JfKbkhBw1oslgPrUfeSt2psuqyEJFD6N/9lg5i7bsKpoq+Q==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", + "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", + "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.0.3", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", + "get-intrinsic": "^1.2.6", "globalthis": "^1.0.4", - "gopd": "^1.0.1", + "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "iterator.prototype": "^1.1.3", - "safe-array-concat": "^1.1.2" + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.4", + "safe-array-concat": "^1.1.3" }, "engines": { "node": ">= 0.4" @@ -3227,14 +3232,15 @@ } }, "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.4", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -3250,14 +3256,14 @@ } }, "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", "dev": true, "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" }, "engines": { "node": ">= 0.4" @@ -3671,28 +3677,28 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.37.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.2.tgz", - "integrity": "sha512-EsTAnj9fLVr/GZleBLFbj/sSuXeWmp1eXIN60ceYnZveqEaUCyW4X+Vh4WTdUhCkW4xutXYqTXCUSyqD4rB75w==", + "version": "7.37.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.3.tgz", + "integrity": "sha512-DomWuTQPFYZwF/7c9W2fkKkStqZmBd3uugfqBYLdkZ3Hii23WzZuOLUskGxB8qkSKqftxEeGL1TB2kMhrce0jA==", "dev": true, "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", - "array.prototype.flatmap": "^1.3.2", + "array.prototype.flatmap": "^1.3.3", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.1.0", + "es-iterator-helpers": "^1.2.1", "estraverse": "^5.3.0", "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.8", "object.fromentries": "^2.0.8", - "object.values": "^1.2.0", + "object.values": "^1.2.1", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.11", + "string.prototype.matchall": "^4.0.12", "string.prototype.repeat": "^1.0.0" }, "engines": { @@ -4273,15 +4279,17 @@ } }, "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" }, "engines": { "node": ">= 0.4" @@ -4321,21 +4329,21 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.6.tgz", - "integrity": "sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", "dev": true, "dependencies": { "call-bind-apply-helpers": "^1.0.1", - "dunder-proto": "^1.0.0", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "function-bind": "^1.1.2", + "get-proto": "^1.0.0", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", - "math-intrinsics": "^1.0.0" + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -4344,6 +4352,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", @@ -4357,14 +4378,14 @@ } }, "node_modules/get-symbol-description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "dev": true, "dependencies": { - "call-bind": "^1.0.5", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -4475,10 +4496,13 @@ "dev": true }, "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", "dev": true, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -4496,10 +4520,13 @@ } }, "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", "dev": true, + "dependencies": { + "dunder-proto": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -5028,14 +5055,14 @@ "integrity": "sha512-qlD8YNDqyTKTyuITrDOffsl6Tdhv+UC4hcdAVuQsK4IMQ99nSgd1MIA/Q+jQYoh9r3hVUXhYh7urSRmXPkW04g==" }, "node_modules/internal-slot": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, "dependencies": { "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" + "hasown": "^2.0.2", + "side-channel": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -5064,13 +5091,14 @@ } }, "node_modules/is-array-buffer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -5086,12 +5114,15 @@ "dev": true }, "node_modules/is-async-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", - "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.0.tgz", + "integrity": "sha512-GExz9MtyhlZyXYLxzlJRj5WUCE661zhDa1Yna52CN57AJsymh+DvXXjyveSioqSRdxvUrdKdvqB1b5cVKsNpWQ==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -5171,11 +5202,13 @@ } }, "node_modules/is-data-view": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", - "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", "dev": true, "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" }, "engines": { @@ -5220,12 +5253,12 @@ } }, "node_modules/is-finalizationregistry": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.0.tgz", - "integrity": "sha512-qfMdqbAQEwBw78ZyReKnlA8ezmPdb9BemzIIip/JkjaZUhitfXDkkr+3QTboW0JrSXT1QWyYShpvnNHGZ4c4yA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", "dev": true, "dependencies": { - "call-bind": "^1.0.7" + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -5247,12 +5280,15 @@ } }, "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -5294,18 +5330,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -5316,12 +5340,12 @@ } }, "node_modules/is-number-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.0.tgz", - "integrity": "sha512-KVSZV0Dunv9DTPkhXwcZ3Q+tUc9TsaE1ZwX5J2WMvsSGS6Md8TFPun5uwh0yRdrNerI6vf/tbJxqSx4c1ZI1Lw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" }, "engines": { @@ -5382,12 +5406,12 @@ } }, "node_modules/is-shared-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", "dev": true, "dependencies": { - "call-bind": "^1.0.7" + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -5409,12 +5433,12 @@ } }, "node_modules/is-string": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.0.tgz", - "integrity": "sha512-PlfzajuF9vSo5wErv3MJAKD/nqf9ngAs1NFQYm16nUYFO2IzxJ2hcm+IOCg+EEopdykNNUhVq5cz35cAUxU8+g==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" }, "engines": { @@ -5442,12 +5466,12 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "dev": true, "dependencies": { - "which-typed-array": "^1.1.14" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -5469,25 +5493,28 @@ } }, "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.0.tgz", + "integrity": "sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==", "dev": true, "dependencies": { - "call-bind": "^1.0.2" + "call-bound": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-weakset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", - "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4" + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -5509,16 +5536,16 @@ "dev": true }, "node_modules/iterator.prototype": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.4.tgz", - "integrity": "sha512-x4WH0BWmrMmg4oHHl+duwubhrvczGlyuGAZu3nvrf0UXOfPu8IhZObFEr7DE/iv01YgVZrsOiRcqw2srkKEDIA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", "dev": true, "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", "has-symbols": "^1.1.0", - "reflect.getprototypeof": "^1.0.8", "set-function-name": "^2.0.2" }, "engines": { @@ -5907,9 +5934,9 @@ } }, "node_modules/math-intrinsics": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.0.0.tgz", - "integrity": "sha512-4MqMiKP90ybymYvsut0CH2g4XWbfLtmlCkXmtmdcDCxNB+mQcu1w/1+L/VD7vi/PSv7X2JYV7SCcR+jiPXnQtA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "dev": true, "engines": { "node": ">= 0.4" @@ -7111,14 +7138,16 @@ } }, "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "dev": true, "dependencies": { - "call-bind": "^1.0.5", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", "object-keys": "^1.1.1" }, "engines": { @@ -7175,12 +7204,13 @@ } }, "node_modules/object.values": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", - "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" }, @@ -7232,6 +7262,23 @@ "node": ">= 0.8.0" } }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -8072,19 +8119,19 @@ } }, "node_modules/reflect.getprototypeof": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.8.tgz", - "integrity": "sha512-B5dj6usc5dkk8uFliwjwDHM8To5/QwdKz9JcBZ8Ic4G1f0YmeeJTtE/ZTdgRFPAfxZFiUaPhZ1Jcs4qeagItGQ==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", "dev": true, "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "dunder-proto": "^1.0.0", - "es-abstract": "^1.23.5", + "es-abstract": "^1.23.9", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "gopd": "^1.2.0", - "which-builtin-type": "^1.2.0" + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -8417,14 +8464,15 @@ } }, "node_modules/safe-array-concat": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", "isarray": "^2.0.5" }, "engines": { @@ -8434,6 +8482,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safe-regex-test": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", @@ -8505,6 +8569,20 @@ "node": ">= 0.4" } }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -8527,15 +8605,69 @@ } }, "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -8699,23 +8831,24 @@ } }, "node_modules/string.prototype.matchall": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", - "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", + "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "regexp.prototype.flags": "^1.5.2", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", "set-function-name": "^2.0.2", - "side-channel": "^1.0.6" + "side-channel": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -8735,15 +8868,18 @@ } }, "node_modules/string.prototype.trim": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", - "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -8753,15 +8889,19 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -9180,30 +9320,30 @@ } }, "node_modules/typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" } }, "node_modules/typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" @@ -9213,17 +9353,18 @@ } }, "node_modules/typed-array-byte-offset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", "dev": true, "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" }, "engines": { "node": ">= 0.4" @@ -9233,17 +9374,17 @@ } }, "node_modules/typed-array-length": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", - "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", "dev": true, "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-proto": "^1.0.3", "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" }, "engines": { "node": ">= 0.4" @@ -9266,15 +9407,18 @@ } }, "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", + "call-bound": "^1.0.3", "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -9590,16 +9734,16 @@ } }, "node_modules/which-boxed-primitive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.0.tgz", - "integrity": "sha512-Ei7Miu/AXe2JJ4iNF5j/UphAgRoma4trE6PtisM09bPygb3egMH3YLW/befsWb1A1AxvNSFidOFTB18XtnIIng==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", "dev": true, "dependencies": { "is-bigint": "^1.1.0", - "is-boolean-object": "^1.2.0", - "is-number-object": "^1.1.0", - "is-string": "^1.1.0", - "is-symbol": "^1.1.0" + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" }, "engines": { "node": ">= 0.4" @@ -9654,15 +9798,16 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.16", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.16.tgz", - "integrity": "sha512-g+N+GAWiRj66DngFwHvISJd+ITsyphZvD1vChfVg6cEdnzy53GzB3oy0fUNlvhz7H7+MiqhYr26qxQShCpKTTQ==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz", + "integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==", "dev": true, "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "for-each": "^0.3.3", - "gopd": "^1.0.1", + "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" }, "engines": { diff --git a/frontend/package.json b/frontend/package.json index 6b4f3989..7eaf72a2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -55,7 +55,7 @@ "eslint-plugin-n": "^15.7.0", "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-promise": "^6.6.0", - "eslint-plugin-react": "^7.37.2", + "eslint-plugin-react": "^7.37.3", "eslint-plugin-unused-imports": "^4.1.4", "husky": "^8.0.0", "lint-staged": "^15.2.11", From bdd78b664fe821b10b6d73876f3436e213309e63 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Jan 2025 11:58:00 +0000 Subject: [PATCH 66/66] build(deps-dev): bump lint-staged from 15.2.11 to 15.3.0 in /frontend Bumps [lint-staged](https://github.com/lint-staged/lint-staged) from 15.2.11 to 15.3.0. - [Release notes](https://github.com/lint-staged/lint-staged/releases) - [Changelog](https://github.com/lint-staged/lint-staged/blob/master/CHANGELOG.md) - [Commits](https://github.com/lint-staged/lint-staged/compare/v15.2.11...v15.3.0) --- updated-dependencies: - dependency-name: lint-staged dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 34 +++++++++++++++++----------------- frontend/package.json | 2 +- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 822685a3..28ce44eb 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -47,7 +47,7 @@ "eslint-plugin-react": "^7.37.3", "eslint-plugin-unused-imports": "^4.1.4", "husky": "^8.0.0", - "lint-staged": "^15.2.11", + "lint-staged": "^15.3.0", "postcss": "^8.4.49", "prettier": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.8", @@ -2623,6 +2623,18 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/character-entities": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", @@ -5702,12 +5714,12 @@ "dev": true }, "node_modules/lint-staged": { - "version": "15.2.11", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.11.tgz", - "integrity": "sha512-Ev6ivCTYRTGs9ychvpVw35m/bcNDuBN+mnTeObCL5h+boS5WzBEC6LHI4I9F/++sZm1m+J2LEiy0gxL/R9TBqQ==", + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.3.0.tgz", + "integrity": "sha512-vHFahytLoF2enJklgtOtCtIjZrKD/LoxlaUusd5nh7dWv/dkKQJY74ndFSzxCdv7g0ueGg1ORgTSt4Y9LPZn9A==", "dev": true, "dependencies": { - "chalk": "~5.3.0", + "chalk": "~5.4.1", "commander": "~12.1.0", "debug": "~4.4.0", "execa": "~8.0.1", @@ -5728,18 +5740,6 @@ "url": "https://opencollective.com/lint-staged" } }, - "node_modules/lint-staged/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/lint-staged/node_modules/lilconfig": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", diff --git a/frontend/package.json b/frontend/package.json index 7eaf72a2..ec155cde 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -58,7 +58,7 @@ "eslint-plugin-react": "^7.37.3", "eslint-plugin-unused-imports": "^4.1.4", "husky": "^8.0.0", - "lint-staged": "^15.2.11", + "lint-staged": "^15.3.0", "postcss": "^8.4.49", "prettier": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.8",