diff --git a/run.py b/run.py index 9ca3ee2..2bc6983 100644 --- a/run.py +++ b/run.py @@ -4,4 +4,4 @@ app = create_app() if __name__ == '__main__': - app.run(debug=True, port=5402) + app.run(host='0.0.0.0', port=5402, debug=True) diff --git a/src/api/routes/chatbot.py b/src/api/routes/chatbot.py index f1a1cf7..cd06708 100644 --- a/src/api/routes/chatbot.py +++ b/src/api/routes/chatbot.py @@ -5,6 +5,7 @@ from src.services.chatbot import Chatbot from src.utils.utils import delete_all_files_in_directory from src.utils.document_loader import load_document from src.services.chatbot import Chatbot +from src.utils.auth import auth_check # Initialize the Blueprint @@ -18,6 +19,7 @@ def allowed_file(filename): return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS @bot.route('/validate_worker_document', methods=['POST']) +@auth_check def validate_worker_document(): try: # Retrieve form data @@ -63,6 +65,7 @@ def validate_worker_document(): @bot.route('/predict_next_n_assessments', methods=['POST']) +@auth_check def predict_next_n_assessments(): try: # Retrieve JSON data from the request @@ -94,6 +97,7 @@ def predict_next_n_assessments(): @bot.route('/use_bot_predict_assessments', methods=['POST']) +@auth_check def use_bot_predict_assessments(): try: # Retrieve JSON data from the request @@ -124,6 +128,7 @@ def use_bot_predict_assessments(): @bot.route('/suggest_assessment_frequencies', methods=['POST']) +@auth_check def use_bot_suggest_frequencies(): try: # Retrieve JSON data from the request @@ -155,6 +160,7 @@ def use_bot_suggest_frequencies(): @bot.route('/predict_goal_achievment_proba', methods=['POST']) +@auth_check def predict_goal_achievement(): try: # Retrieve JSON data from the request diff --git a/src/api/routes/questions.py b/src/api/routes/questions.py index 7a6c38b..a316b56 100644 --- a/src/api/routes/questions.py +++ b/src/api/routes/questions.py @@ -9,6 +9,7 @@ from src.services.questions_generator import QuestionsGenerator from src.utils.utils import delete_all_files_in_directory from src.utils.document_loader import load_document import json +from src.utils.auth import auth_check # Initialize the Blueprint qs_b = Blueprint('questions', __name__) @@ -22,7 +23,9 @@ def allowed_file(filename): return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS + @qs_b.route('/generate_questions_from_sop', methods=['POST']) +@auth_check def generate_questions_from_sop(): # Check if the request contains data if not request.is_json: diff --git a/src/api/routes/sops.py b/src/api/routes/sops.py index 1f552f6..585d3c3 100644 --- a/src/api/routes/sops.py +++ b/src/api/routes/sops.py @@ -3,6 +3,7 @@ from flask import Blueprint, request, jsonify, current_app from werkzeug.utils import secure_filename from src.services.sop_generator import (SopPersonalAssessment,SopGeneratorExecutive) from src.services.sop_document_parser import DocumentParser +from src.utils.auth import auth_check from src.utils.utils import delete_all_files_in_directory from src.utils.document_loader import load_document @@ -20,6 +21,7 @@ def allowed_file(filename): return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS @sops_bp.route('/personal_assessment/get_roles_from_doc', methods=['POST']) +@auth_check def get_roles(): # Check if the post request has the file part if 'document' not in request.files: @@ -63,6 +65,7 @@ def get_roles(): @sops_bp.route('/personal_assessment/get_roles_from_questionnaire', methods=['POST']) +@auth_check def get_roles_questionnaire(): # Check if the post request has the file part questionnaire_data = request.json @@ -87,6 +90,7 @@ def get_roles_questionnaire(): @sops_bp.route('/personal_assessment/generate_sops_from_doc', methods=['POST']) +@auth_check def generate_sops(): # Check if the POST request has the file part if 'document' not in request.files: @@ -143,6 +147,7 @@ def generate_sops(): @sops_bp.route('/personal_assessment/generate_sops_from_questionnaire', methods=['POST']) +@auth_check def generate_sops_from_questionnaire_per(): """ Generate SOPs based on the questionnaire data provided in the request body. @@ -184,6 +189,7 @@ def generate_sops_from_questionnaire_per(): @sops_bp.route('/personal_assessment/generate_sops_by_roles_and_areas', methods=['POST']) +@auth_check def generate_sops_by_roles_and_areas(): """ Generate SOPs based on the roles, SOP types (will, shall, must), and areas provided in the request body. @@ -216,6 +222,7 @@ def generate_sops_by_roles_and_areas(): @sops_bp.route('/executive/generate_sop_mission_from_vision', methods=['POST']) +@auth_check def generate_executive_sops_from_doc(): """ Generate SOPs for executives based on a document containing vision and mission. @@ -292,6 +299,7 @@ def generate_executive_sops_from_doc(): @sops_bp.route('/executive/generate_sop_managers_doc', methods=['POST']) +@auth_check def generate_sop_managers_doc(): if 'document' not in request.files: return jsonify({"error": "No file part", "message": "Please upload a file with the key 'document'."}), 400 @@ -312,18 +320,18 @@ def generate_sop_managers_doc(): docs = load_document(file_path) # Load department managers from form data as a JSON string - department_managers_json = request.form.get('department_managers', '[]') + department_managers_json = request.form.get('departments_managers', '[]') department_managers = json.loads(department_managers_json) sop_generator = DocumentParser() - result = sop_generator.extract_sops_for_managers_by_department(docs, department_managers) + result = sop_generator.extract_sops_for_workers_by_department(docs, department_managers) delete_all_files_in_directory(upload_folder) if not result: return jsonify({"error": "Processing error", "message": "Failed to generate SOPs for department managers."}), 500 - return jsonify({"sops": result.dict(), "message": "SOPs successfully generated for department managers."}), 200 + return jsonify({"sops": result, "message": "SOPs successfully generated for department managers."}), 200 except Exception as e: delete_all_files_in_directory(upload_folder) @@ -334,6 +342,7 @@ def generate_sop_managers_doc(): @sops_bp.route('/executive/generate_sops_from_questionnaire', methods=['POST']) +@auth_check def generate_sops_from_questionnaire(): try: data = request.json @@ -366,6 +375,7 @@ def generate_sops_from_questionnaire(): @sops_bp.route('/executive/get_roles_for_reference_managers', methods=['POST']) +@auth_check def get_roles_for_reference_managers(): try: # Retrieve form data @@ -409,6 +419,7 @@ def get_roles_for_reference_managers(): @sops_bp.route('/manager/get_roles_for_reference_workers', methods=['POST']) +@auth_check def get_roles_for_reference_workers(): try: # Retrieve form data @@ -450,6 +461,7 @@ def get_roles_for_reference_workers(): @sops_bp.route('/manager/generate_sop_workers_doc', methods=['POST']) +@auth_check def generate_sop_workers_doc(): try: # Check if the document is provided diff --git a/src/prompts/questions.py b/src/prompts/questions.py index b760720..ed9de47 100644 --- a/src/prompts/questions.py +++ b/src/prompts/questions.py @@ -12,7 +12,7 @@ def get_questions_prompt(): Input: assessment type: (e.g., daily, weekly, biweekly) frequency type: (e.g., daily, weekly, biweekly) - frequency number: (e.g., day 3, week 2, biweekly 1) + frequency number(the current week or frequency e.g if assessment is weekly and frequcny number is 2 , it means week 2): (e.g., day 3, week 2, biweekly 1) total duration: (e.g., 6 weeks, 12 days) SOPs of the assessment: roles_data e.g [{"position""test position","mame":"name of staff"}] @@ -23,6 +23,7 @@ def get_questions_prompt(): 2. Regardless of the assement type, always use 1,2,3 for the frequency numbering, nothing else 3. All questions are "yes" or "no" questions nothing extra and precise ,not long 4. Generate a total of at least 20 questions all rounda based on the sops and roles for each frequency number + 5. make sure the questions are up to 20 for the current frequency Example response: questions @@ -36,7 +37,8 @@ def get_questions_prompt(): "assigned_to": "name", "role": "person role", "question": "e.g., Is the internal project team being followed according to the SOP?" - "area_tag":"timeline" + "area_tag":"timeline", + "postion":"person position" } ] ## up to at least 20 questions }, @@ -48,7 +50,8 @@ def get_questions_prompt(): "assigned_to": "name", "role": "person role", "question": "e.g., Have communication protocols been followed for the task at hand?". - "area_tag":"communication" + "area_tag":"communication", + "position":"person position" } ## up to at least 20 questions ] } diff --git a/src/prompts/sops.py b/src/prompts/sops.py index 58e4905..fa96829 100644 --- a/src/prompts/sops.py +++ b/src/prompts/sops.py @@ -317,16 +317,16 @@ def get_sop_for_department_workers(): } ''' -def get_sop_for_department_workers(): - return '''Generate SOPs for each worker under the unique department based on the information the workers info provided +def get_sop_for_department_managers(): + return '''Generate SOPs for each manager under the unique department based on the information the managers info provided Instructions: 1. Focus on the provided department and manager role. 2. Categorize SOPs into "must," "shall," and "will." - 3. SOPs should be actionable and relevant to the worker's duties. + 3. SOPs should be actionable and relevant to the manager's duties. 4. If no SOPs can be generated, return empty lists for each category. - 5. Use the provided document and the workers and department information to generate the SOP. - 6. If the provided document cannot provide SOPs for a specific worker stated, then return an empty list for the SOP for that worker. + 5. Use the provided document and the managers and department information to generate the SOP. + 6. If the provided document cannot provide SOPs for a specific manager stated, then return an empty list for the SOP for that worker. Example forma { @@ -335,7 +335,7 @@ def get_sop_for_department_workers(): "name": "Department A", "managers": [ { - "name": "Worker A", + "name": "manager A", "must": ["Conduct weekly meetings"], "shall": ["Submit monthly reports"], "will": ["Improve efficiency"] diff --git a/src/services/questions_generator.py b/src/services/questions_generator.py index f78e96a..4679b65 100644 --- a/src/services/questions_generator.py +++ b/src/services/questions_generator.py @@ -103,7 +103,7 @@ class QuestionsGenerator: {"role": "user", "content": f"Roles Data: {roles_data}"} ], response_format=AssementQuestion, # Ensure you specify the correct format - max_tokens=4096, + max_tokens=10000, temperature=0.1 ) diff --git a/src/services/sop_document_parser.py b/src/services/sop_document_parser.py index d0810dd..6cdbb20 100644 --- a/src/services/sop_document_parser.py +++ b/src/services/sop_document_parser.py @@ -85,7 +85,7 @@ class DocumentParser: } ], response_format=VisionMissionResponse, - max_tokens=4096, + max_tokens=1600, temperature=0.1 ) @@ -120,7 +120,7 @@ class DocumentParser: } ], response_format=Roles_response, - max_tokens=1024, + max_tokens=4096, temperature=0.1 ) @@ -172,7 +172,7 @@ class DocumentParser: {"role": "user", "content": [{"type": "text", "text": text} for text in docs_text]} ], response_format=DepartmentsAndWorkersResponse, # Use the updated response schema - max_tokens=4096, + max_tokens=16000, temperature=0.1 ) @@ -212,7 +212,7 @@ class DocumentParser: {"role": "system", "content": f"The reference roles are{reference_roles} while the extracted roles are {extracted_managers}"}, {"role": "user", "content": prompt} ], - max_tokens=1024, + max_tokens=16000, temperature=0.1, response_format=RolesComparisonResponse ) @@ -257,7 +257,7 @@ class DocumentParser: {"role": "system", "content": f"The reference roles are{reference_roles} while the extracted roles are {extracted_workers}"}, {"role": "user", "content": prompt} ], - max_tokens=1024, + max_tokens=16000, temperature=0.1, response_format=RolesComparisonResponse ) @@ -288,7 +288,7 @@ class DocumentParser: {"role": "user", "content": [{"type": "text", "text": text} for text in docs_text]} ], response_format=DepartmentMembers, # Use the updated response schema - max_tokens=4096, + max_tokens=16000, temperature=0.1 ) @@ -316,7 +316,7 @@ class DocumentParser: {"role": "user", "content": [{"type": "text", "text": text} for text in docs_text]} ], response_format=WorkerSOPsResponse, # Use the updated response schema - max_tokens=4096, + max_tokens=16000, temperature=0.1 ) @@ -328,24 +328,24 @@ class DocumentParser: def extract_sops_for_managers_by_department(self, docs,depts_managers): """ - Extract sops for managers from the document. + Extract departments, managers, and workers from the document. :param docs: List of document chunks - :return: Dictionary containing departments, their managers, + :return: Dictionary containing departments, their managers, and workers. """ try: docs_text = self._extract_text_from_docs(docs) - prompt = get_sop_for_department_workers() # Update your prompt to handle managers and workers + prompt = get_sop_for_department_managers() # Update your prompt to handle managers and workers response = self.client.beta.chat.completions.parse( model=self.model, messages=[ {"role": "system", "content": prompt}, - {"role": "user", "content": f"Mangers information: {depts_managers}"}, + {"role": "user", "content": f"Workers information: {depts_managers}"}, {"role": "user", "content": [{"type": "text", "text": text} for text in docs_text]} ], - response_format=ManagerWithSOPs, # Use the updated response schema - max_tokens=4096, + response_format=WorkerSOPsResponse, # Use the updated response schema + max_tokens=16000, temperature=0.1 ) diff --git a/src/services/sop_generator.py b/src/services/sop_generator.py index 49e2f56..00c58d3 100644 --- a/src/services/sop_generator.py +++ b/src/services/sop_generator.py @@ -59,7 +59,7 @@ class SopPersonalAssessment: } ], response_format=SOPsResponse, - max_tokens=2048, + max_tokens=16000, temperature=0.1 ) @@ -94,7 +94,7 @@ class SopPersonalAssessment: } ], response_format=RoleSops, - max_tokens=1024, + max_tokens=16000, temperature=0.1 ) extracted_text = json.loads(response.choices[0].message.content) @@ -127,7 +127,7 @@ class SopPersonalAssessment: } ], response_format=Roles_response, - max_tokens=1024, + max_tokens=16000, temperature=0.1 ) extracted_text = json.loads(response.choices[0].message.content) @@ -180,7 +180,7 @@ class SopGeneratorExecutive: } ], response_format=Categories, - max_tokens=2048, + max_tokens=16000, temperature=0.1 ) @@ -214,7 +214,7 @@ class SopGeneratorExecutive: {"role": "user", "content": f"Generate SOPs for {role['position']} in {department['name']} department."} ], response_format=ManagerSOPs, - max_tokens=1024, + max_tokens=16000, temperature=0.1 ) manager_sops = json.loads(response.choices[0].message.content) @@ -246,7 +246,7 @@ class SopGeneratorExecutive: {"role": "user", "content": f"Generate SOPs based on this questionnaire:\n{user_content}\n\nExecutives to consider: {', '.join(executives)}\nManagers to consider: {', '.join(managers)}\nDepartments to consider: {', '.join(departments)}"} ], response_format={"type": "json_object"}, - max_tokens=4096, + max_tokens=16000, temperature=0.1 ) diff --git a/src/utils/auth.py b/src/utils/auth.py new file mode 100644 index 0000000..043af9b --- /dev/null +++ b/src/utils/auth.py @@ -0,0 +1,24 @@ +import os +from functools import wraps +from flask import Flask, session, redirect, url_for, request, g, jsonify +from dotenv import load_dotenv +load_dotenv() + +API_KEY = os.getenv("API_KEY") + +def auth_check(func): + @wraps(func) + def decorated_function(*args, **kwargs): + auth_header = request.headers.get('Authorization') + + if not auth_header or not auth_header.startswith('Bearer '): + return jsonify({"error": "Unauthorized", "message": "API key is missing or invalid."}), 401 + + token = auth_header.split(' ')[1] + if token != API_KEY: + return jsonify({"error": "Unauthorized", "message": "API key does not match."}), 401 + + g.authenticated = True + return func(*args, **kwargs) + + return decorated_function diff --git a/test.py b/test.py index cf83ba0..4a05165 100644 --- a/test.py +++ b/test.py @@ -1,81 +1,3 @@ -# Example usage -'''from scripts.run_assessment_prediction_trainer import CompanyModelPipeline -company_ids = ['testid'] -input_base_path = '/root/ds_erp_ai/data/raw/erp_assessment_prediction' # The base path where the raw data for each company is stored - -pipeline = CompanyModelPipeline(company_ids=company_ids, input_base_path=input_base_path) -pipeline.run_pipeline()''' - -'''from src.pipeline.inference import AssessmentInference - -inference = AssessmentInference( - company_id="testid",num_assessments=2 -) - -result = inference.run() - - -print(result) -''' -''' -response2 = bot.predict_next_n_assessment( - company_info=company_info, - companyid="testid", - N=3 -) - -print(f"Predictions {response2}") - - -from src.services.chatbot import Chatbot -company_info = { - 'company_name': "ABC Corp", - 'company_size': "Medium", # Can be "Small", "Medium", or "Large" - 'departments': ["Sales", "Marketing", "IT", "Finance", "HR", "Logistics"] -} -bot = Chatbot() -response = bot.predict_based_on_past_assessment( - query="Should i make my next assessment weekly or biweekly to meet up to deadline?", - company_info=company_info, - companyid="testid" -) - -print(f"Result: {response}") - - -from src.services.sop_document_parser import DocumentParser -from src.utils.document_loader import load_document - -path = r"/root/ds_erp_ai/data/raw/test_sop.pdf" - -parser = DocumentParser() - -workers_department = [ - {"name": "sales", "workers": ["sales manager"]}, - {"name": "development", "workers": ["deployment officer"]} -] -res = parser.extract_sops_for_workers_by_department( - docs=load_document(path), # Load the document for processing - depts_workers=workers_department -) - -print(res)''' - -from src.services.chatbot import Chatbot - -bot = Chatbot( -) - -company_info = { - "company_name": "Example Co", - "company_size": "Medium", - "departments": ["HR", "Finance", "IT"] - } - -response = bot.predict_goal_achievement_probability( - company_info=company_info, - companyid="testid" -) - - -print(response) \ No newline at end of file +import os +API_KEY = "erp_" + os.urandom(16).hex() +print(API_KEY) \ No newline at end of file