Skip to content

Commit

Permalink
ready for submission
Browse files Browse the repository at this point in the history
  • Loading branch information
chitoroagad committed Aug 9, 2024
1 parent fec347b commit 6ff1397
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 603 deletions.
98 changes: 35 additions & 63 deletions app/api/tool_utilities.py
Original file line number Diff line number Diff line change
@@ -1,129 +1,104 @@
import json
import os
from traceback import print_exception
from app.services.logger import setup_logger
from app.services.tool_registry import ToolFile
from app.api.error_utilities import (
VideoTranscriptError,
InputValidationError,
ToolExecutorError,
)
from app.api.error_utilities import VideoTranscriptError, InputValidationError, ToolExecutorError
from typing import Dict, Any, List
from fastapi import HTTPException
from pydantic import ValidationError

logger = setup_logger(__name__)


def load_config():
config_path = os.path.join(os.path.dirname(__file__), "tools_config.json")
with open(config_path, "r") as f:
with open(config_path, 'r') as f:
return json.load(f)


tools_config = load_config()


def get_executor_by_name(module_path):
try:
module = __import__(module_path, fromlist=["executor"])
return getattr(module, "executor")
module = __import__(module_path, fromlist=['executor'])
return getattr(module, 'executor')
except Exception as e:
logger.error(f"Failed to import executor from {module_path}: {str(e)}")
raise ImportError(f"Failed to import module from {module_path}: {str(e)}")


def load_tool_metadata(tool_id):
logger.debug(f"Loading tool metadata for tool_id: {tool_id}")
tool_config = tools_config.get(str(tool_id))

if not tool_config:
logger.error(f"No tool configuration found for tool_id: {tool_id}")
raise HTTPException(status_code=404, detail="Tool configuration not found")

# Ensure the base path is relative to the current file's directory
base_dir = os.path.dirname(os.path.abspath(__file__))
logger.debug(f"Base directory: {base_dir}")

# Construct the directory path
module_dir_path = os.path.join(
base_dir, "..", *tool_config["path"].split(".")[:-1]
) # Go one level up and then to the path
module_dir_path = os.path.join(base_dir, '..', *tool_config['path'].split('.')[:-1]) # Go one level up and then to the path
module_dir_path = os.path.abspath(module_dir_path) # Get absolute path
logger.debug(f"Module directory path: {module_dir_path}")

file_path = os.path.join(module_dir_path, tool_config["metadata_file"])
file_path = os.path.join(module_dir_path, tool_config['metadata_file'])
logger.debug(f"Checking metadata file at: {file_path}")

if not os.path.exists(file_path) or os.path.getsize(file_path) == 0:
logger.error(f"Metadata file missing or empty at: {file_path}")
raise HTTPException(status_code=404, detail="Tool metadata not found")

with open(file_path, "r") as f:
with open(file_path, 'r') as f:
metadata = json.load(f)

logger.debug(f"Loaded metadata: {metadata}")
return metadata


def prepare_input_data(input_data) -> Dict[str, Any]:
inputs = {input.name: input.value for input in input_data}
return inputs


def check_missing_inputs(request_data: Dict[str, Any], validate_inputs: Dict[str, str]):
for validate_input_name in validate_inputs:
if validate_input_name not in request_data:
error_message = f"Missing input: `{validate_input_name}`"
logger.error(error_message)
raise InputValidationError(error_message)


def raise_type_error(input_name: str, input_value: Any, expected_type: str):
error_message = (
f"Input `{input_name}` must be a {expected_type} but got {type(input_value)}"
)
error_message = f"Input `{input_name}` must be a {expected_type} but got {type(input_value)}"
logger.error(error_message)
raise InputValidationError(error_message)


def validate_file_input(input_name: str, input_value: Any):
if not isinstance(input_value, list):
error_message = f"Input `{input_name}` must be a list of file dictionaries but got {type(input_value)}"
logger.error(error_message)
raise InputValidationError(error_message)

for file_obj in input_value:
if not isinstance(file_obj, dict):
error_message = f"Each item in the input `{input_name}` must be a dictionary representing a file but got {type(file_obj)}"
logger.error(error_message)
raise InputValidationError(error_message)
try:
ToolFile.model_validate(
file_obj, from_attributes=True
) # This will raise a validation error if the structure is incorrect
ToolFile.model_validate(file_obj, from_attributes=True) # This will raise a validation error if the structure is incorrect
except ValidationError:
error_message = f"Each item in the input `{input_name}` must be a valid ToolFile where a URL is provided"
logger.error(error_message)
raise InputValidationError(error_message)


