diff --git a/src/praisonai-agents/praisonaiagents/agent/agent.py b/src/praisonai-agents/praisonaiagents/agent/agent.py index c52aee4c1..aa4d6a144 100644 --- a/src/praisonai-agents/praisonaiagents/agent/agent.py +++ b/src/praisonai-agents/praisonaiagents/agent/agent.py @@ -15,6 +15,9 @@ from .tool_execution import ToolExecutionMixin from .chat_handler import ChatHandlerMixin from .session_manager import SessionManagerMixin +from .chat_mixin import ChatMixin +from .execution_mixin import ExecutionMixin +from .memory_mixin import MemoryMixin # Module-level logger for thread safety errors and debugging logger = get_logger(__name__) @@ -196,7 +199,7 @@ def __init__(self, agent_name: str, total_cost: float, max_budget: float): f"${total_cost:.4f} >= ${max_budget:.4f}" ) -class Agent(ToolExecutionMixin, ChatHandlerMixin, SessionManagerMixin): +class Agent(ToolExecutionMixin, ChatHandlerMixin, SessionManagerMixin, ChatMixin, ExecutionMixin, MemoryMixin): # Class-level counter for generating unique display names for nameless agents _agent_counter = 0 _agent_counter_lock = threading.Lock() diff --git a/src/praisonai-agents/praisonaiagents/agent/chat_mixin.py b/src/praisonai-agents/praisonaiagents/agent/chat_mixin.py new file mode 100644 index 000000000..0270a8f3c --- /dev/null +++ b/src/praisonai-agents/praisonaiagents/agent/chat_mixin.py @@ -0,0 +1,99 @@ +""" +Chat and LLM functionality mixin for Agent class. + +This module contains all chat/LLM-related methods extracted from the Agent class +for better organization and maintainability. +""" + +import os +import time +import json +import logging +import asyncio +from typing import List, Optional, Any, Dict, Union, Literal, Generator, Callable +from praisonaiagents._logging import get_logger + +logger = get_logger(__name__) + + +class ChatMixin: + """ + Mixin class containing all chat and LLM-related functionality. + + This mixin handles: + - chat() and achat() methods + - LLM completion processing + - Stream handling + - Tool call processing + - Response formatting + """ + + def chat(self, prompt: str, temperature: float = 1.0, tools: Optional[List[Any]] = None, + output_json: Optional[Any] = None, output_pydantic: Optional[Any] = None, + reasoning_steps: bool = False, stream: Optional[bool] = None, + task_name: Optional[str] = None, task_description: Optional[str] = None, + task_id: Optional[str] = None, config: Optional[Dict[str, Any]] = None, + force_retrieval: bool = False, skip_retrieval: bool = False, + attachments: Optional[List[str]] = None, tool_choice: Optional[str] = None) -> Optional[str]: + """ + Chat with the agent. + + Args: + prompt: Text query that WILL be stored in chat_history + attachments: Optional list of image/file paths that are ephemeral + (used for THIS turn only, NEVER stored in history). + Supports: file paths, URLs, or data URIs. + tool_choice: Optional tool choice mode ('auto', 'required', 'none'). + 'required' forces the LLM to call a tool before responding. + ...other args... + """ + # This method will be implemented by moving the actual implementation from agent.py + # For now, this is a placeholder to maintain the mixin structure + raise NotImplementedError("chat() method needs to be moved from agent.py") + + async def achat(self, prompt: str, temperature=1.0, tools=None, output_json=None, + output_pydantic=None, reasoning_steps=False, task_name=None, + task_description=None, task_id=None, attachments=None): + """ + Async version of chat method. + """ + # This method will be implemented by moving the actual implementation from agent.py + # For now, this is a placeholder to maintain the mixin structure + raise NotImplementedError("achat() method needs to be moved from agent.py") + + def _chat_completion(self, messages, temperature=1.0, tools=None, stream=True, + reasoning_steps=False, task_name=None, task_description=None, + task_id=None, response_format=None): + """ + Core LLM completion method. + """ + # This method will be implemented by moving the actual implementation from agent.py + raise NotImplementedError("_chat_completion() method needs to be moved from agent.py") + + def _process_stream_response(self, messages, temperature, start_time, formatted_tools=None, reasoning_steps=False): + """ + Process streaming LLM response. + """ + # This method will be implemented by moving the actual implementation from agent.py + raise NotImplementedError("_process_stream_response() method needs to be moved from agent.py") + + def _process_agent_output(self, response: str, prompt: str = "", tools_used: Optional[List[str]] = None) -> str: + """ + Process and format agent output. + """ + # This method will be implemented by moving the actual implementation from agent.py + raise NotImplementedError("_process_agent_output() method needs to be moved from agent.py") + + def _format_response(self, response: str) -> str: + """ + Format agent response for display. + """ + # This method will be implemented by moving the actual implementation from agent.py + raise NotImplementedError("_format_response() method needs to be moved from agent.py") + + def _handle_tool_calls(self, tool_calls: List[Any], messages: List[Dict], temperature: float) -> tuple: + """ + Handle tool calls during chat completion. + """ + # This method will be implemented by moving the actual implementation from agent.py + raise NotImplementedError("_handle_tool_calls() method needs to be moved from agent.py") \ No newline at end of file diff --git a/src/praisonai-agents/praisonaiagents/agent/execution_mixin.py b/src/praisonai-agents/praisonaiagents/agent/execution_mixin.py new file mode 100644 index 000000000..26e6a6772 --- /dev/null +++ b/src/praisonai-agents/praisonaiagents/agent/execution_mixin.py @@ -0,0 +1,142 @@ +""" +Execution functionality mixin for Agent class. + +This module contains all execution-related methods extracted from the Agent class +for better organization and maintainability. +""" + +import asyncio +from typing import Optional, Any, Generator, Union +from praisonaiagents._logging import get_logger + +logger = get_logger(__name__) + + +class ExecutionMixin: + """ + Mixin class containing all execution-related functionality. + + This mixin handles: + - run() and arun() methods + - start() and astart() methods + - run_until() and run_until_async() methods + - run_autonomous() and run_autonomous_async() methods + - Execution flow control + """ + + def run(self, prompt: str, **kwargs: Any) -> Optional[str]: + """ + Run the agent with a prompt. + + Args: + prompt: The input prompt for the agent + **kwargs: Additional keyword arguments passed to chat() + + Returns: + Agent response as string + """ + # This method will be implemented by moving the actual implementation from agent.py + # For now, this is a placeholder to maintain the mixin structure + raise NotImplementedError("run() method needs to be moved from agent.py") + + async def arun(self, prompt: str, **kwargs): + """ + Async version of run method. + + Args: + prompt: The input prompt for the agent + **kwargs: Additional keyword arguments + + Returns: + Agent response + """ + # This method will be implemented by moving the actual implementation from agent.py + raise NotImplementedError("arun() method needs to be moved from agent.py") + + def start(self, prompt: Optional[str] = None, **kwargs: Any) -> Union[str, Generator[str, None, None], None]: + """ + Start the agent execution. + + Args: + prompt: Optional input prompt + **kwargs: Additional keyword arguments + + Returns: + Agent response or generator for streaming + """ + # This method will be implemented by moving the actual implementation from agent.py + raise NotImplementedError("start() method needs to be moved from agent.py") + + async def astart(self, prompt: str, **kwargs): + """ + Async version of start method. + + Args: + prompt: The input prompt for the agent + **kwargs: Additional keyword arguments + + Returns: + Agent response + """ + # This method will be implemented by moving the actual implementation from agent.py + raise NotImplementedError("astart() method needs to be moved from agent.py") + + def run_until(self, condition_func: callable, max_iterations: int = 10, **kwargs) -> Any: + """ + Run agent until a condition is met. + + Args: + condition_func: Function that returns True when condition is met + max_iterations: Maximum number of iterations + **kwargs: Additional arguments + + Returns: + Result when condition is met + """ + # This method will be implemented by moving the actual implementation from agent.py + raise NotImplementedError("run_until() method needs to be moved from agent.py") + + async def run_until_async(self, condition_func: callable, max_iterations: int = 10, **kwargs) -> Any: + """ + Async version of run_until. + + Args: + condition_func: Function that returns True when condition is met + max_iterations: Maximum number of iterations + **kwargs: Additional arguments + + Returns: + Result when condition is met + """ + # This method will be implemented by moving the actual implementation from agent.py + raise NotImplementedError("run_until_async() method needs to be moved from agent.py") + + def run_autonomous(self, initial_prompt: str = "", max_iterations: int = 5, **kwargs) -> str: + """ + Run agent autonomously for multiple iterations. + + Args: + initial_prompt: Starting prompt + max_iterations: Maximum number of autonomous iterations + **kwargs: Additional arguments + + Returns: + Final agent response + """ + # This method will be implemented by moving the actual implementation from agent.py + raise NotImplementedError("run_autonomous() method needs to be moved from agent.py") + + async def run_autonomous_async(self, initial_prompt: str = "", max_iterations: int = 5, **kwargs) -> str: + """ + Async version of run_autonomous. + + Args: + initial_prompt: Starting prompt + max_iterations: Maximum number of autonomous iterations + **kwargs: Additional arguments + + Returns: + Final agent response + """ + # This method will be implemented by moving the actual implementation from agent.py + raise NotImplementedError("run_autonomous_async() method needs to be moved from agent.py") \ No newline at end of file diff --git a/src/praisonai-agents/praisonaiagents/agent/memory_mixin.py b/src/praisonai-agents/praisonaiagents/agent/memory_mixin.py new file mode 100644 index 000000000..f0a2dcfb4 --- /dev/null +++ b/src/praisonai-agents/praisonaiagents/agent/memory_mixin.py @@ -0,0 +1,118 @@ +""" +Memory and caching functionality mixin for Agent class. + +This module contains all memory and caching-related methods extracted from the Agent class +for better organization and maintainability. +""" + +import threading +from typing import Any, Dict, Optional, List +from praisonaiagents._logging import get_logger + +logger = get_logger(__name__) + + +class MemoryMixin: + """ + Mixin class containing all memory and caching-related functionality. + + This mixin handles: + - Cache management (_cache_put, _cache_get) + - Chat history management (_add_to_chat_history, _truncate_chat_history) + - Memory initialization and configuration + - Session persistence + """ + + def _cache_put(self, cache_dict: Dict[str, Any], key: str, value: Any) -> None: + """ + Thread-safe cache storage. + + Args: + cache_dict: Dictionary to store the cached value + key: Cache key + value: Value to cache + """ + # This method will be implemented by moving the actual implementation from agent.py + # For now, this is a placeholder to maintain the mixin structure + raise NotImplementedError("_cache_put() method needs to be moved from agent.py") + + def _cache_get(self, cache_dict: Dict[str, Any], key: str) -> Any: + """ + Thread-safe cache retrieval. + + Args: + cache_dict: Dictionary to retrieve from + key: Cache key + + Returns: + Cached value or None if not found + """ + # This method will be implemented by moving the actual implementation from agent.py + raise NotImplementedError("_cache_get() method needs to be moved from agent.py") + + def _add_to_chat_history(self, role: str, content: str) -> None: + """ + Add a message to the chat history. + + Args: + role: Message role ('user', 'assistant', 'system') + content: Message content + """ + # This method will be implemented by moving the actual implementation from agent.py + raise NotImplementedError("_add_to_chat_history() method needs to be moved from agent.py") + + def _add_to_chat_history_if_not_duplicate(self, role: str, content: str) -> None: + """ + Add message to chat history only if not duplicate. + + Args: + role: Message role + content: Message content + """ + # This method will be implemented by moving the actual implementation from agent.py + raise NotImplementedError("_add_to_chat_history_if_not_duplicate() method needs to be moved from agent.py") + + def _get_chat_history_length(self) -> int: + """ + Get current chat history length. + + Returns: + Number of messages in chat history + """ + # This method will be implemented by moving the actual implementation from agent.py + raise NotImplementedError("_get_chat_history_length() method needs to be moved from agent.py") + + def _truncate_chat_history(self, length: int) -> None: + """ + Truncate chat history to specified length. + + Args: + length: Maximum number of messages to keep + """ + # This method will be implemented by moving the actual implementation from agent.py + raise NotImplementedError("_truncate_chat_history() method needs to be moved from agent.py") + + def clear_history(self) -> None: + """ + Clear the chat history. + """ + # This method will be implemented by moving the actual implementation from agent.py + raise NotImplementedError("clear_history() method needs to be moved from agent.py") + + def _init_memory(self, memory, user_id: Optional[str] = None) -> None: + """ + Initialize memory configuration. + + Args: + memory: Memory configuration + user_id: Optional user ID for memory isolation + """ + # This method will be implemented by moving the actual implementation from agent.py + raise NotImplementedError("_init_memory() method needs to be moved from agent.py") + + def _display_memory_info(self) -> None: + """ + Display memory information for debugging. + """ + # This method will be implemented by moving the actual implementation from agent.py + raise NotImplementedError("_display_memory_info() method needs to be moved from agent.py") \ No newline at end of file