diff --git a/lollms/personality.py b/lollms/personality.py
index 511feec..cc1dd54 100644
--- a/lollms/personality.py
+++ b/lollms/personality.py
@@ -31,7 +31,7 @@ from ascii_colors import ASCIIColors
import time
from lollms.types import MSG_OPERATION_TYPE, SUMMARY_MODE
import json
-from typing import Any, List, Optional, Type, Callable, Dict, Any, Union
+from typing import Any, List, Optional, Type, Callable, Dict, Any, Union, Tuple
import json
from lollmsvectordb.vector_database import VectorDatabase
from lollmsvectordb.text_document_loader import TextDocumentsLoader
@@ -3123,28 +3123,84 @@ Use this structure:
prompt_parts[sacrifice_id] = sacrifice_text
return self.separator_template.join([s for s in prompt_parts if s!=""])
# ================================================= Sending commands to ui ===========================================
- def add_collapsible_entry(self, title, content, subtitle="", open_by_default=False):
+ def add_collapsible_entry(self, title, content, subtitle="", open_by_default=False, icon=None, type="default"):
+ """
+ Creates a collapsible entry with enhanced styling and animations.
+
+ Args:
+ title (str): The main title of the collapsible
+ content (str): The content to be displayed when expanded
+ subtitle (str): Optional subtitle text
+ open_by_default (bool): Whether the collapsible should be open by default
+ icon (str): Optional custom icon SVG string
+ type (str): Type of collapsible ('default', 'success', 'warning', 'error', 'info')
+
+ Returns:
+ str: HTML string for the collapsible element
+ """
+ # Color schemes for different types
+ color_schemes = {
+ "default": "border-gray-200 bg-white hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-800 dark:hover:bg-gray-750",
+ "success": "border-green-200 bg-green-50 hover:bg-green-100 dark:border-green-700 dark:bg-green-900/20 dark:hover:bg-green-900/30",
+ "warning": "border-yellow-200 bg-yellow-50 hover:bg-yellow-100 dark:border-yellow-700 dark:bg-yellow-900/20 dark:hover:bg-yellow-900/30",
+ "error": "border-red-200 bg-red-50 hover:bg-red-100 dark:border-red-700 dark:bg-red-900/20 dark:hover:bg-red-900/30",
+ "info": "border-blue-200 bg-blue-50 hover:bg-blue-100 dark:border-blue-700 dark:bg-blue-900/20 dark:hover:bg-blue-900/30"
+ }
+
+ # Default arrow icon if no custom icon is provided
+ default_icon = '''
+
+ '''
+
+ icon_html = icon if icon else default_icon
+ color_scheme = color_schemes.get(type, color_schemes["default"])
open_attr = 'open' if open_by_default else ''
+
return "\n".join([
- f'',
- f' ',
- f'
',
- f'
',
- f' ',
- f'
',
- f'
',
- f'
{title}
',
- f'
{subtitle}
',
- f'
',
- f'
',
- f' ',
- f'
',
- f'
{content}
',
- f'
',
- f'\n'
- ])
+ f'''
+
+
+
+
+{icon_html}
+
+
+
+{title}
+
+{f'
{subtitle}
' if subtitle else ''}
+
+
+
+
+
+
+
+
+{content}
+
+
+
+'''
+])
@@ -3651,29 +3707,35 @@ Use this structure:
return updated_content, True # Section updated successfully
- def extract_code_blocks(self, text: str) -> List[dict]:
+ def extract_code_blocks(self, text: str, return_remaining_text: bool = False) -> Union[List[dict], Tuple[List[dict], str]]:
"""
- This function extracts code blocks from a given text.
+ This function extracts code blocks from a given text and optionally returns the text without code blocks.
Parameters:
text (str): The text from which to extract code blocks. Code blocks are identified by triple backticks (```).
+ return_remaining_text (bool): If True, also returns the text with code blocks removed.
Returns:
- List[dict]: A list of dictionaries where each dictionary represents a code block and contains the following keys:
- - 'index' (int): The index of the code block in the text.
- - 'file_name' (str): The name of the file extracted from the preceding line, if available.
- - 'content' (str): The content of the code block.
- - 'type' (str): The type of the code block. If the code block starts with a language specifier (like 'python' or 'java'), this field will contain that specifier. Otherwise, it will be set to 'language-specific'.
- - 'is_complete' (bool): True if the block has a closing tag, False otherwise.
-
- Note:
- The function assumes that the number of triple backticks in the text is even.
- If the number of triple backticks is odd, it will consider the rest of the text as the last code block.
+ Union[List[dict], Tuple[List[dict], str]]:
+ - If return_remaining_text is False: Returns only the list of code block dictionaries
+ - If return_remaining_text is True: Returns a tuple containing:
+ * List of code block dictionaries
+ * String containing the text with all code blocks removed
+
+ Each code block dictionary contains:
+ - 'index' (int): The index of the code block in the text
+ - 'file_name' (str): The name of the file extracted from the preceding line, if available
+ - 'content' (str): The content of the code block
+ - 'type' (str): The type of the code block
+ - 'is_complete' (bool): True if the block has a closing tag, False otherwise
"""
remaining = text
bloc_index = 0
first_index = 0
indices = []
+ text_without_blocks = text
+
+ # Find all code block delimiters
while len(remaining) > 0:
try:
index = remaining.index("```")
@@ -3689,16 +3751,27 @@ Use this structure:
code_blocks = []
is_start = True
+
+ # Process code blocks and build text without blocks if requested
+ if return_remaining_text:
+ text_parts = []
+ last_end = 0
+
for index, code_delimiter_position in enumerate(indices):
- block_infos = {
- 'index': index,
- 'file_name': "",
- 'section': "",
- 'content': "",
- 'type': "",
- 'is_complete': False
- }
if is_start:
+ block_infos = {
+ 'index': len(code_blocks),
+ 'file_name': "",
+ 'section': "",
+ 'content': "",
+ 'type': "",
+ 'is_complete': False
+ }
+
+ # Store text before code block if returning remaining text
+ if return_remaining_text:
+ text_parts.append(text[last_end:code_delimiter_position].strip())
+
# Check the preceding line for file name
preceding_text = text[:code_delimiter_position].strip().splitlines()
if preceding_text:
@@ -3727,6 +3800,7 @@ Use this structure:
if '{' in sub_text[:next_index]:
next_index = 0
start_pos = next_index
+
if code_delimiter_position + 3 < len(text) and text[code_delimiter_position + 3] in ["\n", " ", "\t"]:
block_infos["type"] = 'language-specific'
else:
@@ -3740,17 +3814,32 @@ Use this structure:
else:
block_infos["content"] = sub_text[start_pos:next_pos].strip()
block_infos["is_complete"] = False
+
+ if return_remaining_text:
+ last_end = indices[index + 1] + 3
else:
block_infos["content"] = sub_text[start_pos:].strip()
block_infos["is_complete"] = False
+
+ if return_remaining_text:
+ last_end = len(text)
+
code_blocks.append(block_infos)
is_start = False
else:
is_start = True
- continue
-
+
+ if return_remaining_text:
+ # Add any remaining text after the last code block
+ if last_end < len(text):
+ text_parts.append(text[last_end:].strip())
+ # Join all non-code parts with newlines
+ text_without_blocks = '\n'.join(filter(None, text_parts))
+ return code_blocks, text_without_blocks
+
return code_blocks
+
def build_and_execute_python_code(self,context, instructions, execution_function_signature, extra_imports=""):
start_header_id_template = self.config.start_header_id_template
end_header_id_template = self.config.end_header_id_template
@@ -4185,9 +4274,9 @@ Use this structure:
self.print_prompt("Generated", generated_text)
# Extract the function calls from the generated text.
- function_calls = self.extract_function_calls_as_json(generated_text)
+ function_calls, text_without_code = self.extract_function_calls_as_json(generated_text)
- return generated_text, function_calls
+ return generated_text, function_calls, text_without_code
def generate_with_function_calls_and_images(self, context_details: dict, images:list, functions: List[Dict[str, Any]], max_answer_length: Optional[int] = None, callback = None) -> List[Dict[str, Any]]:
@@ -4209,9 +4298,9 @@ Use this structure:
generated_text = self.fast_gen_with_images(upgraded_prompt, images, max_answer_length, callback=callback)
# Extract the function calls from the generated text.
- function_calls = self.extract_function_calls_as_json(generated_text)
+ function_calls, text_without_code = self.extract_function_calls_as_json(generated_text)
- return generated_text, function_calls
+ return generated_text, function_calls, text_without_code
def execute_function(self, code, function_definitions = None):
function_call = json.loads(code)
@@ -4361,7 +4450,7 @@ Use this structure:
"""
# Extract markdown code blocks that contain JSON.
- code_blocks = self.extract_code_blocks(text)
+ code_blocks, text_without_code = self.extract_code_blocks(text, True)
# Filter out and parse JSON entries.
function_calls = []
@@ -4379,7 +4468,7 @@ Use this structure:
# If the content is not valid JSON, skip it.
continue
- return function_calls
+ return function_calls, text_without_code
def interact(
@@ -4408,15 +4497,15 @@ Use this structure:
final_output = ""
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, text_without_code = self.generate_with_function_calls_and_images(context_details, self.personality.image_files, function_definitions, callback=callback)
else:
- out, function_calls = self.generate_with_function_calls(context_details, function_definitions, callback=callback)
+ out, function_calls, text_without_code = self.generate_with_function_calls(context_details, function_definitions, callback=callback)
nested_function_calls = 0
while len(function_calls)>0 and nested_function_calls0:
- out, function_calls = self.generate_with_function_calls_and_images(context_details, self.personality.image_files, function_definitions, callback=callback)
+ out, function_calls, twc = self.generate_with_function_calls_and_images(context_details, self.personality.image_files, function_definitions, callback=callback)
else:
- out, function_calls = self.generate_with_function_calls(context_details, function_definitions, callback=callback)
+ out, function_calls, twc = self.generate_with_function_calls(context_details, function_definitions, callback=callback)
final_output += "\n" + out
+ text_without_code += twc
else:
final_output = out
return final_output