mirror of
https://github.com/ParisNeo/lollms.git
synced 2025-01-21 03:55:01 +00:00
enhanced functions
This commit is contained in:
parent
a52db1fb9f
commit
7da874228b
7
lollms/functions/analyze_code/__init__.py
Normal file
7
lollms/functions/analyze_code/__init__.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
from lollms.functions.analyze_code.create_project_database import create_project_database, create_project_database_function
|
||||||
|
from lollms.functions.analyze_code.retreive_information_for_task import retrieve_information_for_task, retrieve_information_for_task_function
|
||||||
|
from lollms.functions.analyze_code.retrieve_classes_from_project import retrieve_classes_from_project, retrieve_classes_from_project_function
|
||||||
|
from lollms.functions.analyze_code.update_class_in_file import update_class_in_file, update_class_in_file_function
|
||||||
|
from lollms.functions.analyze_code.update_function_in_file import update_function_in_file, update_function_in_file_function
|
||||||
|
from lollms.functions.analyze_code.add_function_to_file import add_function_to_file, add_function_to_file_function
|
||||||
|
|
58
lollms/functions/analyze_code/add_function_to_file.py
Normal file
58
lollms/functions/analyze_code/add_function_to_file.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# Lollms function call definition file
|
||||||
|
from functools import partial
|
||||||
|
from lollms.utilities import PackageManager
|
||||||
|
from ascii_colors import trace_exception
|
||||||
|
from typing import List, Union
|
||||||
|
from pathlib import Path
|
||||||
|
import ast
|
||||||
|
|
||||||
|
def add_function_to_file(file_path: Union[str, Path], function_name: str, function_content: str) -> str:
|
||||||
|
"""
|
||||||
|
Adds a new function to the specified file.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
file_path (Union[str, Path]): The path to the Python file.
|
||||||
|
function_name (str): The name of the function to add.
|
||||||
|
function_content (str): The content of the function to add.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Success message or error message.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
file_path = Path(file_path)
|
||||||
|
|
||||||
|
# Validate the file path
|
||||||
|
if not file_path.exists() or not file_path.is_file():
|
||||||
|
return "Invalid file path."
|
||||||
|
|
||||||
|
with open(file_path, "r", encoding="utf-8") as file:
|
||||||
|
content = file.read()
|
||||||
|
tree = ast.parse(content)
|
||||||
|
|
||||||
|
# Check if function already exists
|
||||||
|
for node in ast.walk(tree):
|
||||||
|
if isinstance(node, ast.FunctionDef) and node.name == function_name:
|
||||||
|
return f"Function {function_name} already exists in the file."
|
||||||
|
|
||||||
|
# Add the new function content at the end of the file
|
||||||
|
with open(file_path, "a", encoding="utf-8") as file:
|
||||||
|
file.write("\n\n" + function_content)
|
||||||
|
|
||||||
|
return f"Function {function_name} added successfully to file {file_path}."
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return trace_exception(e)
|
||||||
|
|
||||||
|
|
||||||
|
# Metadata function
|
||||||
|
def add_function_to_file_function():
|
||||||
|
return {
|
||||||
|
"function_name": "add_function_to_file", # The function name in string
|
||||||
|
"function": add_function_to_file, # The function to be called
|
||||||
|
"function_description": "Adds a new function to the specified file.", # Description of the function
|
||||||
|
"function_parameters": [ # The set of parameters
|
||||||
|
{"name": "file_path", "type": "str"},
|
||||||
|
{"name": "function_name", "type": "str"},
|
||||||
|
{"name": "function_content", "type": "str"},
|
||||||
|
]
|
||||||
|
}
|
@ -1,4 +1,8 @@
|
|||||||
# Lollms function call definition file
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from typing import List, Dict, Union, Any
|
from typing import List, Dict, Union, Any
|
||||||
from lollms.utilities import PackageManager
|
from lollms.utilities import PackageManager
|
||||||
@ -129,102 +133,4 @@ def create_project_database_function(project_path, llm):
|
|||||||
"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_description": "Creates a database containing structured information about a Python project.",
|
||||||
"function_parameters": []
|
"function_parameters": []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Lollms function call definition file
|
|
||||||
from functools import partial
|
|
||||||
from typing import List, Dict
|
|
||||||
from lollms.utilities import PackageManager
|
|
||||||
from ascii_colors import trace_exception
|
|
||||||
from pathlib import Path
|
|
||||||
import sqlite3
|
|
||||||
|
|
||||||
# Ensure required packages are installed
|
|
||||||
if not PackageManager.check_package_installed("sqlite3"):
|
|
||||||
PackageManager.install_package("sqlite3")
|
|
||||||
|
|
||||||
def retrieve_information_for_task(project_path: str, task_description: str, llm: APScript) -> Union[str, Dict[str, str]]:
|
|
||||||
"""
|
|
||||||
Retrieves information from the database to perform a task given by the user.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
project_path (str): The path to the project directory.
|
|
||||||
task_description (str): The description of the task to perform.
|
|
||||||
llm (APScript): The language model instance for generating SQL queries.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Union[str, Dict[str, str]]: A string containing relevant information or an error message.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
db_path = Path(project_path) / "project_info.db"
|
|
||||||
|
|
||||||
# Validate the database path
|
|
||||||
if not db_path.exists() or not db_path.is_file():
|
|
||||||
return "Invalid database path."
|
|
||||||
|
|
||||||
conn = sqlite3.connect(db_path)
|
|
||||||
cursor = conn.cursor()
|
|
||||||
|
|
||||||
# Retrieve the list of classes and their descriptions
|
|
||||||
cursor.execute("SELECT name, docstring FROM classes")
|
|
||||||
classes = cursor.fetchall()
|
|
||||||
|
|
||||||
# Format the classes into a string
|
|
||||||
classes_text = "\n".join([f"Class: {cls[0]}, Description: {cls[1]}" for cls in classes])
|
|
||||||
|
|
||||||
# Ask the LLM which classes are needed for the task
|
|
||||||
prompt = f"{llm.personality.config.start_header_id_template}{llm.personality.config.system_message_template}{llm.personality.config.end_header_id_template}" \
|
|
||||||
f"Given the following list of classes and their descriptions:\n" \
|
|
||||||
f"{classes_text}\n\n" \
|
|
||||||
f"Task description: {task_description}\n\n" \
|
|
||||||
f"{llm.personality.config.start_header_id_template}instructions{llm.personality.config.end_header_id_template}" \
|
|
||||||
f"Which classes are needed to perform the task? List the class names.\n" \
|
|
||||||
f"Answer in form of a json list inside a json markdown tag.\n" \
|
|
||||||
f"{llm.personality.config.start_header_id_template}assistant{llm.personality.config.end_header_id_template}"
|
|
||||||
|
|
||||||
needed_classes = llm.fast_gen(prompt, callback=llm.sink).strip()
|
|
||||||
needed_classes = llm.extract_code_blocks(needed_classes)
|
|
||||||
if len(needed_classes)>0:
|
|
||||||
needed_classes = json.loads(needed_classes[0]["content"])
|
|
||||||
# Retrieve the relevant information for the needed classes
|
|
||||||
class_info = {}
|
|
||||||
for class_name in needed_classes:
|
|
||||||
cursor.execute("SELECT * FROM classes WHERE name = ?", (class_name,))
|
|
||||||
class_info[class_name] = cursor.fetchone()
|
|
||||||
|
|
||||||
# Retrieve the project description and structure
|
|
||||||
cursor.execute("SELECT name, description FROM project_info")
|
|
||||||
project_info = cursor.fetchone()
|
|
||||||
|
|
||||||
cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
|
|
||||||
tables = cursor.fetchall()
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
# Format the results into a string
|
|
||||||
result_text = f"Project Name: {project_info[0]}\nProject Description: {project_info[1]}\n\n"
|
|
||||||
result_text += "Project Structure:\n" + "\n".join([table[0] for table in tables]) + "\n\n"
|
|
||||||
result_text += "Needed Classes Information:\n"
|
|
||||||
for class_name, info in class_info.items():
|
|
||||||
result_text += f"Class: {class_name}\n"
|
|
||||||
result_text += f"Description: {info[2]}\n"
|
|
||||||
result_text += f"Methods: {info[4]}\n"
|
|
||||||
result_text += f"Static Methods: {info[5]}\n\n"
|
|
||||||
|
|
||||||
return result_text.strip()
|
|
||||||
else:
|
|
||||||
return "Failed to ask the llm"
|
|
||||||
except Exception as e:
|
|
||||||
return str(e)
|
|
||||||
|
|
||||||
def retrieve_information_for_task_function(project_path, llm):
|
|
||||||
return {
|
|
||||||
"function_name": "retrieve_information_for_task",
|
|
||||||
"function": partial(retrieve_information_for_task, project_path=project_path, llm=llm),
|
|
||||||
"function_description": "Retrieves information from the database to perform a task given by the user.",
|
|
||||||
"function_parameters": [
|
|
||||||
{"name": "task_description", "type": "str", "description":"a description of "}
|
|
||||||
]
|
|
||||||
}
|
|
@ -0,0 +1,96 @@
|
|||||||
|
# Lollms function call definition file
|
||||||
|
from functools import partial
|
||||||
|
from typing import List, Dict, Union
|
||||||
|
from lollms.utilities import PackageManager
|
||||||
|
from lollms.personality import APScript
|
||||||
|
from ascii_colors import trace_exception
|
||||||
|
from pathlib import Path
|
||||||
|
import sqlite3
|
||||||
|
import json
|
||||||
|
# Ensure required packages are installed
|
||||||
|
if not PackageManager.check_package_installed("sqlite3"):
|
||||||
|
PackageManager.install_package("sqlite3")
|
||||||
|
|
||||||
|
def retrieve_information_for_task(project_path: str, task_description: str, llm: APScript) -> Union[str, Dict[str, str]]:
|
||||||
|
"""
|
||||||
|
Retrieves information from the database to perform a task given by the user.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
project_path (str): The path to the project directory.
|
||||||
|
task_description (str): The description of the task to perform.
|
||||||
|
llm (APScript): The language model instance for generating SQL queries.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Union[str, Dict[str, str]]: A string containing relevant information or an error message.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
db_path = Path(project_path) / "project_info.db"
|
||||||
|
|
||||||
|
# Validate the database path
|
||||||
|
if not db_path.exists() or not db_path.is_file():
|
||||||
|
return "Invalid database path."
|
||||||
|
|
||||||
|
conn = sqlite3.connect(db_path)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Retrieve the list of classes and their descriptions
|
||||||
|
cursor.execute("SELECT name, docstring FROM classes")
|
||||||
|
classes = cursor.fetchall()
|
||||||
|
|
||||||
|
# Format the classes into a string
|
||||||
|
classes_text = "\n".join([f"Class: {cls[0]}, Description: {cls[1]}" for cls in classes])
|
||||||
|
|
||||||
|
# Ask the LLM which classes are needed for the task
|
||||||
|
prompt = f"{llm.personality.config.start_header_id_template}{llm.personality.config.system_message_template}{llm.personality.config.end_header_id_template}" \
|
||||||
|
f"Given the following list of classes and their descriptions:\n" \
|
||||||
|
f"{classes_text}\n\n" \
|
||||||
|
f"Task description: {task_description}\n\n" \
|
||||||
|
f"{llm.personality.config.start_header_id_template}instructions{llm.personality.config.end_header_id_template}" \
|
||||||
|
f"Which classes are needed to perform the task? List the class names.\n" \
|
||||||
|
f"Answer in form of a json list inside a json markdown tag.\n" \
|
||||||
|
f"{llm.personality.config.start_header_id_template}assistant{llm.personality.config.end_header_id_template}"
|
||||||
|
|
||||||
|
needed_classes = llm.fast_gen(prompt, callback=llm.sink).strip()
|
||||||
|
needed_classes = llm.extract_code_blocks(needed_classes)
|
||||||
|
if len(needed_classes)>0:
|
||||||
|
needed_classes = json.loads(needed_classes[0]["content"])
|
||||||
|
# Retrieve the relevant information for the needed classes
|
||||||
|
class_info = {}
|
||||||
|
for class_name in needed_classes:
|
||||||
|
cursor.execute("SELECT * FROM classes WHERE name = ?", (class_name,))
|
||||||
|
class_info[class_name] = cursor.fetchone()
|
||||||
|
|
||||||
|
# Retrieve the project description and structure
|
||||||
|
cursor.execute("SELECT name, description FROM project_info")
|
||||||
|
project_info = cursor.fetchone()
|
||||||
|
|
||||||
|
cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
|
||||||
|
tables = cursor.fetchall()
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
# Format the results into a string
|
||||||
|
result_text = f"Project Name: {project_info[0]}\nProject Description: {project_info[1]}\n\n"
|
||||||
|
result_text += "Project Structure:\n" + "\n".join([table[0] for table in tables]) + "\n\n"
|
||||||
|
result_text += "Needed Classes Information:\n"
|
||||||
|
for class_name, info in class_info.items():
|
||||||
|
result_text += f"Class: {class_name}\n"
|
||||||
|
result_text += f"Description: {info[2]}\n"
|
||||||
|
result_text += f"Methods: {info[4]}\n"
|
||||||
|
result_text += f"Static Methods: {info[5]}\n\n"
|
||||||
|
|
||||||
|
return result_text.strip()
|
||||||
|
else:
|
||||||
|
return "Failed to ask the llm"
|
||||||
|
except Exception as e:
|
||||||
|
return str(e)
|
||||||
|
|
||||||
|
def retrieve_information_for_task_function(project_path, llm):
|
||||||
|
return {
|
||||||
|
"function_name": "retrieve_information_for_task",
|
||||||
|
"function": partial(retrieve_information_for_task, project_path=project_path, llm=llm),
|
||||||
|
"function_description": "Retrieves information from the database to perform a task given by the user.",
|
||||||
|
"function_parameters": [
|
||||||
|
{"name": "task_description", "type": "str", "description":"a description of "}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
# Import necessary libraries
|
||||||
|
from pathlib import Path
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
|
from typing import List, Union
|
||||||
|
import ast
|
||||||
|
|
||||||
|
# ascii_colors offers advanced console coloring and bug tracing
|
||||||
|
from ascii_colors import trace_exception
|
||||||
|
|
||||||
|
# Function to retrieve classes from the project files
|
||||||
|
def retrieve_classes_from_project(class_names: List[str], project_path: Union[str, Path]) -> str:
|
||||||
|
"""
|
||||||
|
Retrieves the code of specified classes from the given project path.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
project_path (Union[str, Path]): The path to the Python project directory.
|
||||||
|
class_names (List[str]): List of class names to retrieve.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: The code of the specified classes as a string.
|
||||||
|
"""
|
||||||
|
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_code = "\n"
|
||||||
|
|
||||||
|
# Traverse the project directory and extract class code
|
||||||
|
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) and node.name in class_names:
|
||||||
|
class_code += f"\n\n# Class: {node.name} in file: {py_file.relative_to(project_path)}\n"
|
||||||
|
class_code += "```python\n"
|
||||||
|
class_code += "\n".join(content.split("\n")[node.lineno-1:node.end_lineno])
|
||||||
|
class_code += "\n```\n"
|
||||||
|
|
||||||
|
return class_code if class_code else "No specified classes found."
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return trace_exception(e)
|
||||||
|
|
||||||
|
# Metadata function
|
||||||
|
def retrieve_classes_from_project_function(project_path:str):
|
||||||
|
return {
|
||||||
|
"function_name": "retrieve_classes_from_project",
|
||||||
|
"function": partial(retrieve_classes_from_project,project_path = project_path),
|
||||||
|
"function_description": "Retrieves the code of specified classes from the given project path.",
|
||||||
|
"function_parameters": [
|
||||||
|
{"name": "class_names", "type": "list", "description": "List of class names to retrieve."}
|
||||||
|
]
|
||||||
|
}
|
85
lollms/functions/analyze_code/update_class_in_file.py
Normal file
85
lollms/functions/analyze_code/update_class_in_file.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
# Lollms function call definition file
|
||||||
|
from functools import partial
|
||||||
|
from lollms.utilities import PackageManager
|
||||||
|
from ascii_colors import trace_exception
|
||||||
|
from typing import List, Union
|
||||||
|
from pathlib import Path
|
||||||
|
import ast
|
||||||
|
def update_class_in_file(class_name: str, new_content: str, method_name: Union[str, None] = None, project_path: Union[str, Path] = "") -> str:
|
||||||
|
"""
|
||||||
|
Updates the specified class or method with new content in the given project path.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
class_name (str): The name of the class to update.
|
||||||
|
new_content (str): The new content to replace the class or method with.
|
||||||
|
method_name (Union[str, None]): The name of the method to update. If None, updates the entire class.
|
||||||
|
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."
|
||||||
|
|
||||||
|
# Traverse the project directory and update class or method content
|
||||||
|
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)
|
||||||
|
|
||||||
|
class_found = False
|
||||||
|
method_found = False
|
||||||
|
updated_content = []
|
||||||
|
lines = content.split("\n")
|
||||||
|
|
||||||
|
for node in ast.walk(tree):
|
||||||
|
if isinstance(node, ast.ClassDef) and node.name == class_name:
|
||||||
|
class_found = True
|
||||||
|
if method_name is None:
|
||||||
|
# Update entire class content
|
||||||
|
updated_content.extend(lines[:node.lineno-1])
|
||||||
|
updated_content.append(new_content)
|
||||||
|
updated_content.extend(lines[node.end_lineno:])
|
||||||
|
else:
|
||||||
|
# Update specific method content
|
||||||
|
for class_node in node.body:
|
||||||
|
if isinstance(class_node, ast.FunctionDef) and class_node.name == method_name:
|
||||||
|
method_found = True
|
||||||
|
# Calculate the indentation level
|
||||||
|
indent_level = len(lines[class_node.lineno-1]) - len(lines[class_node.lineno-1].lstrip())
|
||||||
|
indented_new_content = "\n".join(" " * indent_level + line for line in new_content.split("\n"))
|
||||||
|
updated_content.extend(lines[:class_node.lineno-1])
|
||||||
|
updated_content.append(indented_new_content)
|
||||||
|
updated_content.extend(lines[class_node.end_lineno:])
|
||||||
|
break
|
||||||
|
if not method_found:
|
||||||
|
return f"Method {method_name} not found in class {class_name}."
|
||||||
|
break
|
||||||
|
|
||||||
|
if class_found:
|
||||||
|
with open(py_file, "w", encoding="utf-8") as file:
|
||||||
|
file.write("\n".join(updated_content))
|
||||||
|
return f"{'Method ' + method_name if method_name else 'Class ' + class_name} updated successfully in file {py_file.relative_to(project_path)}."
|
||||||
|
|
||||||
|
return "Class not found in the specified project."
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return trace_exception(e)
|
||||||
|
|
||||||
|
|
||||||
|
# Metadata function
|
||||||
|
def update_class_in_file_function(project_path:str ):
|
||||||
|
return {
|
||||||
|
"function_name": "update_class_in_file", # The function name in string
|
||||||
|
"function": partial(update_class_in_file,project_path = project_path), # The function to be called
|
||||||
|
"function_description": "Updates the specified class with new content in the given project path.", # Description of the function
|
||||||
|
"function_parameters": [ # The set of parameters
|
||||||
|
{"name": "class_name", "type": "str"},
|
||||||
|
{"name": "new_content", "type": "str"},
|
||||||
|
{"name": "method_name", "type": "str", "description":"An optional method name, required only if you need to change a single method content. If you need to change the whole class, do not set this element"},
|
||||||
|
]
|
||||||
|
}
|
70
lollms/functions/analyze_code/update_function_in_file.py
Normal file
70
lollms/functions/analyze_code/update_function_in_file.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
# Lollms function call definition file
|
||||||
|
from functools import partial
|
||||||
|
from lollms.utilities import PackageManager
|
||||||
|
from ascii_colors import trace_exception
|
||||||
|
from typing import List, Union
|
||||||
|
from pathlib import Path
|
||||||
|
import ast
|
||||||
|
|
||||||
|
def update_function_in_file(function_name: str, new_content: str, project_path: Union[str, Path] = "") -> str:
|
||||||
|
"""
|
||||||
|
Updates the specified function with new content in the given project path.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
function_name (str): The name of the function to update.
|
||||||
|
new_content (str): The new content to replace the function with.
|
||||||
|
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."
|
||||||
|
|
||||||
|
# Traverse the project directory and update function content
|
||||||
|
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)
|
||||||
|
|
||||||
|
function_found = False
|
||||||
|
updated_content = []
|
||||||
|
lines = content.split("\n")
|
||||||
|
|
||||||
|
for node in ast.walk(tree):
|
||||||
|
if isinstance(node, ast.FunctionDef) and node.name == function_name:
|
||||||
|
function_found = True
|
||||||
|
# Calculate the indentation level
|
||||||
|
indent_level = len(lines[node.lineno-1]) - len(lines[node.lineno-1].lstrip())
|
||||||
|
indented_new_content = "\n".join(" " * indent_level + line for line in new_content.split("\n"))
|
||||||
|
updated_content.extend(lines[:node.lineno-1])
|
||||||
|
updated_content.append(indented_new_content)
|
||||||
|
updated_content.extend(lines[node.end_lineno:])
|
||||||
|
break
|
||||||
|
|
||||||
|
if function_found:
|
||||||
|
with open(py_file, "w", encoding="utf-8") as file:
|
||||||
|
file.write("\n".join(updated_content))
|
||||||
|
return f"Function {function_name} updated successfully in file {py_file.relative_to(project_path)}."
|
||||||
|
|
||||||
|
return "Function not found in the specified project."
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return trace_exception(e)
|
||||||
|
|
||||||
|
|
||||||
|
# Metadata function
|
||||||
|
def update_function_in_file_function(project_path:str ):
|
||||||
|
return {
|
||||||
|
"function_name": "update_function_in_file", # The function name in string
|
||||||
|
"function": partial(update_function_in_file, project_path=project_path), # The function to be called
|
||||||
|
"function_description": "Updates the specified function with new content in the given project path.", # Description of the function
|
||||||
|
"function_parameters": [ # The set of parameters
|
||||||
|
{"name": "function_name", "type": "str"},
|
||||||
|
{"name": "new_content", "type": "str"},
|
||||||
|
]
|
||||||
|
}
|
75
lollms/functions/image_management/draw_bounding_boxes.py
Normal file
75
lollms/functions/image_management/draw_bounding_boxes.py
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
# Lollms function call definition file
|
||||||
|
# File Name: draw_bounding_boxes.py
|
||||||
|
# Author: Saif (ParisNeo)
|
||||||
|
# Description: This script defines a function that takes an image file path and a list of bounding boxes in the form of x, y, w, h in normalized mode along with the labels, and returns the bounding boxes placed on top of the images with the labels.
|
||||||
|
|
||||||
|
# Lollms function call definition file
|
||||||
|
from functools import partial
|
||||||
|
from typing import List, Tuple
|
||||||
|
from lollms.utilities import PackageManager
|
||||||
|
from ascii_colors import trace_exception
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Ensure necessary packages are installed
|
||||||
|
if not PackageManager.check_package_installed("Pillow"):
|
||||||
|
PackageManager.install_package("Pillow")
|
||||||
|
|
||||||
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
|
|
||||||
|
def draw_bounding_boxes(image_path: str, bounding_boxes: List[Tuple[float, float, float, float, str]]) -> str:
|
||||||
|
"""
|
||||||
|
Draws bounding boxes on an image and saves the result.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
image_path (str): Path to the input image file.
|
||||||
|
bounding_boxes (List[Tuple[float, float, float, float, str]]): List of bounding boxes in normalized coordinates (x, y, w, h) along with labels.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Path to the output image file with bounding boxes drawn.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
image_path = Path(image_path)
|
||||||
|
if not image_path.exists():
|
||||||
|
raise FileNotFoundError(f"Image file not found: {image_path}")
|
||||||
|
|
||||||
|
# Load the image
|
||||||
|
image = Image.open(image_path)
|
||||||
|
draw = ImageDraw.Draw(image)
|
||||||
|
width, height = image.size
|
||||||
|
|
||||||
|
# Iterate over bounding boxes and draw them
|
||||||
|
for bbox in bounding_boxes:
|
||||||
|
x, y, w, h, label = bbox
|
||||||
|
left = x * width
|
||||||
|
top = y * height
|
||||||
|
right = left + (w * width)
|
||||||
|
bottom = top + (h * height)
|
||||||
|
|
||||||
|
# Draw rectangle
|
||||||
|
draw.rectangle([left, top, right, bottom], outline="red", width=2)
|
||||||
|
|
||||||
|
# Draw label
|
||||||
|
font = ImageFont.load_default()
|
||||||
|
text_size = draw.textsize(label, font=font)
|
||||||
|
text_background = [left, top - text_size[1], left + text_size[0], top]
|
||||||
|
draw.rectangle(text_background, fill="red")
|
||||||
|
draw.text((left, top - text_size[1]), label, fill="white", font=font)
|
||||||
|
|
||||||
|
# Save the output image
|
||||||
|
output_path = image_path.with_name(f"{image_path.stem}_with_boxes{image_path.suffix}")
|
||||||
|
image.save(output_path)
|
||||||
|
|
||||||
|
return str(output_path)
|
||||||
|
except Exception as e:
|
||||||
|
return trace_exception(e)
|
||||||
|
|
||||||
|
def draw_bounding_boxes_function():
|
||||||
|
return {
|
||||||
|
"function_name": "draw_bounding_boxes",
|
||||||
|
"function": draw_bounding_boxes,
|
||||||
|
"function_description": "Draws bounding boxes on an image and saves the result.",
|
||||||
|
"function_parameters": [
|
||||||
|
{"name": "image_path", "type": "str"},
|
||||||
|
{"name": "bounding_boxes", "type": "List[Tuple[float, float, float, float, str]]"}
|
||||||
|
]
|
||||||
|
}
|
@ -1,4 +1,8 @@
|
|||||||
# Lollms function call definition file
|
# Lollms function call definition file
|
||||||
|
# File Name: set_timer_with_alert.py
|
||||||
|
# Author: Saif (ParisNeo)
|
||||||
|
# Description: This function sets a non-blocking timer that shows a PyQt window with a message and makes noise after a specified duration. It works on any operating system by using the pyautogui library for the alert sound.
|
||||||
|
|
||||||
# Here you need to import any necessary imports depending on the function requested by the user
|
# Here you need to import any necessary imports depending on the function requested by the user
|
||||||
# example import math
|
# example import math
|
||||||
|
|
||||||
@ -6,7 +10,7 @@
|
|||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
# It is advised to import typing elements
|
# It is advised to import typing elements
|
||||||
# from typing import List
|
from typing import Any, Dict
|
||||||
|
|
||||||
# Import PackageManager if there are potential libraries that need to be installed
|
# Import PackageManager if there are potential libraries that need to be installed
|
||||||
from lollms.utilities import PackageManager
|
from lollms.utilities import PackageManager
|
||||||
@ -17,30 +21,35 @@ from ascii_colors import trace_exception
|
|||||||
# Here is an example of how we install a non installed library using PackageManager
|
# Here is an example of how we install a non installed library using PackageManager
|
||||||
if not PackageManager.check_package_installed("PyQt5"):
|
if not PackageManager.check_package_installed("PyQt5"):
|
||||||
PackageManager.install_package("PyQt5")
|
PackageManager.install_package("PyQt5")
|
||||||
|
if not PackageManager.check_package_installed("pyautogui"):
|
||||||
|
PackageManager.install_package("pyautogui")
|
||||||
|
|
||||||
# now we can import the library
|
# now we can import the libraries
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
import sys
|
import sys
|
||||||
from PyQt5.QtWidgets import QApplication, QMessageBox
|
from PyQt5.QtWidgets import QApplication, QMessageBox
|
||||||
import winsound
|
import pyautogui
|
||||||
|
|
||||||
# here is the core of the function to be built
|
# here is the core of the function to be built
|
||||||
def set_timer_with_alert(duration: int, message: str) -> str:
|
def set_timer_with_alert(duration: int, message: str) -> str:
|
||||||
def timer_callback():
|
"""
|
||||||
if type(duration)==str and len(duration)>0:
|
Sets a non-blocking timer that shows a PyQt window with a message and makes noise after a specified duration.
|
||||||
duration = float(duration.split()[0])
|
|
||||||
time.sleep(duration)
|
|
||||||
winsound.Beep(1000, 1000) # Make noise when time is up
|
|
||||||
|
|
||||||
app = QApplication(sys.argv)
|
Parameters:
|
||||||
msg_box = QMessageBox()
|
duration (int): The duration for the timer in seconds.
|
||||||
msg_box.setIcon(QMessageBox.Information)
|
message (str): The message to be displayed in the alert window.
|
||||||
msg_box.setWindowTitle("Timer Alert")
|
|
||||||
msg_box.setText(message)
|
Returns:
|
||||||
msg_box.setStandardButtons(QMessageBox.Ok)
|
str: A success message indicating the timer has been set.
|
||||||
msg_box.buttonClicked.connect(app.quit)
|
"""
|
||||||
msg_box.exec_()
|
def timer_callback():
|
||||||
|
try:
|
||||||
|
time.sleep(duration)
|
||||||
|
pyautogui.alert(text=message, title="Timer Alert", button='OK')
|
||||||
|
pyautogui.beep()
|
||||||
|
except Exception as e:
|
||||||
|
return trace_exception(e)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Start the timer in a new thread to make it non-blocking
|
# Start the timer in a new thread to make it non-blocking
|
||||||
@ -53,7 +62,7 @@ def set_timer_with_alert(duration: int, message: str) -> str:
|
|||||||
return trace_exception(e)
|
return trace_exception(e)
|
||||||
|
|
||||||
# Here is the metadata function that should have the name in format function_name_function
|
# Here is the metadata function that should have the name in format function_name_function
|
||||||
def set_timer_with_alert_function(processor, client):
|
def set_timer_with_alert_function() -> Dict[str, Any]:
|
||||||
return {
|
return {
|
||||||
"function_name": "set_timer_with_alert", # The function name in string
|
"function_name": "set_timer_with_alert", # The function name in string
|
||||||
"function": set_timer_with_alert, # The function to be called
|
"function": set_timer_with_alert, # The function to be called
|
||||||
|
0
lollms/functions/tts/__init__.py
Normal file
0
lollms/functions/tts/__init__.py
Normal file
45
lollms/functions/tts/read_text.py
Normal file
45
lollms/functions/tts/read_text.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# Lollms function call definition file
|
||||||
|
|
||||||
|
# Import necessary modules
|
||||||
|
from pathlib import Path
|
||||||
|
from functools import partial
|
||||||
|
from typing import Union
|
||||||
|
from lollms.utilities import PackageManager
|
||||||
|
from lollms.personality import APScript
|
||||||
|
from lollms.tts import LollmsTTS
|
||||||
|
from safe_store import GenericDataLoader
|
||||||
|
from ascii_colors import trace_exception
|
||||||
|
|
||||||
|
# Here is the core of the function to be built
|
||||||
|
def read_text(text: str, tts_module:LollmsTTS, llm:APScript) -> str:
|
||||||
|
"""
|
||||||
|
This function takes a TTS module and a file path as input, reads the text from the file,
|
||||||
|
and uses the TTS module to generate audio from the text.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
tts_module: The text-to-speech module with a method tts_audio.
|
||||||
|
text: The text to be read.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: The path to the generated audio file.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Generate audio from the text
|
||||||
|
audio_file_path = tts_module.tts_audio(text)
|
||||||
|
llm.chunk(text)
|
||||||
|
llm.new_message("")
|
||||||
|
|
||||||
|
# Return the path to the generated audio file
|
||||||
|
return str(audio_file_path)
|
||||||
|
except Exception as e:
|
||||||
|
return trace_exception(e)
|
||||||
|
|
||||||
|
|
||||||
|
# Metadata function
|
||||||
|
def read_text_function(file_path:str,tts_module:LollmsTTS):
|
||||||
|
return {
|
||||||
|
"function_name": "read_text_from_file", # The function name in string
|
||||||
|
"function": partial(read_text, file_path=file_path, tts_module=tts_module), # The function to be called
|
||||||
|
"function_description": "Reads text from a file and uses a TTS module to generate audio from the text.", # Description of the function
|
||||||
|
"function_parameters": [] # The set of parameters
|
||||||
|
}
|
50
lollms/functions/tts/read_text_from_file.py
Normal file
50
lollms/functions/tts/read_text_from_file.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# Lollms function call definition file
|
||||||
|
|
||||||
|
# Import necessary modules
|
||||||
|
from pathlib import Path
|
||||||
|
from functools import partial
|
||||||
|
from typing import Union
|
||||||
|
from lollms.utilities import PackageManager
|
||||||
|
from lollms.personality import APScript
|
||||||
|
from lollms.tts import LollmsTTS
|
||||||
|
from safe_store import GenericDataLoader
|
||||||
|
from ascii_colors import trace_exception
|
||||||
|
|
||||||
|
# Here is the core of the function to be built
|
||||||
|
def read_text_from_file(file_path: Union[Path, str], tts_module:LollmsTTS, llm:APScript) -> str:
|
||||||
|
"""
|
||||||
|
This function takes a TTS module and a file path as input, reads the text from the file,
|
||||||
|
and uses the TTS module to generate audio from the text.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
tts_module: The text-to-speech module with a method tts_audio.
|
||||||
|
file_path: The path to the text file containing the text to be read.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: The path to the generated audio file.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Ensure file_path is of type Path
|
||||||
|
file_path = Path(file_path)
|
||||||
|
|
||||||
|
# Read the text from the file
|
||||||
|
text = GenericDataLoader.read_file(file_path)
|
||||||
|
|
||||||
|
# Generate audio from the text
|
||||||
|
audio_file_path = tts_module.tts_audio(text,use_threading=True)
|
||||||
|
llm.full(text)
|
||||||
|
|
||||||
|
# Return the path to the generated audio file
|
||||||
|
return str(audio_file_path)
|
||||||
|
except Exception as e:
|
||||||
|
return trace_exception(e)
|
||||||
|
|
||||||
|
|
||||||
|
# Metadata function
|
||||||
|
def read_text_from_file_function(file_path:str,tts_module:LollmsTTS, llm:APScript):
|
||||||
|
return {
|
||||||
|
"function_name": "read_text_from_file", # The function name in string
|
||||||
|
"function": partial(read_text_from_file, file_path=file_path, tts_module=tts_module, llm=llm), # The function to be called
|
||||||
|
"function_description": "Reads text from the current file and uses a TTS module to generate audio from the text.", # Description of the function
|
||||||
|
"function_parameters": [] # The set of parameters
|
||||||
|
}
|
1
lollms/functions/web/__init__.py
Normal file
1
lollms/functions/web/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from lollms.functions.web.google_search import google_search, google_search_function
|
46
lollms/functions/web/google_search.py
Normal file
46
lollms/functions/web/google_search.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# Lollms function call definition file
|
||||||
|
|
||||||
|
# Import necessary libraries
|
||||||
|
from functools import partial
|
||||||
|
from typing import List
|
||||||
|
from lollms.utilities import PackageManager
|
||||||
|
from ascii_colors import trace_exception
|
||||||
|
|
||||||
|
# Ensure the webbrowser package is available
|
||||||
|
if not PackageManager.check_package_installed("webbrowser"):
|
||||||
|
PackageManager.install_package("webbrowser")
|
||||||
|
|
||||||
|
# Import the webbrowser library
|
||||||
|
import webbrowser
|
||||||
|
|
||||||
|
# Define the function to perform a Google search
|
||||||
|
def google_search(query: str) -> str:
|
||||||
|
"""
|
||||||
|
Perform a Google search using the default web browser.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- query (str): The search query.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- str: A message indicating the search was performed.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Construct the Google search URL
|
||||||
|
search_url = f"https://www.google.com/search?q={query}"
|
||||||
|
|
||||||
|
# Open the search URL in the default web browser
|
||||||
|
webbrowser.open(search_url)
|
||||||
|
|
||||||
|
# Return a success message
|
||||||
|
return f"Performed Google search for query: {query}"
|
||||||
|
except Exception as e:
|
||||||
|
return trace_exception(e)
|
||||||
|
|
||||||
|
# Define the metadata function
|
||||||
|
def google_search_function():
|
||||||
|
return {
|
||||||
|
"function_name": "google_search", # The function name in string
|
||||||
|
"function": google_search, # The function to be called
|
||||||
|
"function_description": "Performs a Google search using the default web browser.", # Description of the function
|
||||||
|
"function_parameters": [{"name": "query", "type": "str"}] # The set of parameters
|
||||||
|
}
|
@ -3394,6 +3394,7 @@ The AI should respond in this format using data from actions_list:
|
|||||||
result = function(**parameters)
|
result = function(**parameters)
|
||||||
results.append(result)
|
results.append(result)
|
||||||
except TypeError as e:
|
except TypeError as e:
|
||||||
|
trace_exception(e)
|
||||||
# Handle cases where the function call fails due to incorrect parameters, etc.
|
# Handle cases where the function call fails due to incorrect parameters, etc.
|
||||||
results.append(f"Error calling {function_name}: {e}")
|
results.append(f"Error calling {function_name}: {e}")
|
||||||
else:
|
else:
|
||||||
@ -3521,6 +3522,20 @@ The AI should respond in this format using data from actions_list:
|
|||||||
return function_calls
|
return function_calls
|
||||||
|
|
||||||
|
|
||||||
|
def interact(
|
||||||
|
self,
|
||||||
|
context_details,
|
||||||
|
callback = None
|
||||||
|
):
|
||||||
|
upgraded_prompt = self.build_prompt_from_context_details(context_details)
|
||||||
|
if len(self.personality.image_files)>0:
|
||||||
|
# Generate the initial text based on the upgraded prompt.
|
||||||
|
generated_text = self.fast_gen_with_images(upgraded_prompt, self.personality.image_files, callback=callback)
|
||||||
|
else:
|
||||||
|
generated_text = self.fast_gen(upgraded_prompt, callback=callback)
|
||||||
|
|
||||||
|
return generated_text
|
||||||
|
|
||||||
def interact_with_function_call(
|
def interact_with_function_call(
|
||||||
self,
|
self,
|
||||||
context_details,
|
context_details,
|
||||||
@ -3528,24 +3543,26 @@ The AI should respond in this format using data from actions_list:
|
|||||||
prompt_after_execution=True,
|
prompt_after_execution=True,
|
||||||
callback = None,
|
callback = None,
|
||||||
hide_function_call=False,
|
hide_function_call=False,
|
||||||
separate_output=False):
|
separate_output=False,
|
||||||
|
max_nested_function_calls=10):
|
||||||
|
|
||||||
start_header_id_template = self.config.start_header_id_template
|
start_header_id_template = self.config.start_header_id_template
|
||||||
end_header_id_template = self.config.end_header_id_template
|
end_header_id_template = self.config.end_header_id_template
|
||||||
system_message_template = self.config.system_message_template
|
system_message_template = self.config.system_message_template
|
||||||
separator_template = self.config.separator_template
|
separator_template = self.config.separator_template
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
final_output = ""
|
final_output = ""
|
||||||
if len(self.personality.image_files)>0:
|
if len(self.personality.image_files)>0:
|
||||||
out, function_calls = self.generate_with_function_calls_and_images(context_details, self.personality.image_files, function_definitions, callback=callback)
|
out, function_calls = self.generate_with_function_calls_and_images(context_details, self.personality.image_files, function_definitions, callback=callback)
|
||||||
else:
|
else:
|
||||||
out, function_calls = self.generate_with_function_calls(context_details, function_definitions, callback=callback)
|
out, function_calls = self.generate_with_function_calls(context_details, function_definitions, callback=callback)
|
||||||
if len(function_calls)>0:
|
nested_function_calls = 0
|
||||||
|
while len(function_calls)>0 and nested_function_calls<max_nested_function_calls:
|
||||||
|
nested_function_calls += 1
|
||||||
|
self.chunk("\n")
|
||||||
if hide_function_call:
|
if hide_function_call:
|
||||||
self.full("") #Hide function
|
self.full("") #Hide function
|
||||||
|
|
||||||
if self.config.debug:
|
if self.config.debug:
|
||||||
self.print_prompt("Function calls", json.dumps(function_calls, indent=4))
|
self.print_prompt("Function calls", json.dumps(function_calls, indent=4))
|
||||||
|
|
||||||
@ -3562,11 +3579,6 @@ The AI should respond in this format using data from actions_list:
|
|||||||
else:
|
else:
|
||||||
out, function_calls = self.generate_with_function_calls(context_details, function_definitions, callback=callback)
|
out, function_calls = self.generate_with_function_calls(context_details, function_definitions, callback=callback)
|
||||||
final_output += "\n" + out
|
final_output += "\n" + out
|
||||||
if len(function_calls)>0:
|
|
||||||
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
|
|
||||||
context_details["discussion_messages"] +=out
|
|
||||||
else:
|
else:
|
||||||
final_output = out
|
final_output = out
|
||||||
return final_output
|
return final_output
|
||||||
|
Loading…
Reference in New Issue
Block a user