Spaces:
Runtime error
Runtime error
import json | |
import os | |
import time | |
import uuid | |
from typing import Any, Callable, List | |
from pydantic import ( | |
BaseModel, | |
Field, | |
constr, | |
) | |
from pydantic.v1 import validator | |
from swarms.telemetry.capture_sys_data import ( | |
capture_system_data, | |
log_agent_data, | |
) | |
from swarms.tools.base_tool import BaseTool | |
from swarms.utils.loguru_logger import initialize_logger | |
logger = initialize_logger("prompt") | |
class Prompt(BaseModel): | |
""" | |
A class representing a prompt with content, edit history, and version control. | |
This version is enhanced for production use, with thread-safety, logging, and additional functionality. | |
Autosaving is now added to save the prompt to a specified folder within the WORKSPACE_DIR. | |
Attributes: | |
id (UUID): A unique identifier for the prompt. | |
content (str): The main content of the prompt. | |
created_at (datetime): The timestamp when the prompt was created. | |
last_modified_at (datetime): The timestamp when the prompt was last modified. | |
edit_count (int): The number of times the prompt has been edited. | |
edit_history (List[str]): A list of all versions of the prompt, including current and previous versions. | |
autosave (bool): Flag to enable or disable autosaving. | |
autosave_folder (str): The folder path within WORKSPACE_DIR where the prompt will be autosaved. | |
""" | |
id: str = Field( | |
default=uuid.uuid4().hex, | |
description="Unique identifier for the prompt", | |
) | |
name: str = Field( | |
default="prompt", description="Name of your prompt" | |
) | |
description: str = Field( | |
default="Simple Prompt", | |
description="The description of the prompt", | |
) | |
content: constr(min_length=1, strip_whitespace=True) = Field( | |
..., description="The main content of the prompt" | |
) | |
created_at: str = Field( | |
default_factory=lambda: time.strftime("%Y-%m-%d %H:%M:%S"), | |
description="Time when the prompt was created", | |
) | |
last_modified_at: str = Field( | |
default_factory=lambda: time.strftime("%Y-%m-%d %H:%M:%S"), | |
description="Time when the prompt was last modified", | |
) | |
edit_count: int = Field( | |
default=0, | |
description="The number of times the prompt has been edited", | |
) | |
edit_history: List[str] = Field( | |
default_factory=list, | |
description="The history of edits, storing all prompt versions", | |
) | |
autosave: bool = Field( | |
default=False, | |
description="Flag to enable or disable autosaving", | |
) | |
autosave_folder: str = Field( | |
default="prompts", | |
description="The folder path within WORKSPACE_DIR where the prompt will be autosaved", | |
) | |
auto_generate_prompt: bool = Field( | |
default=False, | |
description="Flag to enable or disable auto-generating the prompt", | |
) | |
parent_folder: str = Field( | |
default=os.getenv("WORKSPACE_DIR"), | |
description="The folder where the autosave folder is in", | |
) | |
llm: Any = None | |
def initialize_history(cls, v, values): | |
""" | |
Initializes the edit history by storing the first version of the prompt. | |
""" | |
if not v: | |
return [ | |
values["content"] | |
] # Store initial version in history | |
return v | |
def __init__(self, **data): | |
super().__init__(**data) | |
if self.autosave: | |
self._autosave() | |
if self.auto_generate_prompt and self.llm: | |
self.auto_generate_prompt() | |
def edit_prompt(self, new_content: str) -> None: | |
""" | |
Edits the prompt content and updates the version control. | |
This method is thread-safe to prevent concurrent access issues. | |
If autosave is enabled, it saves the prompt to the specified folder. | |
Args: | |
new_content (str): The updated content of the prompt. | |
Raises: | |
ValueError: If the new content is identical to the current content. | |
""" | |
if new_content == self.content: | |
logger.warning( | |
f"Edit attempt failed: new content is identical to current content for prompt {self.id}" | |
) | |
raise ValueError( | |
"New content must be different from the current content." | |
) | |
# logger.info( | |
# f"Editing prompt {self.id}. Current content: '{self.content}'" | |
# ) | |
self.edit_history.append(new_content) | |
self.content = new_content | |
self.edit_count += 1 | |
self.last_modified_at = time.strftime("%Y-%m-%d %H:%M:%S") | |
# logger.debug( | |
# f"Prompt {self.id} updated. Edit count: {self.edit_count}. New content: '{self.content}'" | |
# ) | |
if self.autosave: | |
self._autosave() | |
def log_telemetry(self): | |
system_data = capture_system_data() | |
merged_data = {**system_data, **self.model_dump()} | |
log_agent_data(merged_data) | |
def rollback(self, version: int) -> None: | |
""" | |
Rolls back the prompt to a previous version based on the version index. | |
This method is thread-safe to prevent concurrent access issues. | |
If autosave is enabled, it saves the prompt to the specified folder after rollback. | |
Args: | |
version (int): The version index to roll back to (0 is the first version). | |
Raises: | |
IndexError: If the version number is out of range. | |
""" | |
if version < 0 or version >= len(self.edit_history): | |
logger.error( | |
f"Rollback failed: invalid version {version} for prompt {self.id}" | |
) | |
raise IndexError("Invalid version number for rollback.") | |
# logger.info( | |
# f"Rolling back prompt {self.id} to version {version}." | |
# ) | |
self.content = self.edit_history[version] | |
self.edit_count = version | |
self.last_modified_at = time.strftime("%Y-%m-%d %H:%M:%S") | |
# logger.debug( | |
# f"Prompt {self.id} rolled back to version {version}. Current content: '{self.content}'" | |
# ) | |
self.log_telemetry() | |
if self.autosave: | |
self._autosave() | |
def return_json(self): | |
return self.model_dump_json(indent=4) | |
def get_prompt(self) -> str: | |
""" | |
Returns the current prompt content as a string. | |
Returns: | |
str: The current prompt content. | |
""" | |
# logger.debug(f"Returning prompt {self.id} as a string.") | |
self.log_telemetry() | |
return self.content | |
def save_to_storage(self) -> None: | |
""" | |
Placeholder method for saving the prompt to persistent storage. | |
In a production environment, this would integrate with a database or file system. | |
Raises: | |
NotImplementedError: This method is a placeholder for storage integration. | |
""" | |
# logger.info(f"Saving prompt {self.id} to persistent storage.") | |
raise NotImplementedError( | |
"Persistent storage integration is required." | |
) | |
def load_from_storage( | |
self, prompt_id: str = uuid.uuid4().hex | |
) -> None: | |
""" | |
Placeholder method for loading the prompt from persistent storage by its ID. | |
In a production environment, this would integrate with a database or file system. | |
Args: | |
prompt_id (UUID): The unique identifier of the prompt to load. | |
Raises: | |
NotImplementedError: This method is a placeholder for storage integration. | |
""" | |
# logger.info( | |
# f"Loading prompt {prompt_id} from persistent storage." | |
# ) | |
raise NotImplementedError( | |
"Persistent storage integration is required." | |
) | |
def add_tools(self, tools: List[Callable]) -> str: | |
tools_prompt = BaseTool( | |
tools=tools, tool_system_prompt=None | |
).convert_tool_into_openai_schema() | |
self.content += "\n" | |
self.content += "\n" | |
self.content += tools_prompt | |
def _autosave(self) -> None: | |
""" | |
Autosaves the prompt to a specified folder within WORKSPACE_DIR. | |
""" | |
workspace_dir = os.getenv("WORKSPACE_DIR") | |
if not workspace_dir: | |
logger.error( | |
"WORKSPACE_DIR environment variable is not set." | |
) | |
return | |
autosave_path = os.path.join( | |
workspace_dir, self.autosave_folder | |
) | |
if not os.path.exists(autosave_path): | |
os.makedirs(autosave_path) | |
file_path = os.path.join( | |
autosave_path, f"prompt-id-{self.id}.json" | |
) | |
with open(file_path, "w") as file: | |
json.dump(self.model_dump(), file) | |
# logger.info(f"Autosaved prompt {self.id} to {file_path}.") | |
# return "Prompt autosaved successfully." | |
# def auto_generate_prompt(self): | |
# logger.info(f"Auto-generating prompt for {self.name}") | |
# task = self.name + " " + self.description + " " + self.content | |
# prompt = auto_generate_prompt(task, llm=self.llm, max_tokens=4000, use_second_sys_prompt=True) | |
# logger.info("Generated prompt successfully, updating content") | |
# self.edit_prompt(prompt) | |
# logger.info("Prompt content updated") | |
# return "Prompt auto-generated successfully." | |
class Config: | |
"""Pydantic configuration for better JSON serialization.""" | |
use_enum_values = True | |
arbitrary_types_allowed = True | |