diff --git a/lollms/app.py b/lollms/app.py index 2268190..2ded091 100644 --- a/lollms/app.py +++ b/lollms/app.py @@ -1125,7 +1125,7 @@ class LollmsApplication(LoLLMsCom): # Build the final discussion messages by detokenizing the full_message_list discussion_messages = "" - for i in range(len(full_message_list)-1): + for i in range(len(full_message_list)-1 if not is_continue else len(full_message_list)): message_tokens = full_message_list[i] discussion_messages += self.model.detokenize(message_tokens) @@ -1134,7 +1134,7 @@ class LollmsApplication(LoLLMsCom): else: ai_prefix = "" # Build the final prompt by concatenating the conditionning and discussion messages - prompt_data = conditionning + internet_search_results + documentation + knowledge + user_description + discussion_messages + positive_boost + negative_boost + fun_mode + start_ai_header_id_template + ai_prefix + end_ai_header_id_template + prompt_data = conditionning + internet_search_results + documentation + knowledge + user_description + discussion_messages + positive_boost + negative_boost + fun_mode + (start_ai_header_id_template + ai_prefix + end_ai_header_id_template if not is_continue else '') # Tokenize the prompt data tokens = self.model.tokenize(prompt_data) diff --git a/lollms/functions/analyze_code/add_code_to_file.py b/lollms/functions/analyze_code/add_code_to_file.py new file mode 100644 index 0000000..3d3bc37 --- /dev/null +++ b/lollms/functions/analyze_code/add_code_to_file.py @@ -0,0 +1,66 @@ +# Lollms function call definition file +# File Name: add_code_to_file.py +# Author: ParisNeo +# Description: This function adds given code to a specified Python file in a project directory. + +# Import necessary modules +from functools import partial +from lollms.utilities import PackageManager +from ascii_colors import trace_exception +from typing import Union +from pathlib import Path + +# Function to add code to a file +def add_code_to_file(file_name: str, code_content: str, project_path: Union[str, Path] = "") -> str: + """ + Adds the specified code content to the given file in the project path. + + Args: + file_name (str): The name of the file to add code to. + code_content (str): The code content to be added. + project_path (Union[str, Path]): The path to the Python project directory. + + Returns: + str: Success message or error message. + """ + try: + project_path = Path(project_path) + + # Validate the project path + if not project_path.exists() or not project_path.is_dir(): + return "Invalid project path." + + file_path = project_path / file_name + + # Check if the file exists + if not file_path.exists(): + return f"File {file_name} does not exist in the specified project." + + # Read the current content of the file + with open(file_path, "r", encoding="utf-8") as file: + current_content = file.read() + + # Add the new code content + updated_content = current_content + "\n\n" + code_content + + # Write the updated content back to the file + with open(file_path, "w", encoding="utf-8") as file: + file.write(updated_content) + + return f"Code added successfully to file {file_name}." + + except Exception as e: + return trace_exception(e) + + +# Metadata function +def add_code_to_file_function(project_path: str): + return { + "function_name": "add_code_to_file", # The function name in string + "function": partial(add_code_to_file, project_path=project_path), # The function to be called + "function_description": "Adds the specified code content to the given file in the project path.", # Description of the function + "function_parameters": [ # The set of parameters + {"name": "file_name", "type": "str"}, + {"name": "code_content", "type": "str"}, + ] + } diff --git a/lollms/functions/analyze_code/create_project_database.py b/lollms/functions/analyze_code/create_project_database.py index 74199a5..d9114b7 100644 --- a/lollms/functions/analyze_code/create_project_database.py +++ b/lollms/functions/analyze_code/create_project_database.py @@ -1,8 +1,3 @@ -""" - -""" - - from functools import partial from typing import List, Dict, Union, Any from lollms.utilities import PackageManager @@ -17,7 +12,7 @@ import json if not PackageManager.check_package_installed("sqlite3"): PackageManager.install_package("sqlite3") -def create_project_database(project_path: Union[str, Path], max_summary_size:str=512, llm: APScript=None) -> str: +def create_project_database(project_path: Union[str, Path], max_summary_size: str = 512, llm: APScript = None) -> str: """ Creates a database containing structured information about a Python project. @@ -54,6 +49,7 @@ def create_project_database(project_path: Union[str, Path], max_summary_size:str name TEXT NOT NULL, docstring TEXT, parameters TEXT, + core TEXT, FOREIGN KEY (file_id) REFERENCES files (id) )''') @@ -62,11 +58,20 @@ def create_project_database(project_path: Union[str, Path], max_summary_size:str file_id INTEGER, name TEXT NOT NULL, docstring TEXT, - methods TEXT, - static_methods TEXT, + core TEXT, FOREIGN KEY (file_id) REFERENCES files (id) )''') + cursor.execute('''CREATE TABLE IF NOT EXISTS methods ( + id INTEGER PRIMARY KEY, + class_id INTEGER, + name TEXT NOT NULL, + docstring TEXT, + parameters TEXT, + core TEXT, + FOREIGN KEY (class_id) REFERENCES classes (id) + )''') + cursor.execute('''CREATE TABLE IF NOT EXISTS project_info ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, @@ -83,12 +88,12 @@ def create_project_database(project_path: Union[str, Path], max_summary_size:str readme_content = readme_file.read() structure = "\n".join([str(p.relative_to(project_path)) for p in project_path.rglob("*")]) readme_content += f"## Project Structure:\n{structure}" - project_description = llm.summerize_text(readme_content, "Build a comprehensive description of this project from the available information", max_generation_size=max_summary_size, callback=llm.sink) + project_description = llm.summarize_text(readme_content, "Build a comprehensive description of this project from the available information", max_generation_size=max_summary_size, callback=llm.sink) else: # Construct a description based on the project structure structure = "\n".join([str(p.relative_to(project_path)) for p in project_path.rglob("*")]) constructed_text = f"Project Name: {project_name}\n\nProject Structure:\n{structure}" - project_description = llm.summerize_text(constructed_text, "Build a comprehensive description of this project from the available information", max_generation_size=max_summary_size, callback=llm.sink) + project_description = llm.summarize_text(constructed_text, "Build a comprehensive description of this project from the available information", max_generation_size=max_summary_size, callback=llm.sink) # Insert project information into the database cursor.execute("INSERT INTO project_info (name, description) VALUES (?, ?)", (project_name, project_description)) @@ -103,20 +108,22 @@ def create_project_database(project_path: Union[str, Path], max_summary_size:str for node in ast.walk(tree): if isinstance(node, ast.FunctionDef): - parameters = [arg.arg for arg in node.args.args] - cursor.execute("INSERT INTO functions (file_id, name, docstring, parameters) VALUES (?, ?, ?, ?)", - (file_id, node.name, ast.get_docstring(node), str(parameters))) + parameters = [(arg.arg, arg.annotation.id if arg.annotation else None) for arg in node.args.args] + core = ast.get_source_segment(content, node) + cursor.execute("INSERT INTO functions (file_id, name, docstring, parameters, core) VALUES (?, ?, ?, ?, ?)", + (file_id, node.name, ast.get_docstring(node), json.dumps(parameters), core)) elif isinstance(node, ast.ClassDef): methods = [] static_methods = [] + core = ast.get_source_segment(content, node) + class_id = cursor.execute("INSERT INTO classes (file_id, name, docstring, core) VALUES (?, ?, ?, ?)", + (file_id, node.name, ast.get_docstring(node), core)).lastrowid for class_node in node.body: if isinstance(class_node, ast.FunctionDef): - if any(isinstance(decorator, ast.Name) and decorator.id == 'staticmethod' for decorator in class_node.decorator_list): - static_methods.append(class_node.name) - else: - methods.append(class_node.name) - cursor.execute("INSERT INTO classes (file_id, name, docstring, methods, static_methods) VALUES (?, ?, ?, ?, ?)", - (file_id, node.name, ast.get_docstring(node), str(methods), str(static_methods))) + parameters = [(arg.arg, arg.annotation.id if arg.annotation else None) for arg in class_node.args.args] + method_core = ast.get_source_segment(content, class_node) + cursor.execute("INSERT INTO methods (class_id, name, docstring, parameters, core) VALUES (?, ?, ?, ?, ?)", + (class_id, class_node.name, ast.get_docstring(class_node), json.dumps(parameters), method_core)) # Commit changes and close the connection conn.commit() @@ -130,7 +137,7 @@ def create_project_database(project_path: Union[str, Path], max_summary_size:str def create_project_database_function(project_path, llm): return { "function_name": "create_project_database", - "function": partial(create_project_database,project_path=project_path, llm=llm), + "function": partial(create_project_database, project_path=project_path, llm=llm), "function_description": "Creates a database containing structured information about a Python project.", "function_parameters": [] - } \ No newline at end of file + } diff --git a/lollms/functions/analyze_code/git_commit.py b/lollms/functions/analyze_code/git_commit.py new file mode 100644 index 0000000..89a3eaa --- /dev/null +++ b/lollms/functions/analyze_code/git_commit.py @@ -0,0 +1,64 @@ +# Lollms function call definition file +# File Name: git_commit.py +# Author: ParisNeo +# Description: This function commits changes to a Git repository with a specified commit message. + +# Import necessary libraries +from functools import partial +from typing import Union +from pathlib import Path +import subprocess +from ascii_colors import trace_exception + +# Ensure Git is installed +from lollms.utilities import PackageManager + +if not PackageManager.check_package_installed("git"): + PackageManager.install_package("gitpython") + +import git + +def git_commit(commit_message: str, repo_path: Union[str, Path]) -> str: + """ + Commits changes to a Git repository with a specified commit message. + + Args: + repo_path (Union[str, Path]): The path to the Git repository. + commit_message (str): The commit message. + + Returns: + str: Success or error message. + """ + try: + repo_path = Path(repo_path) + if not repo_path.exists(): + return "Repository path does not exist." + + repo = git.Repo(repo_path) + + # Stage all changes + repo.git.add(A=True) + + # Commit changes + repo.index.commit(commit_message) + + return "Changes committed successfully." + + except Exception as e: + return trace_exception(e) + +def git_commit_function(repo_path:Path|str): + return { + "function_name": "git_commit", + "function": partial(git_commit, repo_path=repo_path), + "function_description": "Commits changes to a Git repository with a specified commit message.", + "function_parameters": [ + {"name": "commit_message", "type": "str"} + ] + } + +if __name__ == "__main__": + # Example usage + repo_path = "path/to/repo" + commit_message = "Your commit message" + print(git_commit(repo_path, commit_message)) diff --git a/lollms/functions/analyze_code/git_pull.py b/lollms/functions/analyze_code/git_pull.py new file mode 100644 index 0000000..65407a2 --- /dev/null +++ b/lollms/functions/analyze_code/git_pull.py @@ -0,0 +1,59 @@ +# Lollms function call definition file +# File Name: git_pull.py +# Author: ParisNeo +# Description: This function pulls the latest changes from the remote repository to the local Git repository. + +# Import necessary libraries +from functools import partial +from typing import Union +from pathlib import Path +import subprocess +from ascii_colors import trace_exception + +# Ensure Git is installed +from lollms.utilities import PackageManager + +if not PackageManager.check_package_installed("git"): + PackageManager.install_package("gitpython") + +import git + +def git_pull(repo_path: Union[str, Path]) -> str: + """ + Pulls the latest changes from the remote repository to the local Git repository. + + Args: + repo_path (Union[str, Path]): The path to the Git repository. + + Returns: + str: Success or error message. + """ + try: + repo_path = Path(repo_path) + if not repo_path.exists(): + return "Repository path does not exist." + + repo = git.Repo(repo_path) + + # Pull latest changes + repo.remotes.origin.pull() + + return "Latest changes pulled successfully." + + except Exception as e: + return trace_exception(e) + +def git_pull_function(repo_path:Path|str): + return { + "function_name": "git_pull", + "function": partial(git_pull, repo_path=repo_path), + "function_description": "Pulls the latest changes from the remote repository to the local Git repository.", + "function_parameters": [ + {"name": "repo_path", "type": "str"} + ] + } + +if __name__ == "__main__": + # Example usage + repo_path = "path/to/repo" + print(git_pull(repo_path)) diff --git a/lollms/functions/analyze_code/list_classes.py b/lollms/functions/analyze_code/list_classes.py new file mode 100644 index 0000000..2f656d9 --- /dev/null +++ b/lollms/functions/analyze_code/list_classes.py @@ -0,0 +1,61 @@ +# Lollms function call definition file +# File Name: list_project_classes.py +# Author: Saif +# Description: This script defines a function to list all classes in a given Python project. + +# Import necessary libraries +from functools import partial +from pathlib import Path +from typing import Union, List +import ast +import sqlite3 + +# Import PackageManager if there are potential libraries that need to be installed +from lollms.utilities import PackageManager + +# ascii_colors offers advanced console coloring and bug tracing +from ascii_colors import trace_exception + +# Here is the core of the function to be built +def list_project_classes(project_path: Union[str, Path]) -> List[str]: + """ + Lists all classes in a given Python project. + + Args: + project_path (Union[str, Path]): The path to the Python project directory. + + Returns: + List[str]: A list of class names found in the project. + """ + try: + project_path = Path(project_path) + + # Validate the project path + if not project_path.exists() or not project_path.is_dir(): + return ["Invalid project path."] + + class_names = [] + + # Traverse the project directory and extract class names + for py_file in project_path.rglob("*.py"): + with open(py_file, "r", encoding="utf-8") as file: + content = file.read() + tree = ast.parse(content) + + for node in ast.walk(tree): + if isinstance(node, ast.ClassDef): + class_names.append(node.name) + + return class_names + + except Exception as e: + return [trace_exception(e)] + +# Metadata function +def list_project_classes_function(project_path): + return { + "function_name": "list_project_classes", + "function": partial(list_project_classes, project_path=project_path), + "function_description": "Lists all classes in a given Python project.", + "function_parameters": [] + } diff --git a/lollms/functions/analyze_code/list_files.py b/lollms/functions/analyze_code/list_files.py new file mode 100644 index 0000000..c64c4df --- /dev/null +++ b/lollms/functions/analyze_code/list_files.py @@ -0,0 +1,51 @@ +# Lollms function call definition file +# File Name: list_project_structure.py +# Author: ParisNeo +# Description: This function lists and displays the structure of the project directory. + +from functools import partial +from pathlib import Path +from typing import Union, List +from ascii_colors import trace_exception + +def list_project_structure(project_path: Union[str, Path]) -> str: + """ + Lists and displays the structure of the project directory, excluding certain directories. + + Args: + project_path (Union[str, Path]): The path to the project directory. + + Returns: + str: A string representation of the project directory structure. + """ + try: + project_path = Path(project_path) + + # Validate the project path + if not project_path.exists() or not project_path.is_dir(): + return "Invalid project path." + + # Directories to exclude + exclude_dirs = {'.git', '.vscode', '__pycache__'} + + structure = [] + for file in project_path.rglob("*"): + # Skip excluded directories + if any(part in exclude_dirs or part.endswith('.egg-info') for part in file.parts): + continue + + indent_level = len(file.relative_to(project_path).parts) - 1 + structure.append(f"{' ' * indent_level}{file.name}") + + return "\n".join(structure) + + except Exception as e: + return trace_exception(e) + +def list_project_structure_function(project_path: Union[str, Path]): + return { + "function_name": "list_project_structure", + "function": partial(list_project_structure, project_path=project_path), + "function_description": "Lists and displays the structure of the project directory.", + "function_parameters": [] + } diff --git a/lollms/functions/analyze_code/search_class_in_project.py b/lollms/functions/analyze_code/search_class_in_project.py new file mode 100644 index 0000000..d31c6fb --- /dev/null +++ b/lollms/functions/analyze_code/search_class_in_project.py @@ -0,0 +1,84 @@ +from typing import Optional, Union +from pathlib import Path +import sqlite3 +import difflib +import json + +from ascii_colors import trace_exception +from functools import partial + +def search_class_in_project(class_name: str, db_path: Union[str, Path]) -> Optional[str]: + """ + Searches for a specific class by name in the project database and returns detailed information. + + Args: + db_path (Union[str, Path]): The path to the project database file. + class_name (str): The name of the class to search for. + + Returns: + Optional[str]: A string with detailed information about the class, or None if not found. + """ + try: + db_path = Path(db_path) + if not db_path.exists(): + return "Database file does not exist." + + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + + # Fetch all class names to find the closest match + cursor.execute("SELECT id, name FROM classes") + classes = cursor.fetchall() + class_names = [name for _, name in classes] + closest_matches = difflib.get_close_matches(class_name, class_names, n=1, cutoff=0.1) + + if not closest_matches: + return f"No class found with a name similar to '{class_name}'." + + closest_class_name = closest_matches[0] + cursor.execute("SELECT id, file_id, name, docstring, core FROM classes WHERE name = ?", (closest_class_name,)) + class_info = cursor.fetchone() + + if not class_info: + return f"No class found with the name '{closest_class_name}'." + + class_id, file_id, name, docstring, core = class_info + + # Fetch the file path + cursor.execute("SELECT path FROM files WHERE id = ?", (file_id,)) + file_path = cursor.fetchone()[0] + + # Fetch methods of the class + cursor.execute("SELECT name, docstring, parameters FROM methods WHERE class_id = ?", (class_id,)) + methods = cursor.fetchall() + + # Construct the detailed information string + details = f"Class: {name}\n" + details += f"File: {file_path}\n" + details += f"Description: {docstring}\n\n" + details += "Methods:\n" + for method_name, method_docstring, method_parameters in methods: + details += f" - {method_name}({', '.join([f'{param[0]}: {param[1]}' for param in json.loads(method_parameters)])})\n" + details += f" Description: {method_docstring}\n" + + conn.close() + return details + + except Exception as e: + return trace_exception(e) + + +def search_class_in_project_function(project_path): + return { + "function_name": "create_project_database", + "function": partial(search_class_in_project, project_path=project_path), + "function_description": "Searches for a specific class by name in the project database and returns detailed information.", + "function_parameters": [{"name":"class_name", "type":"str", "description":"Name of the class to search"}] + } + + +if __name__ == "__main__": + # Example usage + db_path = "path/to/project_info.db" + class_name = "MyClass" + print(search_class_in_project(db_path, class_name)) diff --git a/lollms/personality.py b/lollms/personality.py index 148fc0e..853394e 100644 --- a/lollms/personality.py +++ b/lollms/personality.py @@ -3587,7 +3587,7 @@ The AI should respond in this format using data from actions_list: outputs = self.execute_function_calls(function_calls,function_definitions) final_output = "\n".join([str(o) if type(o)==str else str(o[0]) if (type(o)==tuple or type(0)==list) and len(o)>0 else "" for o in outputs]) - out += f"{separator_template}{start_header_id_template}function calls results{end_header_id_template}\n" + final_output + out += f"{separator_template}{start_header_id_template}function calls results{end_header_id_template}\n" + final_output + "\n" if prompt_after_execution: if separate_output: self.full(final_output)