def validate_input_type(input_name: str, input_value: Any, expected_type: str):
if expected_type == "text" and not isinstance(input_value, str):
if expected_type == 'text' and not isinstance(input_value, str):
raise_type_error(input_name, input_value, "string")
elif expected_type == "number" and not isinstance(input_value, (int, float)):
elif expected_type == 'number' and not isinstance(input_value, (int, float)):
raise_type_error(input_name, input_value, "number")
elif expected_type == "file":
elif expected_type == 'file':
validate_file_input(input_name, input_value)


def validate_inputs(
request_data: Dict[str, Any], validate_data: List[Dict[str, str]]
) -> bool:
validate_inputs = {
input_item["name"]: input_item["type"] for input_item in validate_data
}

def validate_inputs(request_data: Dict[str, Any], validate_data: List[Dict[str, str]]) -> bool:
validate_inputs = {input_item['name']: input_item['type'] for input_item in validate_data}

# Check for missing inputs
check_missing_inputs(request_data, validate_inputs)

Expand All @@ -137,44 +112,41 @@ def validate_inputs(

return True


def convert_files_to_tool_files(inputs: Dict[str, Any]) -> Dict[str, Any]:
if "files" in inputs:
inputs["files"] = [ToolFile(**file_object) for file_object in inputs["files"]]
if 'files' in inputs:
inputs['files'] = [ToolFile(**file_object) for file_object in inputs['files']]
return inputs


def finalize_inputs(input_data, validate_data: List[Dict[str, str]]) -> Dict[str, Any]:
inputs = prepare_input_data(input_data)
validate_inputs(inputs, validate_data)
inputs = convert_files_to_tool_files(inputs)
return inputs


def execute_tool(tool_id, request_inputs_dict):
try:
tool_config = tools_config.get(str(tool_id))

if not tool_config:
raise HTTPException(status_code=404, detail="Tool executable not found")

execute_function = get_executor_by_name(tool_config["path"])
request_inputs_dict["verbose"] = True

execute_function = get_executor_by_name(tool_config['path'])
request_inputs_dict['verbose'] = True
return execute_function(**request_inputs_dict)

except VideoTranscriptError as e:
logger.error(f"Failed to execute tool due to video transcript error: {str(e)}")
raise HTTPException(status_code=400, detail=str(e))

except ToolExecutorError as e:
logger.error(f"Failed to execute tool due to executor error: {str(e)}")
raise HTTPException(status_code=400, detail=str(e))

except ImportError as e:
logger.error(f"Failed to execute tool due to import error: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))

except Exception as e:
logger.error(f"Encountered error in executing tool: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))
raise HTTPException(status_code=500, detail=str(e))
7 changes: 0 additions & 7 deletions app/features/syllabus_generator/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,3 @@ def executor(
)
t = s.create_syllabus()
print(t)
# executor(
# subject="Data Structures",
# grade_level="University",
# course_overview="This course covers the fundamental concepts and applications of data structures in computer science. Students will explore various data structures such as arrays, linked lists, stacks, queues, trees, and graphs.",
# options=["all"],
# customisation="",
# )
35 changes: 35 additions & 0 deletions app/features/syllabus_generator/tests/test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"user": {
"id": "string",
"fullName": "string",
"email": "string"
},
"type": "tool",
"tool_data": {
"tool_id": 2,
"inputs": [
{
"name": "subject",
"value": "Data Structures"
},
{
"name": "grade_level",
"value": "University"
},
{
"name": "course_overview",
"value": "This course covers the fundamental concepts and applications of data structures in computer science. Students will explore various data structures such as arrays, linked lists, stacks, queues, trees, and graphs. The course will emphasize both the theoretical and practical aspects of data structures, including their implementation and analysis of their efficiency. By the end of the course, students will be equipped with the knowledge to use data structures effectively in real-world applications and advanced computing challenges."
},
{
"name": "customisation",
"value": "I want the course to require proficiency in Python"
},
{
"name": "options",
"value": [
"all"
]
}
]
}
}
Loading

0 comments on commit 6ff1397

Please sign in to comment.