├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.md ├── __init__.py ├── docs ├── api_reference.md ├── architecture_overview.md ├── developer_guide.md ├── faq.md ├── index.md ├── ovadare_logo.png └── user_guide.md ├── examples └── example_integration.py ├── ovadare ├── __init__.py ├── agents │ ├── __init__.py │ ├── agent.py │ ├── agent_interface.py │ ├── agent_registry.py │ └── agent_sdk.py ├── communication │ ├── __init__.py │ ├── api_endpoints.py │ └── security.py ├── conflicts │ ├── __init__.py │ ├── conflict.py │ ├── conflict_classifier.py │ ├── conflict_detector.py │ ├── conflict_resolver.py │ ├── resolution.py │ └── resolution_engine.py ├── core │ ├── base_classes.py │ ├── event_dispatcher.py │ ├── framework.py │ └── ioc_container.py ├── escalation │ ├── __init__.py │ ├── ai_assistant.py │ ├── escalation_manager.py │ └── human_interface.py ├── feedback │ ├── __init__.py │ ├── adaptive_learning.py │ ├── feedback_loop.py │ ├── feedback_manager.py │ └── policy_adjustment.py ├── monitoring │ ├── __init__.py │ ├── analytics.py │ ├── dashboards.py │ ├── monitoring_service.py │ └── reporting.py ├── policies │ ├── __init__.py │ ├── dynamic_policy_loader.py │ ├── dynamic_policy_manager.py │ ├── policy.py │ ├── policy_manager.py │ └── policy_rules.py ├── security │ ├── authentication.py │ └── authorization.py ├── tests │ ├── autogen_tests.py │ ├── test_agent_sdk.py │ ├── test_api_endpoints.py │ ├── test_authentication.py │ ├── test_authorization.py │ └── test_conflict_detector.py └── utils │ ├── __init__.py │ ├── configuration.py │ ├── logging_config.py │ └── secrets_manager.py ├── pyproject.toml ├── pytest.ini ├── requirements.txt ├── setup.cfg ├── setup.py └── tests ├── __init__.py ├── test_agents.py ├── test_communication.py ├── test_conflicts.py ├── test_core.py ├── test_escalation.py ├── test_feedback.py ├── test_monitoring.py ├── test_policies.py └── test_utils.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # Virtual environment 30 | venv/ 31 | .venv/ 32 | env/ 33 | .env/ 34 | ENV/ 35 | env.bak/ 36 | venv.bak/ 37 | 38 | # Pytest cache 39 | .pytest_cache/ 40 | 41 | # MyPy cache 42 | .mypy_cache/ 43 | 44 | # Pyre type checker 45 | .pyre/ 46 | 47 | # Environments 48 | .env 49 | .venv 50 | env/ 51 | venv/ 52 | ENV/ 53 | env.bak/ 54 | venv.bak/ 55 | 56 | # Jupyter Notebook 57 | .ipynb_checkpoints 58 | 59 | # Editor settings 60 | .vscode/ 61 | *.swp 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 The Python Packaging Authority 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | include LICENSE 3 | recursive-include docs * 4 | recursive-include examples * 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | ![Logo of OVADARE](./docs/ovadare_logo.png) 4 | 5 | # **OVADARE** 6 | 7 | 🤖 **OVADARE**: Cutting-edge framework for detecting, classifying, and resolving conflicts between AI agents autonomously. Designed to integrate seamlessly with AutoGen, CrewAI and other AI orchestration platforms, OVADARE empowers multi-agent systems to handle complex tasks collaboratively and efficiently. 8 | 9 |

10 | 11 | [Homepage](https://www.ovadare.com/) | [Documentation](https://docs.ovadare.com/) 12 | 13 |

14 | 15 | [![GitHub Repo stars](https://img.shields.io/github/stars/nospecs/ovadare)](https://github.com/nospecs/ovadare) 16 | [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT) 17 |
18 | 19 | ## Table of contents 20 | 21 | - [Why OVADARE?](#why-ovadare) 22 | - [Getting Started](#getting-started) 23 | - [Key Features](#key-features) 24 | - [Examples](#examples) 25 | - [Conflict Detection](#conflict-detection) 26 | - [Policy Adjustment](#policy-adjustment) 27 | - [Resolution Automation](#resolution-automation) 28 | - [Integration with Autogen](#integration-with-autogen) 29 | - [Contribution](#contribution) 30 | - [License](#license) 31 | 32 | ## Why OVADARE? 33 | 34 | In a world where multi-agent systems are becoming the backbone of AI-driven solutions, OVADARE stands out by providing robust conflict detection, classification, and resolution capabilities. Whether it's resolving task overlaps, prioritization issues, or behavioral conflicts among agents, OVADARE ensures seamless collaboration within your AI ecosystem. 35 | 36 | ## Getting Started 37 | 38 | To get started with OVADARE, follow these simple steps: 39 | 40 | ### 1. Installation 41 | 42 | Ensure you have Python >=3.8 installed on your system. OVADARE can be installed via pip: 43 | 44 | ``` 45 | pip install ovadare 46 | ``` 47 | 48 | ### 2. Setting Up OVADARE 49 | 50 | To integrate OVADARE into your project, create an OVADARE conflict resolution manager: 51 | 52 | ``` 53 | from ovadare.conflicts import ConflictDetector, ConflictResolver 54 | 55 | detector = ConflictDetector() 56 | resolver = ConflictResolver() 57 | 58 | # Detect conflicts 59 | conflicts = detector.detect_conflicts(agent_data) 60 | 61 | # Resolve conflicts 62 | resolved = resolver.resolve_conflicts(conflicts) 63 | ``` 64 | 65 | ### 3. Configuring Policies 66 | 67 | OVADARE allows you to define custom policies for conflict resolution: 68 | 69 | ``` 70 | from ovadare.policies import PolicyManager, Policy 71 | 72 | policy_manager = PolicyManager() 73 | 74 | custom_policy = Policy( 75 | name="TaskPrioritization", 76 | rules=[ 77 | "If two agents are assigned the same task, prioritize based on expertise.", 78 | "Resolve resource allocation conflicts using weighted scoring." 79 | ] 80 | ) 81 | 82 | policy_manager.add_policy(custom_policy) 83 | ``` 84 | 85 | ### 4. Running with Autogen 86 | 87 | OVADARE integrates seamlessly with Autogen for multi-agent orchestration. Define agents and tasks using Autogen and let OVADARE handle the conflicts: 88 | 89 | ``` 90 | from autogen import Agent, Task 91 | 92 | agents = [ 93 | Agent(name="Agent1", role="Planner"), 94 | Agent(name="Agent2", role="Executor") 95 | ] 96 | 97 | tasks = [ 98 | Task(name="Plan Project", assigned_to="Agent1"), 99 | Task(name="Execute Project", assigned_to="Agent2"), 100 | ] 101 | 102 | # Detect and resolve conflicts before execution 103 | conflicts = detector.detect_conflicts(tasks) 104 | resolved_tasks = resolver.resolve_conflicts(conflicts) 105 | ``` 106 | 107 | ## Key Features 108 | 109 | - **Comprehensive Conflict Handling**: Detect, classify, and resolve agent-level conflicts. 110 | - **Customizable Policies**: Define dynamic policies tailored to your specific needs. 111 | - **Seamless Integration**: Works out-of-the-box with platforms like Autogen. 112 | - **Extensibility**: Easily extend functionality with custom rules and modules. 113 | - **Advanced Analytics**: Monitor and visualize conflict trends and resolution effectiveness. 114 | 115 | ## Examples 116 | 117 | ### Conflict Detection 118 | 119 | Detect conflicts in a multi-agent system with ease: 120 | 121 | ``` 122 | conflicts = detector.detect_conflicts(agent_tasks) 123 | print(conflicts) 124 | ``` 125 | 126 | ### Policy Adjustment 127 | 128 | Dynamically adjust policies based on feedback loops: 129 | 130 | ``` 131 | policy_manager.adjust_policy("TaskPrioritization", new_rules=["If deadlines conflict, prioritize by urgency."]) 132 | ``` 133 | 134 | ### Resolution Automation 135 | 136 | Automate resolution using AI-powered decision-making engines: 137 | 138 | ``` 139 | resolved = resolver.resolve_conflicts(conflicts, method="ai-assisted") 140 | ``` 141 | 142 | ## Integration with Autogen 143 | 144 | OVADARE enhances Autogen by adding robust conflict resolution capabilities to its agent orchestration framework. By integrating OVADARE, you can ensure that your agents collaborate effectively without stepping on each other's toes. 145 | 146 | ## Contribution 147 | 148 | OVADARE is open-source, and we welcome contributions. To contribute: 149 | 150 | 1. Fork the repository. 151 | 2. Create a new branch for your feature. 152 | 3. Add your feature or improvement. 153 | 4. Send a pull request. 154 | 155 | ### Running Tests 156 | 157 | ``` 158 | pytest tests/ 159 | ``` 160 | 161 | ### Static Analysis 162 | 163 | ``` 164 | mypy ovadare/ 165 | ``` 166 | 167 | ### Packaging 168 | 169 | ``` 170 | python setup.py sdist bdist_wheel 171 | ``` 172 | 173 | ## License 174 | 175 | OVADARE is released under the [MIT License](https://github.com/ovadare/ovadare/blob/main/LICENSE). 176 | 177 | ## Supporting OVADARE 178 | Give OVADARE a ⭐ — it helps more people discover it! 179 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/api_reference.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/docs/api_reference.md -------------------------------------------------------------------------------- /docs/architecture_overview.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/docs/architecture_overview.md -------------------------------------------------------------------------------- /docs/developer_guide.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/docs/developer_guide.md -------------------------------------------------------------------------------- /docs/faq.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/docs/faq.md -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/docs/index.md -------------------------------------------------------------------------------- /docs/ovadare_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/docs/ovadare_logo.png -------------------------------------------------------------------------------- /docs/user_guide.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/docs/user_guide.md -------------------------------------------------------------------------------- /examples/example_integration.py: -------------------------------------------------------------------------------- 1 | # example_integration.py 2 | 3 | from ovadare.agents import Agent 4 | from ovadare.conflicts.conflict_detector import ConflictDetector 5 | from ovadare.conflicts.conflict_resolver import ConflictResolver 6 | from ovadare.policies.policy_manager import PolicyManager, Policy 7 | from autogen import AssistantAgent # Ensure this is correctly imported 8 | 9 | def main(): 10 | # Initialize Policy Manager 11 | policy_manager = PolicyManager() 12 | 13 | # Define Policies 14 | read_policy = Policy( 15 | name='ReadPolicy', 16 | rules={ 17 | 'access_level': 'read', 18 | 'resource_limits': 'moderate' 19 | } 20 | ) 21 | write_policy = Policy( 22 | name='WritePolicy', 23 | rules={ 24 | 'access_level': 'write', 25 | 'resource_limits': 'high' 26 | } 27 | ) 28 | 29 | # Add Policies to Policy Manager 30 | policy_manager.add_policy(read_policy) 31 | policy_manager.add_policy(write_policy) 32 | 33 | # Initialize Agents with Policies 34 | agent_a = Agent( 35 | agent_id='agent_a', 36 | name='AgentA', 37 | role='DataProcessor', 38 | policies=[read_policy] 39 | ) 40 | agent_b = Agent( 41 | agent_id='agent_b', 42 | name='AgentB', 43 | role='DataAnalyzer', 44 | policies=[write_policy] 45 | ) 46 | 47 | # Simulate Agent Actions 48 | # AgentA attempts to write data, which should cause a policy conflict 49 | agent_a_action = { 50 | 'agent': agent_a.agent_id, 51 | 'action': 'write_data', 52 | 'resource': 'Dataset1' 53 | } 54 | agent_b_action = { 55 | 'agent': agent_b.agent_id, 56 | 'action': 'read_data', 57 | 'resource': 'Dataset1' 58 | } 59 | 60 | # Collect Actions 61 | agent_actions = [agent_a_action, agent_b_action] 62 | 63 | # Initialize Conflict Detector 64 | conflict_detector = ConflictDetector() 65 | 66 | # Detect Conflicts 67 | conflicts = conflict_detector.detect(agent_id=agent_a.agent_id, action=agent_a_action) 68 | 69 | # Check if any conflicts were detected 70 | if conflicts: 71 | print("Conflicts detected:") 72 | for conflict in conflicts: 73 | print(conflict.to_dict()) 74 | 75 | # Initialize Conflict Resolver 76 | conflict_resolver = ConflictResolver(conflict_detector=conflict_detector, resolution_engine=ResolutionEngine()) 77 | 78 | # Resolve Conflicts 79 | resolutions = conflict_resolver.resolve_conflicts(conflicts) 80 | 81 | print("\nResolutions:") 82 | for resolution in resolutions: 83 | print(resolution.to_dict()) 84 | else: 85 | print("No conflicts detected. Agents can proceed with their actions.") 86 | 87 | # Integration with Autogen for Automated Testing 88 | # Set up Autogen Assistant Agent for generating test cases 89 | test_agent = AssistantAgent( 90 | name='TestAgent', 91 | system_prompt='You are an expert in testing Ovadare\'s conflict detection capabilities.', 92 | model='gpt-4' 93 | ) 94 | 95 | # Define a test scenario 96 | test_scenario = """ 97 | Given AgentA with read-only access tries to write data to Dataset1, 98 | and AgentB with write access reads data from Dataset1, 99 | test that a policy conflict is detected for AgentA and resolved appropriately. 100 | """ 101 | 102 | # Run the test scenario 103 | test_response = test_agent.run(test_scenario) 104 | print("\nAutogen Test Agent Response:") 105 | print(test_response) 106 | 107 | if __name__ == '__main__': 108 | main() 109 | -------------------------------------------------------------------------------- /ovadare/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/ovadare/__init__.py -------------------------------------------------------------------------------- /ovadare/agents/__init__.py: -------------------------------------------------------------------------------- 1 | from .agent import Agent 2 | from .agent_interface import AgentInterface 3 | 4 | __all__ = ['Agent', 'AgentInterface'] 5 | -------------------------------------------------------------------------------- /ovadare/agents/agent.py: -------------------------------------------------------------------------------- 1 | # ovadare/agents/agent.py 2 | 3 | from .agent_interface import AgentInterface 4 | from ovadare.conflicts.resolution import Resolution 5 | from typing import Dict, Any, List 6 | import logging 7 | 8 | logger = logging.getLogger(__name__) 9 | logger.setLevel(logging.DEBUG) 10 | 11 | 12 | class Agent(AgentInterface): 13 | def __init__(self, agent_id: str, name: str = "", role: str = "", policies: List = None): 14 | self._agent_id = agent_id 15 | self.name = name 16 | self.role = role 17 | self.policies = policies or [] 18 | self._capabilities = {} 19 | # Initialize other necessary attributes 20 | logger.debug(f"Agent '{self._agent_id}' initialized with name '{self.name}', role '{self.role}', and policies '{self.policies}'.") 21 | 22 | @property 23 | def agent_id(self) -> str: 24 | return self._agent_id 25 | 26 | @property 27 | def capabilities(self) -> Dict[str, Any]: 28 | return self._capabilities 29 | 30 | def initialize(self) -> None: 31 | logger.debug(f"Initializing agent {self._agent_id}") 32 | # Implement initialization logic 33 | 34 | def shutdown(self) -> None: 35 | logger.debug(f"Shutting down agent {self._agent_id}") 36 | # Implement shutdown logic 37 | 38 | def perform_action(self, action: Dict[str, Any]) -> Dict[str, Any]: 39 | logger.debug(f"Agent {self._agent_id} performing action: {action}") 40 | # Implement action logic 41 | return {"status": "success"} 42 | 43 | def report_status(self) -> Dict[str, Any]: 44 | logger.debug(f"Agent {self._agent_id} reporting status") 45 | # Implement status reporting 46 | return {"agent_id": self._agent_id, "status": "active"} 47 | 48 | def handle_event(self, event_type: str, event_data: Dict[str, Any]) -> None: 49 | logger.debug(f"Agent {self._agent_id} handling event {event_type}: {event_data}") 50 | # Implement event handling 51 | 52 | def handle_resolution(self, resolution: Resolution) -> None: 53 | logger.debug(f"Agent {self._agent_id} handling resolution: {resolution}") 54 | # Implement resolution handling 55 | -------------------------------------------------------------------------------- /ovadare/agents/agent_interface.py: -------------------------------------------------------------------------------- 1 | # ovadare/agents/agent_interface.py 2 | 3 | """ 4 | Agent Interface Module for the Ovadare Framework 5 | 6 | This module provides the AgentInterface, which defines the methods that agents must 7 | implement to interact with the Ovadare framework. Agents represent entities that perform 8 | actions within the system and may need to respond to resolutions for conflicts. 9 | """ 10 | 11 | from abc import ABC, abstractmethod 12 | from typing import Dict, Any 13 | import logging 14 | 15 | from ovadare.conflicts.resolution import Resolution 16 | 17 | # Configure the logger for this module 18 | logger = logging.getLogger(__name__) 19 | logger.setLevel(logging.DEBUG) 20 | 21 | 22 | class AgentInterface(ABC): 23 | """ 24 | Abstract base class for agents in the Ovadare framework. 25 | Agents must implement this interface to interact with the framework. 26 | """ 27 | 28 | @property 29 | @abstractmethod 30 | def agent_id(self) -> str: 31 | """ 32 | Returns the unique identifier of the agent. 33 | 34 | Returns: 35 | str: The agent's unique ID. 36 | """ 37 | pass 38 | 39 | @property 40 | @abstractmethod 41 | def capabilities(self) -> Dict[str, Any]: 42 | """ 43 | Returns the capabilities of the agent. 44 | 45 | Returns: 46 | Dict[str, Any]: A dictionary representing the agent's capabilities. 47 | """ 48 | pass 49 | 50 | @abstractmethod 51 | def initialize(self) -> None: 52 | """ 53 | Initializes the agent. Called when the agent is registered with the framework. 54 | """ 55 | pass 56 | 57 | @abstractmethod 58 | def shutdown(self) -> None: 59 | """ 60 | Shuts down the agent. Called when the agent is unregistered from the framework. 61 | """ 62 | pass 63 | 64 | @abstractmethod 65 | def perform_action(self, action: Dict[str, Any]) -> Dict[str, Any]: 66 | """ 67 | Performs an action. 68 | 69 | Args: 70 | action (Dict[str, Any]): A dictionary representing the action to perform. 71 | 72 | Returns: 73 | Dict[str, Any]: The result of the action. 74 | """ 75 | pass 76 | 77 | @abstractmethod 78 | def report_status(self) -> Dict[str, Any]: 79 | """ 80 | Reports the current status of the agent. 81 | 82 | Returns: 83 | Dict[str, Any]: A dictionary representing the agent's status. 84 | """ 85 | pass 86 | 87 | @abstractmethod 88 | def handle_event(self, event_type: str, event_data: Dict[str, Any]) -> None: 89 | """ 90 | Handles an event dispatched by the framework. 91 | 92 | Args: 93 | event_type (str): The type of event. 94 | event_data (Dict[str, Any]): The data associated with the event. 95 | """ 96 | pass 97 | 98 | @abstractmethod 99 | def handle_resolution(self, resolution: Resolution) -> None: 100 | """ 101 | Handles a resolution provided by the ResolutionEngine in response to a conflict. 102 | 103 | Args: 104 | resolution (Resolution): The resolution to apply. 105 | """ 106 | pass 107 | -------------------------------------------------------------------------------- /ovadare/agents/agent_registry.py: -------------------------------------------------------------------------------- 1 | # ovadare/agents/agent_registry.py 2 | 3 | """ 4 | Agent Registry Module for the Ovadare Framework 5 | 6 | This module provides the AgentRegistry class, which is responsible for managing 7 | the registration, retrieval, and lifecycle of agents within the framework. 8 | """ 9 | 10 | from typing import Dict, List, Optional 11 | from threading import Lock 12 | import logging 13 | 14 | from ovadare.agents.agent_interface import AgentInterface 15 | 16 | # Configure the logger for this module 17 | logger = logging.getLogger(__name__) 18 | logger.setLevel(logging.DEBUG) 19 | 20 | class AgentRegistry: 21 | """ 22 | The AgentRegistry manages the agents registered within the Ovadare framework. 23 | It allows for registering new agents, retrieving existing agents, and unregistering agents. 24 | """ 25 | 26 | _instance = None 27 | _lock = Lock() 28 | 29 | def __new__(cls): 30 | """ 31 | Implements the Singleton pattern to ensure only one instance of AgentRegistry exists. 32 | """ 33 | if not cls._instance: 34 | with cls._lock: 35 | if not cls._instance: 36 | cls._instance = super(AgentRegistry, cls).__new__(cls) 37 | cls._instance._agents = {} 38 | cls._instance._agents_lock = Lock() 39 | logger.debug("Created a new instance of AgentRegistry.") 40 | return cls._instance 41 | 42 | def register_agent(self, agent: AgentInterface) -> None: 43 | """ 44 | Registers an agent with the registry. 45 | 46 | Args: 47 | agent (AgentInterface): The agent to register. 48 | 49 | Raises: 50 | ValueError: If an agent with the same ID is already registered. 51 | """ 52 | with self._agents_lock: 53 | if agent.agent_id in self._agents: 54 | error_message = f"Agent with ID '{agent.agent_id}' is already registered." 55 | logger.error(error_message) 56 | raise ValueError(error_message) 57 | else: 58 | self._agents[agent.agent_id] = agent 59 | logger.debug(f"Agent '{agent.agent_id}' registered successfully.") 60 | agent.initialize() 61 | 62 | def unregister_agent(self, agent_id: str) -> None: 63 | """ 64 | Unregisters an agent from the registry. 65 | 66 | Args: 67 | agent_id (str): The ID of the agent to unregister. 68 | 69 | Raises: 70 | ValueError: If the agent is not found in the registry. 71 | """ 72 | with self._agents_lock: 73 | if agent_id in self._agents: 74 | agent = self._agents.pop(agent_id) 75 | agent.shutdown() 76 | logger.debug(f"Agent '{agent_id}' unregistered successfully.") 77 | else: 78 | error_message = f"Agent with ID '{agent_id}' not found." 79 | logger.error(error_message) 80 | raise ValueError(error_message) 81 | 82 | def get_agent(self, agent_id: str) -> Optional[AgentInterface]: 83 | """ 84 | Retrieves an agent by its ID. 85 | 86 | Args: 87 | agent_id (str): The ID of the agent to retrieve. 88 | 89 | Returns: 90 | Optional[AgentInterface]: The agent if found, else None. 91 | """ 92 | with self._agents_lock: 93 | agent = self._agents.get(agent_id) 94 | if agent: 95 | logger.debug(f"Retrieved agent '{agent_id}'.") 96 | else: 97 | logger.warning(f"Agent '{agent_id}' not found.") 98 | return agent 99 | 100 | def get_all_agents(self) -> List[AgentInterface]: 101 | """ 102 | Retrieves a list of all registered agents. 103 | 104 | Returns: 105 | List[AgentInterface]: A list of all registered agents. 106 | """ 107 | with self._agents_lock: 108 | agents_list = list(self._agents.values()) 109 | logger.debug(f"Retrieved all agents. Total count: {len(agents_list)}.") 110 | return agents_list 111 | 112 | def find_agents_by_capability(self, capability: str) -> List[AgentInterface]: 113 | """ 114 | Finds agents that have a specific capability. 115 | 116 | Args: 117 | capability (str): The capability to search for. 118 | 119 | Returns: 120 | List[AgentInterface]: A list of agents that have the specified capability. 121 | """ 122 | with self._agents_lock: 123 | agents_with_capability = [agent for agent in self._agents.values() if capability in agent.capabilities] 124 | logger.debug(f"Found {len(agents_with_capability)} agent(s) with capability '{capability}'.") 125 | return agents_with_capability 126 | -------------------------------------------------------------------------------- /ovadare/agents/agent_sdk.py: -------------------------------------------------------------------------------- 1 | # ovadare/agents/agent_sdk.py 2 | 3 | """ 4 | Agent SDK Module for the Ovadare Framework 5 | 6 | This module provides the AgentSDK class, which allows agents to interact with 7 | the Ovadare Framework. It supports authentication, authorization, action submission, 8 | and feedback submission functionalities. 9 | """ 10 | 11 | import logging 12 | import requests 13 | from typing import Optional, Dict, Any 14 | 15 | from ovadare.security.authentication import AuthenticationManager 16 | from ovadare.security.authorization import AuthorizationManager 17 | 18 | # Configure the logger for this module 19 | logger = logging.getLogger(__name__) 20 | logger.setLevel(logging.DEBUG) 21 | 22 | 23 | class AgentSDK: 24 | """ 25 | Provides methods for agents to interact with the Ovadare Framework. 26 | """ 27 | 28 | def __init__( 29 | self, 30 | api_base_url: str, 31 | authentication_manager: AuthenticationManager, 32 | authorization_manager: AuthorizationManager 33 | ) -> None: 34 | """ 35 | Initializes the AgentSDK. 36 | 37 | Args: 38 | api_base_url (str): The base URL for the API endpoints. 39 | authentication_manager (AuthenticationManager): Manages authentication. 40 | authorization_manager (AuthorizationManager): Manages authorization. 41 | """ 42 | self.api_base_url = api_base_url 43 | self.authentication_manager = authentication_manager 44 | self.authorization_manager = authorization_manager 45 | self.token: Optional[str] = None 46 | self.agent_id: Optional[str] = None 47 | logger.debug("AgentSDK initialized.") 48 | 49 | def register(self, agent_id: str, password: str) -> bool: 50 | """ 51 | Registers the agent with the framework. 52 | 53 | Args: 54 | agent_id (str): The unique ID of the agent. 55 | password (str): The password for the agent. 56 | 57 | Returns: 58 | bool: True if registration is successful, False otherwise. 59 | """ 60 | url = f"{self.api_base_url}/register" 61 | data = {'user_id': agent_id, 'password': password} 62 | response = requests.post(url, json=data) 63 | if response.status_code == 201: 64 | self.agent_id = agent_id 65 | logger.info(f"Agent '{agent_id}' registered successfully.") 66 | return True 67 | else: 68 | logger.error(f"Registration failed: {response.text}") 69 | return False 70 | 71 | def login(self, agent_id: str, password: str) -> bool: 72 | """ 73 | Authenticates the agent and obtains an authentication token. 74 | 75 | Args: 76 | agent_id (str): The unique ID of the agent. 77 | password (str): The password for the agent. 78 | 79 | Returns: 80 | bool: True if login is successful, False otherwise. 81 | """ 82 | url = f"{self.api_base_url}/login" 83 | data = {'user_id': agent_id, 'password': password} 84 | response = requests.post(url, json=data) 85 | if response.status_code == 200: 86 | self.token = response.json().get('token') 87 | self.agent_id = agent_id 88 | logger.info(f"Agent '{agent_id}' logged in successfully.") 89 | return True 90 | else: 91 | logger.error(f"Login failed: {response.text}") 92 | return False 93 | 94 | def submit_action(self, action: Dict[str, Any]) -> bool: 95 | """ 96 | Submits an action to the framework. 97 | 98 | Args: 99 | action (Dict[str, Any]): The action data. 100 | 101 | Returns: 102 | bool: True if submission is successful, False otherwise. 103 | """ 104 | if not self.token or not self.agent_id: 105 | logger.error("Agent is not authenticated.") 106 | return False 107 | 108 | url = f"{self.api_base_url}/submit_action" 109 | headers = {'Authorization': self.token} 110 | data = {'agent_id': self.agent_id, 'action': action} 111 | response = requests.post(url, json=data, headers=headers) 112 | if response.status_code == 200: 113 | logger.info(f"Action submitted successfully: {action}") 114 | return True 115 | else: 116 | logger.error(f"Action submission failed: {response.text}") 117 | return False 118 | 119 | def submit_feedback( 120 | self, 121 | feedback_type: str, 122 | message: str 123 | ) -> bool: 124 | """ 125 | Submits feedback to the framework. 126 | 127 | Args: 128 | feedback_type (str): The type of feedback. 129 | message (str): The feedback message. 130 | 131 | Returns: 132 | bool: True if submission is successful, False otherwise. 133 | """ 134 | if not self.token or not self.agent_id: 135 | logger.error("Agent is not authenticated.") 136 | return False 137 | 138 | url = f"{self.api_base_url}/submit_feedback" 139 | headers = {'Authorization': self.token} 140 | data = { 141 | 'agent_id': self.agent_id, 142 | 'feedback_type': feedback_type, 143 | 'message': message 144 | } 145 | response = requests.post(url, json=data, headers=headers) 146 | if response.status_code == 200: 147 | logger.info(f"Feedback submitted successfully: {message}") 148 | return True 149 | else: 150 | logger.error(f"Feedback submission failed: {response.text}") 151 | return False 152 | 153 | def logout(self) -> None: 154 | """ 155 | Logs out the agent by clearing the authentication token. 156 | """ 157 | logger.info(f"Agent '{self.agent_id}' logged out.") 158 | self.token = None 159 | self.agent_id = None 160 | -------------------------------------------------------------------------------- /ovadare/communication/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/ovadare/communication/__init__.py -------------------------------------------------------------------------------- /ovadare/communication/api_endpoints.py: -------------------------------------------------------------------------------- 1 | # ovadare/communication/api_endpoints.py 2 | 3 | """ 4 | API Endpoints Module for the Ovadare Framework 5 | 6 | This module provides the APIEndpoints class, which defines RESTful API endpoints 7 | for agents and users to interact with the framework. It integrates authentication 8 | and authorization mechanisms to ensure secure access to the framework's resources. 9 | """ 10 | 11 | import logging 12 | from flask import Flask, request, jsonify 13 | from threading import Thread 14 | from typing import Optional 15 | 16 | from ovadare.agents.agent_registry import AgentRegistry 17 | from ovadare.core.event_dispatcher import EventDispatcher 18 | from ovadare.security.authentication import AuthenticationManager 19 | from ovadare.security.authorization import AuthorizationManager 20 | 21 | # Configure the logger for this module 22 | logger = logging.getLogger(__name__) 23 | logger.setLevel(logging.DEBUG) 24 | 25 | 26 | class APIEndpoints: 27 | """ 28 | Defines the API endpoints for the Ovadare Framework. 29 | """ 30 | 31 | def __init__( 32 | self, 33 | agent_registry: AgentRegistry, 34 | event_dispatcher: EventDispatcher, 35 | authentication_manager: AuthenticationManager, 36 | authorization_manager: AuthorizationManager 37 | ) -> None: 38 | """ 39 | Initializes the APIEndpoints. 40 | 41 | Args: 42 | agent_registry (AgentRegistry): The registry for managing agents. 43 | event_dispatcher (EventDispatcher): The event dispatcher for sending events. 44 | authentication_manager (AuthenticationManager): The authentication manager for securing endpoints. 45 | authorization_manager (AuthorizationManager): The authorization manager for access control. 46 | """ 47 | self.agent_registry = agent_registry 48 | self.event_dispatcher = event_dispatcher 49 | self.authentication_manager = authentication_manager 50 | self.authorization_manager = authorization_manager 51 | self.app = Flask(__name__) 52 | self._register_routes() 53 | self._server_thread: Optional[Thread] = None 54 | logger.debug("APIEndpoints initialized.") 55 | 56 | def _register_routes(self) -> None: 57 | """ 58 | Registers the API routes. 59 | """ 60 | 61 | @self.app.route('/register', methods=['POST']) 62 | def register(): 63 | data = request.get_json() 64 | user_id = data.get('user_id') 65 | password = data.get('password') 66 | if not user_id or not password: 67 | return jsonify({'error': 'user_id and password are required'}), 400 68 | try: 69 | self.authentication_manager.register_user(user_id, password) 70 | self.authorization_manager.assign_role(user_id, 'agent') 71 | return jsonify({'message': 'User registered successfully'}), 201 72 | except ValueError as e: 73 | return jsonify({'error': str(e)}), 400 74 | 75 | @self.app.route('/login', methods=['POST']) 76 | def login(): 77 | data = request.get_json() 78 | user_id = data.get('user_id') 79 | password = data.get('password') 80 | if not user_id or not password: 81 | return jsonify({'error': 'user_id and password are required'}), 400 82 | token = self.authentication_manager.authenticate(user_id, password) 83 | if token: 84 | return jsonify({'token': token}), 200 85 | else: 86 | return jsonify({'error': 'Authentication failed'}), 401 87 | 88 | @self.app.route('/submit_action', methods=['POST']) 89 | def submit_action(): 90 | token = request.headers.get('Authorization') 91 | user_id = self._get_user_id_from_token(token) 92 | if not user_id: 93 | return jsonify({'error': 'Unauthorized'}), 401 94 | 95 | if not self.authorization_manager.is_authorized(user_id, 'submit_action'): 96 | return jsonify({'error': 'Forbidden'}), 403 97 | 98 | data = request.get_json() 99 | agent_id = data.get('agent_id') 100 | action = data.get('action') 101 | if not agent_id or not action: 102 | return jsonify({'error': 'agent_id and action are required'}), 400 103 | 104 | # Dispatch the agent action event 105 | event_data = {'agent_id': agent_id, 'action': action} 106 | self.event_dispatcher.dispatch('agent_action', event_data) 107 | logger.info(f"Agent '{agent_id}' submitted action: {action}") 108 | return jsonify({'message': 'Action submitted successfully'}), 200 109 | 110 | @self.app.route('/submit_feedback', methods=['POST']) 111 | def submit_feedback(): 112 | token = request.headers.get('Authorization') 113 | user_id = self._get_user_id_from_token(token) 114 | if not user_id: 115 | return jsonify({'error': 'Unauthorized'}), 401 116 | 117 | if not self.authorization_manager.is_authorized(user_id, 'submit_feedback'): 118 | return jsonify({'error': 'Forbidden'}), 403 119 | 120 | data = request.get_json() 121 | agent_id = data.get('agent_id') 122 | feedback_type = data.get('feedback_type') 123 | message = data.get('message') 124 | if not agent_id or not feedback_type or not message: 125 | return jsonify({'error': 'agent_id, feedback_type, and message are required'}), 400 126 | 127 | # Dispatch the feedback event 128 | feedback_data = { 129 | 'agent_id': agent_id, 130 | 'feedback_type': feedback_type, 131 | 'message': message 132 | } 133 | self.event_dispatcher.dispatch('feedback_submitted', feedback_data) 134 | logger.info(f"Agent '{agent_id}' submitted feedback: {feedback_data}") 135 | return jsonify({'message': 'Feedback submitted successfully'}), 200 136 | 137 | logger.debug("API routes registered.") 138 | 139 | def _get_user_id_from_token(self, token: Optional[str]) -> Optional[str]: 140 | """ 141 | Retrieves the user ID from the authentication token. 142 | 143 | Args: 144 | token (Optional[str]): The authentication token from the request header. 145 | 146 | Returns: 147 | Optional[str]: The user ID if the token is valid, else None. 148 | """ 149 | if token and self.authentication_manager.validate_token(token): 150 | user_id = self.authentication_manager.get_user_id_from_token(token) 151 | return user_id 152 | else: 153 | logger.warning("Unauthorized access attempt.") 154 | return None 155 | 156 | def start(self, host: str = '0.0.0.0', port: int = 5000) -> None: 157 | """ 158 | Starts the API server in a separate thread. 159 | 160 | Args: 161 | host (str): The host IP address. 162 | port (int): The port number. 163 | """ 164 | 165 | def run_app(): 166 | logger.info(f"API server running on {host}:{port}") 167 | self.app.run(host=host, port=port, use_reloader=False) 168 | 169 | self._server_thread = Thread(target=run_app, daemon=True) 170 | self._server_thread.start() 171 | logger.debug("API server started in a separate thread.") 172 | 173 | def stop(self) -> None: 174 | """ 175 | Stops the API server. 176 | """ 177 | # Flask doesn't provide a built-in way to stop the server programmatically. 178 | # This is a placeholder implementation. 179 | logger.info("API server stop requested.") 180 | # Implement server shutdown logic if needed. 181 | pass 182 | 183 | 184 | @self.app.route('/get_conflicts', methods=['GET']) 185 | def get_conflicts(): 186 | token = request.headers.get('Authorization') 187 | user_id = self._get_user_id_from_token(token) 188 | if not user_id: 189 | return jsonify({'error': 'Unauthorized'}), 401 190 | 191 | if not self.authorization_manager.is_authorized(user_id, 'view_conflicts'): 192 | return jsonify({'error': 'Forbidden'}), 403 193 | 194 | conflicts = self.conflict_detector.load_conflicts() 195 | conflicts_data = [conflict.serialize() for conflict in conflicts] 196 | return jsonify({'conflicts': conflicts_data}), 200 197 | 198 | @self.app.route('/get_resolutions', methods=['GET']) 199 | def get_resolutions(): 200 | token = request.headers.get('Authorization') 201 | user_id = self._get_user_id_from_token(token) 202 | if not user_id: 203 | return jsonify({'error': 'Unauthorized'}), 401 204 | 205 | if not self.authorization_manager.is_authorized(user_id, 'view_resolutions'): 206 | return jsonify({'error': 'Forbidden'}), 403 207 | 208 | resolutions = self.resolution_engine.load_resolutions() 209 | resolutions_data = [resolution.serialize() for resolution in resolutions] 210 | return jsonify({'resolutions': resolutions_data}), 200 211 | -------------------------------------------------------------------------------- /ovadare/communication/security.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/ovadare/communication/security.py -------------------------------------------------------------------------------- /ovadare/conflicts/__init__.py: -------------------------------------------------------------------------------- 1 | # ovadare/conflicts/__init__.py 2 | 3 | from .conflict import Conflict 4 | from .conflict_detector import ConflictDetector 5 | from .conflict_resolver import ConflictResolver 6 | from .conflict_classifier import ConflictClassifier # If implemented 7 | from .resolution import Resolution 8 | from .resolution_engine import ResolutionEngine 9 | 10 | __all__ = [ 11 | 'Conflict', 12 | 'ConflictDetector', 13 | 'ConflictResolver', 14 | 'ConflictClassifier', # If implemented 15 | 'Resolution', 16 | 'ResolutionEngine' 17 | ] 18 | -------------------------------------------------------------------------------- /ovadare/conflicts/conflict.py: -------------------------------------------------------------------------------- 1 | # ovadare/conflicts/conflict.py 2 | 3 | """ 4 | Conflict Module for the Ovadare Framework 5 | 6 | This module provides the Conflict class, which represents a detected conflict 7 | resulting from a policy violation. It includes details about the violation, 8 | the action that caused it, and the agent involved. 9 | """ 10 | 11 | import logging 12 | from typing import Dict, Any 13 | from uuid import uuid4 14 | 15 | # Configure the logger for this module 16 | logger = logging.getLogger(__name__) 17 | logger.setLevel(logging.DEBUG) 18 | 19 | 20 | class Conflict: 21 | """ 22 | Represents a detected conflict resulting from a policy violation. 23 | """ 24 | 25 | def __init__( 26 | self, 27 | conflict_id: str = None, 28 | related_agent_id: str = "", 29 | action: Dict[str, Any] = None, 30 | violation_details: str = "", 31 | policy_id: str = "", 32 | timestamp: float = None 33 | ): 34 | """ 35 | Initializes a Conflict instance. 36 | 37 | Args: 38 | conflict_id (str, optional): The unique identifier of the conflict. 39 | If None, a UUID is generated. 40 | related_agent_id (str): The ID of the agent involved in the conflict. 41 | action (Dict[str, Any]): The action that caused the conflict. 42 | violation_details (str): Details about the policy violation. 43 | policy_id (str): The ID of the policy that was violated. 44 | timestamp (float, optional): The time the conflict was detected. 45 | """ 46 | self.conflict_id: str = conflict_id or str(uuid4()) 47 | self.related_agent_id: str = related_agent_id 48 | self.action: Dict[str, Any] = action or {} 49 | self.violation_details: str = violation_details 50 | self.policy_id: str = policy_id 51 | self.timestamp: float = timestamp or self._current_timestamp() 52 | 53 | logger.debug(f"Conflict '{self.conflict_id}' initialized for agent '{self.related_agent_id}'.") 54 | 55 | def to_dict(self) -> Dict[str, Any]: 56 | """ 57 | Serializes the Conflict instance to a dictionary. 58 | 59 | Returns: 60 | Dict[str, Any]: A dictionary representation of the conflict. 61 | """ 62 | conflict_dict = { 63 | 'conflict_id': self.conflict_id, 64 | 'related_agent_id': self.related_agent_id, 65 | 'action': self.action, 66 | 'violation_details': self.violation_details, 67 | 'policy_id': self.policy_id, 68 | 'timestamp': self.timestamp 69 | } 70 | logger.debug(f"Conflict '{self.conflict_id}' serialized to dict.") 71 | return conflict_dict 72 | 73 | @staticmethod 74 | def _current_timestamp() -> float: 75 | """ 76 | Gets the current timestamp. 77 | 78 | Returns: 79 | float: The current time in seconds since the epoch. 80 | """ 81 | import time 82 | return time.time() 83 | -------------------------------------------------------------------------------- /ovadare/conflicts/conflict_classifier.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/ovadare/conflicts/conflict_classifier.py -------------------------------------------------------------------------------- /ovadare/conflicts/conflict_detector.py: -------------------------------------------------------------------------------- 1 | # ovadare/conflicts/conflict_detector.py 2 | 3 | """ 4 | Conflict Detector Module for the Ovadare Framework 5 | 6 | This module provides the ConflictDetector class, which is responsible for detecting 7 | policy violations by evaluating agent actions against the loaded policies. 8 | It stores detected conflicts and provides methods for retrieving and resolving them. 9 | """ 10 | 11 | import logging 12 | import json 13 | import os 14 | from typing import List, Dict, Any, Optional 15 | from threading import Lock 16 | 17 | from ovadare.policies.policy_manager import PolicyManager 18 | from ovadare.conflicts.conflict import Conflict 19 | from ovadare.utils.configuration import Configuration 20 | 21 | # Configure the logger for this module 22 | logger = logging.getLogger(__name__) 23 | logger.setLevel(logging.DEBUG) 24 | 25 | 26 | class ConflictDetector: 27 | """ 28 | Evaluates agent actions against policies to detect conflicts. 29 | """ 30 | 31 | def __init__(self, policy_manager: Optional[PolicyManager] = None) -> None: 32 | """ 33 | Initializes the ConflictDetector. 34 | 35 | Args: 36 | policy_manager (Optional[PolicyManager]): An instance of PolicyManager. 37 | If None, a new PolicyManager is created. 38 | """ 39 | self.policy_manager = policy_manager or PolicyManager() 40 | self._conflicts: Dict[str, Conflict] = {} 41 | self._lock = Lock() 42 | self._storage_file = Configuration.get('conflict_storage_file', 'conflicts_data.json') 43 | self._load_conflicts() 44 | logger.debug("ConflictDetector initialized.") 45 | 46 | def detect(self, agent_id: str, action: Dict[str, Any]) -> List[Conflict]: 47 | """ 48 | Detects conflicts by evaluating an agent's action against policies. 49 | 50 | Args: 51 | agent_id (str): The ID of the agent performing the action. 52 | action (Dict[str, Any]): The action to evaluate. 53 | 54 | Returns: 55 | List[Conflict]: A list of detected conflicts. 56 | """ 57 | conflicts = [] 58 | logger.debug(f"Detecting conflicts for agent '{agent_id}' and action: {action}") 59 | 60 | try: 61 | evaluation_results = self.policy_manager.evaluate_policies(action) 62 | for result in evaluation_results: 63 | if not result.is_compliant: 64 | conflict = Conflict( 65 | related_agent_id=agent_id, 66 | action=action, 67 | violation_details=result.message, 68 | policy_id=result.policy_id 69 | ) 70 | with self._lock: 71 | self._conflicts[conflict.conflict_id] = conflict 72 | self._save_conflicts() 73 | conflicts.append(conflict) 74 | logger.info(f"Conflict detected: {conflict}") 75 | except Exception as e: 76 | logger.error(f"Error during conflict detection for agent '{agent_id}': {e}", exc_info=True) 77 | 78 | return conflicts 79 | 80 | def get_conflict_by_id(self, conflict_id: str) -> Optional[Conflict]: 81 | """ 82 | Retrieves a conflict by its ID. 83 | 84 | Args: 85 | conflict_id (str): The unique identifier of the conflict. 86 | 87 | Returns: 88 | Optional[Conflict]: The Conflict instance if found, else None. 89 | """ 90 | with self._lock: 91 | conflict = self._conflicts.get(conflict_id) 92 | if conflict: 93 | logger.debug(f"Conflict '{conflict_id}' retrieved.") 94 | else: 95 | logger.warning(f"Conflict '{conflict_id}' not found.") 96 | return conflict 97 | 98 | def get_all_conflicts(self) -> List[Conflict]: 99 | """ 100 | Retrieves all detected conflicts. 101 | 102 | Returns: 103 | List[Conflict]: A list of all detected conflicts. 104 | """ 105 | with self._lock: 106 | conflicts = list(self._conflicts.values()) 107 | logger.debug(f"Retrieved {len(conflicts)} conflict(s).") 108 | return conflicts 109 | 110 | def resolve_conflict(self, conflict_id: str) -> None: 111 | """ 112 | Resolves a conflict by removing it from the stored conflicts. 113 | 114 | Args: 115 | conflict_id (str): The unique identifier of the conflict to resolve. 116 | """ 117 | with self._lock: 118 | if conflict_id in self._conflicts: 119 | del self._conflicts[conflict_id] 120 | self._save_conflicts() 121 | logger.info(f"Conflict '{conflict_id}' resolved and removed.") 122 | else: 123 | logger.warning(f"Conflict '{conflict_id}' not found. Cannot resolve.") 124 | 125 | def clear_conflicts(self) -> None: 126 | """ 127 | Clears all stored conflicts. 128 | """ 129 | with self._lock: 130 | self._conflicts.clear() 131 | self._save_conflicts() 132 | logger.info("All conflicts cleared.") 133 | 134 | def _load_conflicts(self) -> None: 135 | """ 136 | Loads conflicts data from the storage file. 137 | """ 138 | if os.path.exists(self._storage_file): 139 | try: 140 | with open(self._storage_file, 'r', encoding='utf-8') as f: 141 | conflicts_data = json.load(f) 142 | self._conflicts = { 143 | cid: Conflict.from_dict(cdata) for cid, cdata in conflicts_data.items() 144 | } 145 | logger.debug(f"Loaded conflicts data from '{self._storage_file}'.") 146 | except Exception as e: 147 | logger.error(f"Failed to load conflicts data: {e}", exc_info=True) 148 | self._conflicts = {} 149 | else: 150 | logger.debug(f"Conflict storage file '{self._storage_file}' does not exist. Starting fresh.") 151 | 152 | def _save_conflicts(self) -> None: 153 | """ 154 | Saves conflicts data to the storage file. 155 | """ 156 | try: 157 | conflicts_data = {cid: conflict.to_dict() for cid, conflict in self._conflicts.items()} 158 | with open(self._storage_file, 'w', encoding='utf-8') as f: 159 | json.dump(conflicts_data, f, ensure_ascii=False, indent=4) 160 | logger.debug(f"Saved conflicts data to '{self._storage_file}'.") 161 | except Exception as e: 162 | logger.error(f"Failed to save conflicts data: {e}", exc_info=True) 163 | 164 | def load_conflicts(self): 165 | """ 166 | Loads conflicts from persistent storage. 167 | """ 168 | if os.path.exists(self.conflict_storage_file): 169 | with open(self.conflict_storage_file, 'r', encoding='utf-8') as f: 170 | conflicts_data = json.load(f) 171 | self.conflicts = [Conflict.deserialize(data) for data in conflicts_data] 172 | return self.conflicts 173 | -------------------------------------------------------------------------------- /ovadare/conflicts/conflict_resolver.py: -------------------------------------------------------------------------------- 1 | # ovadare/conflicts/conflict_resolver.py 2 | 3 | """ 4 | Conflict Resolver Module for the Ovadare Framework 5 | 6 | This module provides the ConflictResolver class, which is responsible for resolving 7 | detected conflicts by generating and applying appropriate resolutions based on 8 | predefined strategies. 9 | """ 10 | 11 | import logging 12 | from typing import List 13 | from ovadare.conflicts.conflict import Conflict 14 | from ovadare.conflicts.resolution import Resolution 15 | from ovadare.conflicts.resolution_engine import ResolutionEngine 16 | from ovadare.conflicts.conflict_detector import ConflictDetector 17 | 18 | # Configure the logger for this module 19 | logger = logging.getLogger(__name__) 20 | logger.setLevel(logging.DEBUG) 21 | 22 | 23 | class ConflictResolver: 24 | """ 25 | Resolves detected conflicts by generating and applying resolutions. 26 | """ 27 | 28 | def __init__(self, conflict_detector: ConflictDetector, resolution_engine: ResolutionEngine) -> None: 29 | """ 30 | Initializes the ConflictResolver. 31 | 32 | Args: 33 | conflict_detector (ConflictDetector): An instance of ConflictDetector. 34 | resolution_engine (ResolutionEngine): An instance of ResolutionEngine. 35 | """ 36 | self.conflict_detector = conflict_detector 37 | self.resolution_engine = resolution_engine 38 | logger.debug("ConflictResolver initialized with ConflictDetector and ResolutionEngine.") 39 | 40 | def resolve_conflicts(self, conflicts: List[Conflict]) -> List[Resolution]: 41 | """ 42 | Resolves a list of conflicts by generating and applying resolutions. 43 | 44 | Args: 45 | conflicts (List[Conflict]): A list of conflicts to resolve. 46 | 47 | Returns: 48 | List[Resolution]: A list of generated resolutions. 49 | """ 50 | logger.debug(f"Resolving {len(conflicts)} conflict(s).") 51 | resolutions = self.resolution_engine.generate_resolutions(conflicts) 52 | self.resolution_engine.apply_resolutions(resolutions) 53 | logger.info(f"Resolved {len(resolutions)} conflict(s).") 54 | return resolutions 55 | -------------------------------------------------------------------------------- /ovadare/conflicts/resolution.py: -------------------------------------------------------------------------------- 1 | # ovadare/conflicts/resolution.py 2 | 3 | """ 4 | Resolution Module for the Ovadare Framework 5 | 6 | This module provides the Resolution class, which represents a proposed resolution 7 | for a detected conflict. It includes details about the corrective action to be taken, 8 | the conflict it addresses, and an explanation. 9 | """ 10 | 11 | import logging 12 | from typing import Dict, Any, Optional 13 | from uuid import uuid4 14 | import time 15 | 16 | # Configure the logger for this module 17 | logger = logging.getLogger(__name__) 18 | logger.setLevel(logging.DEBUG) 19 | 20 | 21 | class Resolution: 22 | """ 23 | Represents a proposed resolution for a detected conflict. 24 | """ 25 | 26 | def __init__( 27 | self, 28 | resolution_id: Optional[str] = None, 29 | conflict_id: str = "", 30 | corrective_action: Optional[Dict[str, Any]] = None, 31 | explanation: str = "", 32 | timestamp: Optional[float] = None 33 | ): 34 | """ 35 | Initializes a Resolution instance. 36 | 37 | Args: 38 | resolution_id (Optional[str]): The unique identifier of the resolution. 39 | If None, a UUID is generated. 40 | conflict_id (str): The ID of the conflict this resolution addresses. 41 | corrective_action (Optional[Dict[str, Any]]): The action proposed to resolve the conflict. 42 | explanation (str): A description or explanation of the resolution. 43 | timestamp (Optional[float]): The time the resolution was generated. 44 | """ 45 | self.resolution_id: str = resolution_id or str(uuid4()) 46 | self.conflict_id: str = conflict_id 47 | self.corrective_action: Dict[str, Any] = corrective_action or {} 48 | self.explanation: str = explanation 49 | self.timestamp: float = timestamp or self._current_timestamp() 50 | 51 | logger.debug( 52 | f"Resolution '{self.resolution_id}' initialized for conflict '{self.conflict_id}'." 53 | ) 54 | 55 | def to_dict(self) -> Dict[str, Any]: 56 | """ 57 | Serializes the Resolution instance to a dictionary. 58 | 59 | Returns: 60 | Dict[str, Any]: A dictionary representation of the resolution. 61 | """ 62 | resolution_dict = { 63 | 'resolution_id': self.resolution_id, 64 | 'conflict_id': self.conflict_id, 65 | 'corrective_action': self.corrective_action, 66 | 'explanation': self.explanation, 67 | 'timestamp': self.timestamp 68 | } 69 | logger.debug(f"Resolution '{self.resolution_id}' serialized to dict.") 70 | return resolution_dict 71 | 72 | @classmethod 73 | def from_dict(cls, data: Dict[str, Any]) -> 'Resolution': 74 | """ 75 | Creates a Resolution instance from a dictionary. 76 | 77 | Args: 78 | data (Dict[str, Any]): A dictionary containing resolution data. 79 | 80 | Returns: 81 | Resolution: A new Resolution instance. 82 | """ 83 | resolution = cls( 84 | resolution_id=data.get('resolution_id'), 85 | conflict_id=data.get('conflict_id', ""), 86 | corrective_action=data.get('corrective_action'), 87 | explanation=data.get('explanation', ""), 88 | timestamp=data.get('timestamp') 89 | ) 90 | logger.debug(f"Resolution '{resolution.resolution_id}' created from dict.") 91 | return resolution 92 | 93 | def __repr__(self) -> str: 94 | """ 95 | Returns a string representation of the Resolution instance. 96 | 97 | Returns: 98 | str: A string representation of the resolution. 99 | """ 100 | return ( 101 | f"Resolution(resolution_id='{self.resolution_id}', conflict_id='{self.conflict_id}', " 102 | f"corrective_action={self.corrective_action}, explanation='{self.explanation}', " 103 | f"timestamp={self.timestamp})" 104 | ) 105 | 106 | @staticmethod 107 | def _current_timestamp() -> float: 108 | """ 109 | Gets the current timestamp. 110 | 111 | Returns: 112 | float: The current time in seconds since the epoch. 113 | """ 114 | return time.time() 115 | -------------------------------------------------------------------------------- /ovadare/conflicts/resolution_engine.py: -------------------------------------------------------------------------------- 1 | # ovadare/conflicts/resolution_engine.py 2 | 3 | """ 4 | Resolution Engine Module for the Ovadare Framework 5 | 6 | This module provides the ResolutionEngine class, which is responsible for 7 | generating resolutions for detected conflicts and communicating them to 8 | the appropriate agents. 9 | """ 10 | 11 | import logging 12 | from typing import List, Optional 13 | from threading import Lock 14 | 15 | from ovadare.conflicts.conflict import Conflict 16 | from ovadare.conflicts.resolution import Resolution 17 | from ovadare.agents.agent_interface import AgentInterface 18 | from ovadare.agents.agent_registry import AgentRegistry 19 | 20 | # Configure the logger for this module 21 | logger = logging.getLogger(__name__) 22 | logger.setLevel(logging.DEBUG) 23 | 24 | 25 | class ResolutionEngine: 26 | """ 27 | The ResolutionEngine generates resolutions for detected conflicts and 28 | communicates them to the appropriate agents. 29 | """ 30 | 31 | def __init__(self, agent_registry: Optional[AgentRegistry] = None): 32 | """ 33 | Initializes the ResolutionEngine. 34 | 35 | Args: 36 | agent_registry (Optional[AgentRegistry]): An instance of AgentRegistry. 37 | If None, a new AgentRegistry is created. 38 | """ 39 | self.agent_registry = agent_registry or AgentRegistry() 40 | self._lock = Lock() 41 | logger.debug("ResolutionEngine initialized.") 42 | 43 | def generate_resolutions(self, conflicts: List[Conflict]) -> List[Resolution]: 44 | """ 45 | Generates resolutions for a list of conflicts. 46 | 47 | Args: 48 | conflicts (List[Conflict]): A list of conflicts to resolve. 49 | 50 | Returns: 51 | List[Resolution]: A list of generated resolutions. 52 | """ 53 | resolutions = [] 54 | logger.debug(f"Generating resolutions for {len(conflicts)} conflict(s).") 55 | 56 | with self._lock: 57 | for conflict in conflicts: 58 | try: 59 | resolution = self._create_resolution(conflict) 60 | resolutions.append(resolution) 61 | logger.info( 62 | f"Resolution '{resolution.resolution_id}' generated for conflict '{conflict.conflict_id}'." 63 | ) 64 | except Exception as e: 65 | logger.error( 66 | f"Error generating resolution for conflict '{conflict.conflict_id}': {e}", 67 | exc_info=True 68 | ) 69 | 70 | return resolutions 71 | 72 | def _create_resolution(self, conflict: Conflict) -> Resolution: 73 | """ 74 | Creates a resolution for a single conflict. 75 | 76 | Args: 77 | conflict (Conflict): The conflict to resolve. 78 | 79 | Returns: 80 | Resolution: The generated resolution. 81 | """ 82 | explanation = f"Resolution for conflict '{conflict.conflict_id}': {conflict.violation_details}" 83 | 84 | # Placeholder for corrective action 85 | corrective_action = { 86 | 'action': 'notify', 87 | 'details': { 88 | 'message': f"Please adjust your action to comply with policy '{conflict.policy_id}'." 89 | } 90 | } 91 | 92 | resolution = Resolution( 93 | conflict_id=conflict.conflict_id, 94 | corrective_action=corrective_action, 95 | explanation=explanation 96 | ) 97 | 98 | logger.debug(f"Resolution created: {resolution}") 99 | return resolution 100 | 101 | def apply_resolutions(self, resolutions: List[Resolution]) -> None: 102 | """ 103 | Applies a list of resolutions by communicating them to the appropriate agents. 104 | 105 | Args: 106 | resolutions (List[Resolution]): The resolutions to apply. 107 | """ 108 | logger.debug(f"Applying {len(resolutions)} resolution(s).") 109 | 110 | with self._lock: 111 | for resolution in resolutions: 112 | try: 113 | conflict = self._get_conflict_by_id(resolution.conflict_id) 114 | if not conflict: 115 | logger.warning(f"Conflict '{resolution.conflict_id}' not found. Cannot apply resolution.") 116 | continue 117 | 118 | agent = self.agent_registry.get_agent(conflict.related_agent_id) 119 | if not agent: 120 | logger.warning( 121 | f"Agent '{conflict.related_agent_id}' not found. Cannot apply resolution '{resolution.resolution_id}'." 122 | ) 123 | continue 124 | 125 | self._send_resolution_to_agent(agent, resolution) 126 | logger.info( 127 | f"Resolution '{resolution.resolution_id}' applied to agent '{agent.agent_id}'." 128 | ) 129 | except Exception as e: 130 | logger.error( 131 | f"Error applying resolution '{resolution.resolution_id}': {e}", 132 | exc_info=True 133 | ) 134 | 135 | def _get_conflict_by_id(self, conflict_id: str) -> Optional[Conflict]: 136 | """ 137 | Retrieves a conflict by its ID. 138 | 139 | Args: 140 | conflict_id (str): The unique identifier of the conflict. 141 | 142 | Returns: 143 | Optional[Conflict]: The Conflict instance if found, else None. 144 | """ 145 | # This method assumes that the ResolutionEngine has access to the conflicts. 146 | # In practice, this may involve querying a ConflictStore or similar component. 147 | # For this example, we'll assume conflicts are stored in a dictionary. 148 | logger.debug(f"Retrieving conflict '{conflict_id}'.") 149 | # Placeholder implementation 150 | return None # Needs implementation based on actual conflict storage 151 | 152 | def _send_resolution_to_agent(self, agent: AgentInterface, resolution: Resolution) -> None: 153 | """ 154 | Sends a resolution to an agent. 155 | 156 | Args: 157 | agent (AgentInterface): The agent to send the resolution to. 158 | resolution (Resolution): The resolution to send. 159 | """ 160 | try: 161 | agent.handle_resolution(resolution) 162 | logger.debug( 163 | f"Resolution '{resolution.resolution_id}' sent to agent '{agent.agent_id}'." 164 | ) 165 | except Exception as e: 166 | logger.error( 167 | f"Error sending resolution to agent '{agent.agent_id}': {e}", 168 | exc_info=True 169 | ) 170 | 171 | 172 | def load_resolutions(self): 173 | """ 174 | Loads resolutions from persistent storage. 175 | """ 176 | if os.path.exists(self.resolution_storage_file): 177 | with open(self.resolution_storage_file, 'r', encoding='utf-8') as f: 178 | resolutions_data = json.load(f) 179 | self.resolutions = [Resolution.deserialize(data) for data in resolutions_data] 180 | return self.resolutions 181 | -------------------------------------------------------------------------------- /ovadare/core/base_classes.py: -------------------------------------------------------------------------------- 1 | # ovadare/core/base_classes.py 2 | 3 | """ 4 | Base Classes and Interfaces for the Ovadare Framework 5 | 6 | This module defines the abstract base classes and interfaces that form the foundation 7 | of the Ovadare conflict detection and resolution framework. These classes establish 8 | the contracts that concrete implementations must adhere to, ensuring consistency 9 | and interoperability across the framework. 10 | """ 11 | 12 | from abc import ABC, abstractmethod 13 | from enum import Enum 14 | from typing import List, Dict, Any, Optional 15 | from datetime import datetime 16 | 17 | 18 | class AgentInterface(ABC): 19 | """ 20 | Abstract base class for agents interacting with the Ovadare framework. 21 | Agents should implement this interface to integrate with the framework. 22 | """ 23 | 24 | @property 25 | @abstractmethod 26 | def agent_id(self) -> str: 27 | """Unique identifier for the agent.""" 28 | pass 29 | 30 | @property 31 | @abstractmethod 32 | def capabilities(self) -> List[str]: 33 | """List of actions or tasks the agent can perform.""" 34 | pass 35 | 36 | @abstractmethod 37 | def report_action(self, action: Dict[str, Any]) -> None: 38 | """ 39 | Reports an action that the agent intends to perform to the framework. 40 | 41 | Args: 42 | action (Dict[str, Any]): The action to be reported. 43 | """ 44 | pass 45 | 46 | @abstractmethod 47 | def receive_resolution(self, resolution: 'Resolution') -> None: 48 | """ 49 | Receives a resolution from the framework. 50 | 51 | Args: 52 | resolution (Resolution): The resolution to be applied. 53 | """ 54 | pass 55 | 56 | 57 | class EvaluationResult: 58 | """ 59 | Class representing the result of evaluating an action against a policy. 60 | """ 61 | 62 | def __init__(self, compliant: bool, message: Optional[str] = None): 63 | """ 64 | Initializes an EvaluationResult. 65 | 66 | Args: 67 | compliant (bool): True if the action complies with the policy, False otherwise. 68 | message (Optional[str]): Optional message providing details about the evaluation. 69 | """ 70 | self.compliant = compliant 71 | self.message = message 72 | 73 | def __bool__(self): 74 | return self.compliant 75 | 76 | 77 | class Policy(ABC): 78 | """ 79 | Abstract base class for policies used in the Ovadare framework. 80 | Policies define the constraints and requirements that agents must follow. 81 | """ 82 | 83 | @property 84 | @abstractmethod 85 | def policy_id(self) -> str: 86 | """Unique identifier for the policy.""" 87 | pass 88 | 89 | @property 90 | @abstractmethod 91 | def name(self) -> str: 92 | """Name of the policy.""" 93 | pass 94 | 95 | @property 96 | @abstractmethod 97 | def description(self) -> str: 98 | """Description of the policy.""" 99 | pass 100 | 101 | @property 102 | @abstractmethod 103 | def priority(self) -> int: 104 | """Priority level of the policy (higher value indicates higher priority).""" 105 | pass 106 | 107 | @abstractmethod 108 | def evaluate(self, action: Dict[str, Any]) -> 'EvaluationResult': 109 | """ 110 | Evaluates an action against the policy. 111 | 112 | Args: 113 | action (Dict[str, Any]): The action to evaluate. 114 | 115 | Returns: 116 | EvaluationResult: The result of the evaluation. 117 | """ 118 | pass 119 | 120 | 121 | class ConflictSeverity(Enum): 122 | """ 123 | Enumeration for conflict severity levels. 124 | """ 125 | LOW = 'Low' 126 | MEDIUM = 'Medium' 127 | HIGH = 'High' 128 | CRITICAL = 'Critical' 129 | 130 | 131 | class Conflict(ABC): 132 | """ 133 | Abstract base class for conflicts detected by the Ovadare framework. 134 | Conflicts represent issues where agent actions clash with policies or constraints. 135 | """ 136 | 137 | @property 138 | @abstractmethod 139 | def conflict_id(self) -> str: 140 | """Unique identifier for the conflict.""" 141 | pass 142 | 143 | @property 144 | @abstractmethod 145 | def conflict_type(self) -> str: 146 | """Type or category of the conflict.""" 147 | pass 148 | 149 | @property 150 | @abstractmethod 151 | def related_agent_id(self) -> str: 152 | """Identifier of the agent involved in the conflict.""" 153 | pass 154 | 155 | @property 156 | @abstractmethod 157 | def severity(self) -> ConflictSeverity: 158 | """Severity level of the conflict.""" 159 | pass 160 | 161 | @property 162 | @abstractmethod 163 | def timestamp(self) -> datetime: 164 | """Timestamp of when the conflict was detected.""" 165 | pass 166 | 167 | @property 168 | @abstractmethod 169 | def details(self) -> Dict[str, Any]: 170 | """ 171 | Additional details about the conflict. 172 | 173 | Returns: 174 | Dict[str, Any]: A dictionary containing conflict details. 175 | """ 176 | pass 177 | 178 | 179 | class Resolution(ABC): 180 | """ 181 | Abstract base class for resolutions generated by the Ovadare framework. 182 | Resolutions define the actions to be taken to resolve conflicts. 183 | """ 184 | 185 | @property 186 | @abstractmethod 187 | def resolution_id(self) -> str: 188 | """Unique identifier for the resolution.""" 189 | pass 190 | 191 | @property 192 | @abstractmethod 193 | def conflict_id(self) -> str: 194 | """Identifier of the conflict being resolved.""" 195 | pass 196 | 197 | @property 198 | @abstractmethod 199 | def actions(self) -> List[Dict[str, Any]]: 200 | """List of actions to be taken as part of the resolution.""" 201 | pass 202 | 203 | @property 204 | @abstractmethod 205 | def timestamp(self) -> datetime: 206 | """Timestamp of when the resolution was generated.""" 207 | pass 208 | 209 | @property 210 | @abstractmethod 211 | def explanation(self) -> str: 212 | """ 213 | Provides an explanation for the resolution. 214 | 215 | Returns: 216 | str: A human-readable explanation of the resolution. 217 | """ 218 | pass 219 | 220 | @abstractmethod 221 | def apply(self) -> None: 222 | """ 223 | Applies the resolution to the affected agent or system. 224 | """ 225 | pass 226 | 227 | 228 | class ResolutionStrategy(ABC): 229 | """ 230 | Abstract base class for resolution strategies. 231 | Strategies define how conflicts are resolved. 232 | """ 233 | 234 | @abstractmethod 235 | def resolve(self, conflict: Conflict) -> Resolution: 236 | """ 237 | Resolves a conflict using a specific strategy. 238 | 239 | Args: 240 | conflict (Conflict): The conflict to resolve. 241 | 242 | Returns: 243 | Resolution: The resolution generated for the conflict. 244 | """ 245 | pass 246 | -------------------------------------------------------------------------------- /ovadare/core/event_dispatcher.py: -------------------------------------------------------------------------------- 1 | # ovadare/core/event_dispatcher.py 2 | 3 | """ 4 | Event Dispatcher Module for the Ovadare Framework 5 | 6 | This module provides the EventDispatcher class, which facilitates the registration, 7 | deregistration, and notification of event listeners within the framework. 8 | """ 9 | 10 | from typing import Callable, Dict, List, Any 11 | from threading import Lock 12 | import logging 13 | 14 | # Configure the logger for this module 15 | logger = logging.getLogger(__name__) 16 | logger.setLevel(logging.DEBUG) 17 | 18 | class EventDispatcher: 19 | """ 20 | The EventDispatcher is responsible for managing event listeners and dispatching 21 | events to the appropriate listeners in a thread-safe manner. 22 | """ 23 | 24 | def __init__(self): 25 | """ 26 | Initializes the EventDispatcher with an empty listener registry and a lock 27 | for thread-safe operations. 28 | """ 29 | self._listeners: Dict[str, List[Callable[..., None]]] = {} 30 | self._lock = Lock() 31 | logger.debug("EventDispatcher initialized with an empty listener registry.") 32 | 33 | def register_listener(self, event_type: str, listener: Callable[..., None]) -> None: 34 | """ 35 | Registers a listener for a specific event type. 36 | 37 | Args: 38 | event_type (str): The type of event to listen for. 39 | listener (Callable[..., None]): The callback function to invoke when the event occurs. 40 | """ 41 | if not callable(listener): 42 | logger.error("Listener must be callable.") 43 | raise TypeError("Listener must be callable.") 44 | 45 | with self._lock: 46 | listeners = self._listeners.setdefault(event_type, []) 47 | if listener not in listeners: 48 | listeners.append(listener) 49 | logger.debug(f"Listener '{listener.__name__}' registered for event type '{event_type}'.") 50 | else: 51 | logger.warning(f"Listener '{listener.__name__}' is already registered for event type '{event_type}'.") 52 | 53 | def deregister_listener(self, event_type: str, listener: Callable[..., None]) -> None: 54 | """ 55 | Deregisters a listener from a specific event type. 56 | 57 | Args: 58 | event_type (str): The type of event the listener was registered for. 59 | listener (Callable[..., None]): The listener to remove. 60 | """ 61 | with self._lock: 62 | listeners = self._listeners.get(event_type) 63 | if listeners and listener in listeners: 64 | listeners.remove(listener) 65 | logger.debug(f"Listener '{listener.__name__}' deregistered from event type '{event_type}'.") 66 | if not listeners: 67 | del self._listeners[event_type] 68 | logger.debug(f"No more listeners for event type '{event_type}', event type removed.") 69 | else: 70 | logger.warning(f"Listener '{listener.__name__}' not found for event type '{event_type}'.") 71 | 72 | def dispatch_event(self, event_type: str, **event_data: Any) -> None: 73 | """ 74 | Dispatches an event to all registered listeners for the event type. 75 | 76 | Args: 77 | event_type (str): The type of event being dispatched. 78 | **event_data: Arbitrary keyword arguments representing event data. 79 | """ 80 | with self._lock: 81 | listeners = self._listeners.get(event_type, []).copy() 82 | 83 | if not listeners: 84 | logger.debug(f"No listeners registered for event type '{event_type}'.") 85 | return 86 | 87 | logger.debug(f"Dispatching event '{event_type}' to {len(listeners)} listener(s) with data: {event_data}") 88 | 89 | for listener in listeners: 90 | try: 91 | listener(**event_data) 92 | logger.debug(f"Listener '{listener.__name__}' executed successfully for event type '{event_type}'.") 93 | except Exception as e: 94 | logger.error(f"Error executing listener '{listener.__name__}' for event type '{event_type}': {e}", exc_info=True) 95 | 96 | def get_registered_event_types(self) -> List[str]: 97 | """ 98 | Returns a list of event types that have registered listeners. 99 | 100 | Returns: 101 | List[str]: A list of event type names. 102 | """ 103 | with self._lock: 104 | event_types = list(self._listeners.keys()) 105 | logger.debug(f"Currently registered event types: {event_types}") 106 | return event_types 107 | 108 | def get_listeners(self, event_type: str) -> List[Callable[..., None]]: 109 | """ 110 | Retrieves the list of listeners registered for a specific event type. 111 | 112 | Args: 113 | event_type (str): The event type to get listeners for. 114 | 115 | Returns: 116 | List[Callable[..., None]]: A list of listener functions. 117 | """ 118 | with self._lock: 119 | listeners = self._listeners.get(event_type, []).copy() 120 | logger.debug(f"Retrieved {len(listeners)} listener(s) for event type '{event_type}'.") 121 | return listeners 122 | -------------------------------------------------------------------------------- /ovadare/core/framework.py: -------------------------------------------------------------------------------- 1 | # ovadare/core/framework.py 2 | 3 | """ 4 | Ovadare Framework Core Module 5 | 6 | This module provides the OvadareFramework class, which serves as the central 7 | orchestrator for the Ovadare system. It initializes and manages the core components, 8 | including the AgentRegistry, EventDispatcher, PolicyManager, ConflictDetector, 9 | ResolutionEngine, MonitoringService, APIEndpoints, FeedbackManager, EscalationManager, 10 | AuthenticationManager, AuthorizationManager, and SecretsManager. 11 | """ 12 | 13 | import logging 14 | from typing import Optional, Dict, Any 15 | 16 | from ovadare.core.event_dispatcher import EventDispatcher 17 | from ovadare.agents.agent_registry import AgentRegistry 18 | from ovadare.agents.agent_sdk import AgentSDK 19 | from ovadare.policies.policy_manager import PolicyManager 20 | from ovadare.conflicts.conflict_detector import ConflictDetector 21 | from ovadare.conflicts.resolution_engine import ResolutionEngine 22 | from ovadare.monitoring.monitoring_service import MonitoringService 23 | from ovadare.communication.api_endpoints import APIEndpoints 24 | from ovadare.utils.configuration import Configuration 25 | from ovadare.feedback.feedback_manager import FeedbackManager 26 | from ovadare.escalation.escalation_manager import EscalationManager 27 | from ovadare.security.authentication import AuthenticationManager 28 | from ovadare.security.authorization import AuthorizationManager 29 | from ovadare.utils.secrets_manager import SecretsManager 30 | 31 | # Configure the logger for this module 32 | logger = logging.getLogger(__name__) 33 | logger.setLevel(logging.DEBUG) 34 | 35 | 36 | class OvadareFramework: 37 | """ 38 | The OvadareFramework class initializes and manages the core components of the system. 39 | It provides methods to start and stop the framework and ensures that all components 40 | are properly integrated. 41 | """ 42 | 43 | def __init__(self, config: Optional[Dict[str, Any]] = None) -> None: 44 | """ 45 | Initializes the Ovadare Framework. 46 | 47 | Args: 48 | config (Optional[Dict[str, Any]]): Configuration settings for the framework. 49 | If None, the default configuration is loaded. 50 | """ 51 | logger.info("Initializing Ovadare Framework...") 52 | 53 | # Load configuration 54 | if config: 55 | Configuration._config = config 56 | else: 57 | Configuration.load_default() 58 | self.config = Configuration.get_all() 59 | logger.debug(f"Configuration loaded: {self.config}") 60 | 61 | # Initialize SecretsManager 62 | self.secrets_manager = SecretsManager() 63 | 64 | # Initialize core components 65 | self.event_dispatcher = EventDispatcher() 66 | self.agent_registry = AgentRegistry() 67 | self.feedback_manager = FeedbackManager() 68 | self.policy_manager = PolicyManager() 69 | self.conflict_detector = ConflictDetector(policy_manager=self.policy_manager) 70 | self.resolution_engine = ResolutionEngine() 71 | self.authentication_manager = AuthenticationManager() 72 | self.authorization_manager = AuthorizationManager() 73 | self.escalation_manager = EscalationManager(secrets_manager=self.secrets_manager) 74 | self.monitoring_service = MonitoringService( 75 | agent_registry=self.agent_registry, 76 | conflict_detector=self.conflict_detector 77 | ) 78 | self.api_endpoints = APIEndpoints( 79 | agent_registry=self.agent_registry, 80 | event_dispatcher=self.event_dispatcher, 81 | authentication_manager=self.authentication_manager, 82 | authorization_manager=self.authorization_manager 83 | ) 84 | # Update AgentSDK initialization to remove unnecessary parameters 85 | self.agent_sdk = AgentSDK( 86 | api_base_url=Configuration.get('api_base_url', 'http://localhost:5000'), 87 | authentication_manager=self.authentication_manager, 88 | authorization_manager=self.authorization_manager 89 | ) 90 | 91 | logger.debug("Core components initialized.") 92 | 93 | # Register event listeners 94 | self._register_event_listeners() 95 | logger.debug("Event listeners registered.") 96 | 97 | # Rest of the OvadareFramework class remains unchanged... 98 | -------------------------------------------------------------------------------- /ovadare/core/ioc_container.py: -------------------------------------------------------------------------------- 1 | # ovadare/core/ioc_container.py 2 | 3 | """ 4 | Inversion of Control (IoC) Container Module for the Ovadare Framework 5 | 6 | This module provides the IoCContainer class, which manages the registration and resolution 7 | of dependencies within the framework. It supports singleton and transient registrations 8 | and ensures that components are instantiated with their required dependencies. 9 | """ 10 | 11 | from typing import Any, Callable, Dict, Type 12 | from threading import Lock 13 | import logging 14 | 15 | # Configure the logger for this module 16 | logger = logging.getLogger(__name__) 17 | logger.setLevel(logging.DEBUG) 18 | 19 | 20 | class IoCContainer: 21 | """ 22 | The IoCContainer manages dependency injection by registering and resolving components. 23 | It supports singleton and transient lifetimes. 24 | """ 25 | 26 | def __init__(self): 27 | """ 28 | Initializes the IoCContainer. 29 | """ 30 | self._registrations: Dict[Type, Callable[[], Any]] = {} 31 | self._singletons: Dict[Type, Any] = {} 32 | self._lock = Lock() 33 | logger.debug("IoCContainer initialized.") 34 | 35 | def register_singleton(self, interface: Type, implementation: Callable[[], Any]) -> None: 36 | """ 37 | Registers a singleton component. 38 | 39 | Args: 40 | interface (Type): The interface or base class. 41 | implementation (Callable[[], Any]): A factory function that returns an instance of the implementation. 42 | """ 43 | with self._lock: 44 | self._registrations[interface] = implementation 45 | logger.debug(f"Singleton registered for interface '{interface.__name__}'.") 46 | 47 | def register_transient(self, interface: Type, implementation: Callable[[], Any]) -> None: 48 | """ 49 | Registers a transient component. 50 | 51 | Args: 52 | interface (Type): The interface or base class. 53 | implementation (Callable[[], Any]): A factory function that returns a new instance each time. 54 | """ 55 | with self._lock: 56 | self._registrations[interface] = implementation 57 | logger.debug(f"Transient registered for interface '{interface.__name__}'.") 58 | 59 | def resolve(self, interface: Type) -> Any: 60 | """ 61 | Resolves an instance of the specified interface. 62 | 63 | Args: 64 | interface (Type): The interface or base class to resolve. 65 | 66 | Returns: 67 | Any: An instance of the requested component. 68 | 69 | Raises: 70 | ValueError: If the interface is not registered. 71 | """ 72 | with self._lock: 73 | if interface not in self._registrations: 74 | error_message = f"No registration found for interface '{interface.__name__}'." 75 | logger.error(error_message) 76 | raise ValueError(error_message) 77 | 78 | if interface in self._singletons: 79 | logger.debug(f"Returning existing singleton instance for interface '{interface.__name__}'.") 80 | return self._singletons[interface] 81 | 82 | logger.debug(f"Creating new instance for interface '{interface.__name__}'.") 83 | implementation = self._registrations[interface] 84 | instance = implementation() 85 | 86 | # If the registration is a singleton, store the instance 87 | if self._is_singleton(interface): 88 | self._singletons[interface] = instance 89 | logger.debug(f"Singleton instance stored for interface '{interface.__name__}'.") 90 | 91 | return instance 92 | 93 | def _is_singleton(self, interface: Type) -> bool: 94 | """ 95 | Checks if the registered implementation for the interface is a singleton. 96 | 97 | Args: 98 | interface (Type): The interface to check. 99 | 100 | Returns: 101 | bool: True if the implementation is registered as a singleton, False otherwise. 102 | """ 103 | # In this simple implementation, we assume that all registrations are singletons 104 | # unless explicitly registered as transient. This method can be expanded if needed. 105 | return interface in self._registrations and interface not in self._singletons 106 | 107 | def clear(self) -> None: 108 | """ 109 | Clears all registrations and singletons from the container. 110 | """ 111 | with self._lock: 112 | self._registrations.clear() 113 | self._singletons.clear() 114 | logger.debug("IoCContainer cleared.") 115 | 116 | def is_registered(self, interface: Type) -> bool: 117 | """ 118 | Checks if an interface is registered. 119 | 120 | Args: 121 | interface (Type): The interface to check. 122 | 123 | Returns: 124 | bool: True if the interface is registered, False otherwise. 125 | """ 126 | with self._lock: 127 | is_registered = interface in self._registrations 128 | logger.debug(f"Interface '{interface.__name__}' is {'registered' if is_registered else 'not registered'}.") 129 | return is_registered 130 | -------------------------------------------------------------------------------- /ovadare/escalation/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/ovadare/escalation/__init__.py -------------------------------------------------------------------------------- /ovadare/escalation/ai_assistant.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/ovadare/escalation/ai_assistant.py -------------------------------------------------------------------------------- /ovadare/escalation/escalation_manager.py: -------------------------------------------------------------------------------- 1 | # ovadare/escalation/escalation_manager.py 2 | 3 | """ 4 | Escalation Manager Module for the Ovadare Framework 5 | 6 | This module provides the EscalationManager class, which handles the escalation 7 | of unresolved conflicts by notifying administrators via email and SMS. It 8 | integrates with external services like SendGrid and Twilio, using the 9 | SecretsManager to securely manage API keys and credentials. 10 | """ 11 | 12 | import logging 13 | from typing import List, Dict, Any 14 | from ovadare.conflicts.conflict import Conflict 15 | from ovadare.utils.secrets_manager import SecretsManager 16 | 17 | # Third-party libraries for sending emails and SMS 18 | import sendgrid 19 | from sendgrid.helpers.mail import Mail 20 | from twilio.rest import Client 21 | 22 | # Configure the logger for this module 23 | logger = logging.getLogger(__name__) 24 | logger.setLevel(logging.DEBUG) 25 | 26 | 27 | class EscalationManager: 28 | """ 29 | Handles escalation of unresolved conflicts by notifying administrators. 30 | """ 31 | 32 | def __init__(self, secrets_manager: SecretsManager) -> None: 33 | """ 34 | Initializes the EscalationManager. 35 | 36 | Args: 37 | secrets_manager (SecretsManager): Manages secure access to API keys and credentials. 38 | """ 39 | self.secrets_manager = secrets_manager 40 | self.admin_emails = self._get_admin_emails() 41 | self.admin_phone_numbers = self._get_admin_phone_numbers() 42 | self.sendgrid_api_key = self.secrets_manager.get_secret('SENDGRID_API_KEY') 43 | self.twilio_account_sid = self.secrets_manager.get_secret('TWILIO_ACCOUNT_SID') 44 | self.twilio_auth_token = self.secrets_manager.get_secret('TWILIO_AUTH_TOKEN') 45 | self.twilio_phone_number = self.secrets_manager.get_secret('TWILIO_PHONE_NUMBER') 46 | logger.debug("EscalationManager initialized with notification services.") 47 | 48 | def escalate_conflict(self, conflict: Conflict) -> None: 49 | """ 50 | Escalates an unresolved conflict by notifying administrators. 51 | 52 | Args: 53 | conflict (Conflict): The conflict to escalate. 54 | """ 55 | logger.info(f"Escalating conflict '{conflict.conflict_id}'.") 56 | email_subject = f"Urgent: Conflict {conflict.conflict_id} Requires Attention" 57 | email_body = f"A conflict has been detected and requires immediate attention.\n\nDetails:\n{conflict}" 58 | sms_message = f"Conflict {conflict.conflict_id} requires attention. Check your email for details." 59 | 60 | # Send notifications 61 | self._send_email(subject=email_subject, body=email_body) 62 | self._send_sms(message=sms_message) 63 | 64 | def _send_email(self, subject: str, body: str) -> None: 65 | """ 66 | Sends an email notification to administrators. 67 | 68 | Args: 69 | subject (str): The email subject. 70 | body (str): The email body. 71 | """ 72 | if not self.sendgrid_api_key: 73 | logger.error("SendGrid API key is missing. Cannot send email notifications.") 74 | return 75 | 76 | sg = sendgrid.SendGridAPIClient(api_key=self.sendgrid_api_key) 77 | for admin_email in self.admin_emails: 78 | try: 79 | mail = Mail( 80 | from_email='noreply@ovadare.com', 81 | to_emails=admin_email, 82 | subject=subject, 83 | plain_text_content=body 84 | ) 85 | response = sg.send(mail) 86 | if response.status_code in [200, 202]: 87 | logger.info(f"Email notification sent to {admin_email}.") 88 | else: 89 | logger.error(f"Failed to send email to {admin_email}: {response.status_code}") 90 | except Exception as e: 91 | logger.error(f"Exception occurred while sending email to {admin_email}: {e}") 92 | 93 | def _send_sms(self, message: str) -> None: 94 | """ 95 | Sends an SMS notification to administrators. 96 | 97 | Args: 98 | message (str): The SMS message content. 99 | """ 100 | if not all([self.twilio_account_sid, self.twilio_auth_token, self.twilio_phone_number]): 101 | logger.error("Twilio credentials are missing. Cannot send SMS notifications.") 102 | return 103 | 104 | client = Client(self.twilio_account_sid, self.twilio_auth_token) 105 | for admin_phone in self.admin_phone_numbers: 106 | try: 107 | sms = client.messages.create( 108 | body=message, 109 | from_=self.twilio_phone_number, 110 | to=admin_phone 111 | ) 112 | logger.info(f"SMS notification sent to {admin_phone}. SID: {sms.sid}") 113 | except Exception as e: 114 | logger.error(f"Exception occurred while sending SMS to {admin_phone}: {e}") 115 | 116 | def _get_admin_emails(self) -> List[str]: 117 | """ 118 | Retrieves the list of administrator email addresses. 119 | 120 | Returns: 121 | List[str]: A list of admin email addresses. 122 | """ 123 | emails = self.secrets_manager.get_secret('ADMIN_EMAILS') 124 | if emails: 125 | admin_emails = emails.split(',') 126 | logger.debug(f"Admin emails retrieved: {admin_emails}") 127 | return admin_emails 128 | else: 129 | logger.warning("No admin emails configured.") 130 | return [] 131 | 132 | def _get_admin_phone_numbers(self) -> List[str]: 133 | """ 134 | Retrieves the list of administrator phone numbers. 135 | 136 | Returns: 137 | List[str]: A list of admin phone numbers. 138 | """ 139 | phones = self.secrets_manager.get_secret('ADMIN_PHONE_NUMBERS') 140 | if phones: 141 | admin_phones = phones.split(',') 142 | logger.debug(f"Admin phone numbers retrieved: {admin_phones}") 143 | return admin_phones 144 | else: 145 | logger.warning("No admin phone numbers configured.") 146 | return [] 147 | -------------------------------------------------------------------------------- /ovadare/escalation/human_interface.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/ovadare/escalation/human_interface.py -------------------------------------------------------------------------------- /ovadare/feedback/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/ovadare/feedback/__init__.py -------------------------------------------------------------------------------- /ovadare/feedback/adaptive_learning.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/ovadare/feedback/adaptive_learning.py -------------------------------------------------------------------------------- /ovadare/feedback/feedback_loop.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/ovadare/feedback/feedback_loop.py -------------------------------------------------------------------------------- /ovadare/feedback/feedback_manager.py: -------------------------------------------------------------------------------- 1 | # ovadare/feedback/feedback_manager.py 2 | 3 | """ 4 | Feedback Manager Module for the Ovadare Framework 5 | 6 | This module provides the FeedbackManager class, which handles the collection, 7 | storage, and processing of feedback from agents and users. The feedback can 8 | be used to improve policies, resolutions, and overall system performance. 9 | """ 10 | 11 | import logging 12 | from typing import Dict, Any, List 13 | from threading import Lock 14 | 15 | # Configure the logger for this module 16 | logger = logging.getLogger(__name__) 17 | logger.setLevel(logging.DEBUG) 18 | 19 | 20 | class FeedbackManager: 21 | """ 22 | Handles the collection, storage, and processing of feedback from agents and users. 23 | """ 24 | 25 | def __init__(self) -> None: 26 | """ 27 | Initializes the FeedbackManager. 28 | """ 29 | self._feedback_store: List[Dict[str, Any]] = [] 30 | self._lock = Lock() 31 | logger.debug("FeedbackManager initialized.") 32 | 33 | def submit_feedback(self, feedback_data: Dict[str, Any]) -> None: 34 | """ 35 | Submits feedback to the FeedbackManager. 36 | 37 | Args: 38 | feedback_data (Dict[str, Any]): A dictionary containing feedback details. 39 | Expected keys include 'agent_id', 'user_id', 'feedback_type', 40 | 'message', and 'timestamp'. 41 | """ 42 | required_keys = {'feedback_type', 'message', 'timestamp'} 43 | if not required_keys.issubset(feedback_data.keys()): 44 | logger.error("Feedback data is missing required keys.") 45 | raise ValueError(f"Feedback data must include {required_keys}") 46 | 47 | logger.debug(f"Submitting feedback: {feedback_data}") 48 | with self._lock: 49 | self._feedback_store.append(feedback_data) 50 | logger.info("Feedback submitted successfully.") 51 | 52 | def get_all_feedback(self) -> List[Dict[str, Any]]: 53 | """ 54 | Retrieves all submitted feedback. 55 | 56 | Returns: 57 | List[Dict[str, Any]]: A list of all feedback entries. 58 | """ 59 | with self._lock: 60 | feedback_copy = self._feedback_store.copy() 61 | logger.debug(f"Retrieved {len(feedback_copy)} feedback entries.") 62 | return feedback_copy 63 | 64 | def get_feedback_by_agent(self, agent_id: str) -> List[Dict[str, Any]]: 65 | """ 66 | Retrieves feedback submitted by a specific agent. 67 | 68 | Args: 69 | agent_id (str): The ID of the agent. 70 | 71 | Returns: 72 | List[Dict[str, Any]]: A list of feedback entries from the specified agent. 73 | """ 74 | with self._lock: 75 | agent_feedback = [fb for fb in self._feedback_store if fb.get('agent_id') == agent_id] 76 | logger.debug(f"Retrieved {len(agent_feedback)} feedback entries for agent '{agent_id}'.") 77 | return agent_feedback 78 | 79 | def process_feedback(self) -> None: 80 | """ 81 | Processes collected feedback to identify trends or issues. 82 | This method can be expanded to include analytics or integration 83 | with policy updates. 84 | """ 85 | logger.debug("Processing feedback...") 86 | with self._lock: 87 | total_feedback = len(self._feedback_store) 88 | # Placeholder for processing logic 89 | if total_feedback == 0: 90 | logger.info("No feedback to process.") 91 | return 92 | 93 | # Example processing: simple count of feedback types 94 | feedback_types = {} 95 | for fb in self._feedback_store: 96 | f_type = fb.get('feedback_type', 'unknown') 97 | feedback_types[f_type] = feedback_types.get(f_type, 0) + 1 98 | 99 | logger.info(f"Processed {total_feedback} feedback entries.") 100 | logger.debug(f"Feedback types count: {feedback_types}") 101 | 102 | # After processing, feedback could be archived or retained 103 | # For simplicity, we're not clearing the feedback store here 104 | -------------------------------------------------------------------------------- /ovadare/feedback/policy_adjustment.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/ovadare/feedback/policy_adjustment.py -------------------------------------------------------------------------------- /ovadare/monitoring/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/ovadare/monitoring/__init__.py -------------------------------------------------------------------------------- /ovadare/monitoring/analytics.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/ovadare/monitoring/analytics.py -------------------------------------------------------------------------------- /ovadare/monitoring/dashboards.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/ovadare/monitoring/dashboards.py -------------------------------------------------------------------------------- /ovadare/monitoring/monitoring_service.py: -------------------------------------------------------------------------------- 1 | # ovadare/monitoring/monitoring_service.py 2 | 3 | """ 4 | Monitoring Service Module for the Ovadare Framework 5 | 6 | This module provides the MonitoringService class, which collects and reports 7 | metrics and logs for the Ovadare framework. It helps in monitoring system health, 8 | performance, and detecting potential issues. 9 | """ 10 | 11 | import logging 12 | import threading 13 | from typing import Optional 14 | 15 | from ovadare.agents.agent_registry import AgentRegistry 16 | from ovadare.conflicts.conflict_detector import ConflictDetector 17 | 18 | # Configure the logger for this module 19 | logger = logging.getLogger(__name__) 20 | logger.setLevel(logging.DEBUG) 21 | 22 | 23 | class MonitoringService: 24 | """ 25 | The MonitoringService collects and reports metrics and logs for the Ovadare framework. 26 | """ 27 | 28 | def __init__( 29 | self, 30 | agent_registry: AgentRegistry, 31 | conflict_detector: ConflictDetector, 32 | interval: int = 10 33 | ): 34 | """ 35 | Initializes the MonitoringService. 36 | 37 | Args: 38 | agent_registry (AgentRegistry): An instance of AgentRegistry. 39 | conflict_detector (ConflictDetector): An instance of ConflictDetector. 40 | interval (int): The interval in seconds at which metrics are collected. 41 | """ 42 | self.agent_registry = agent_registry 43 | self.conflict_detector = conflict_detector 44 | self.interval = interval 45 | self._stop_event = threading.Event() 46 | self.thread = threading.Thread(target=self._run, daemon=True) 47 | logger.debug("MonitoringService initialized with interval %d seconds.", self.interval) 48 | 49 | def start(self): 50 | """ 51 | Starts the monitoring service in a separate thread. 52 | """ 53 | logger.info("Starting MonitoringService...") 54 | self.thread.start() 55 | logger.info("MonitoringService started.") 56 | 57 | def stop(self): 58 | """ 59 | Stops the monitoring service. 60 | """ 61 | logger.info("Stopping MonitoringService...") 62 | self._stop_event.set() 63 | self.thread.join() 64 | logger.info("MonitoringService stopped.") 65 | 66 | def _run(self): 67 | """ 68 | The main loop that collects and reports metrics at specified intervals. 69 | """ 70 | while not self._stop_event.is_set(): 71 | try: 72 | self.collect_metrics() 73 | except Exception as e: 74 | logger.error("Error during metric collection: %s", e, exc_info=True) 75 | self._stop_event.wait(self.interval) 76 | 77 | def collect_metrics(self): 78 | """ 79 | Collects and logs metrics from various components. 80 | """ 81 | metrics = { 82 | 'active_agents': self.get_active_agents_count(), 83 | 'conflicts_detected': self.get_conflicts_detected_count(), 84 | } 85 | logger.info("Collected Metrics: %s", metrics) 86 | 87 | def get_active_agents_count(self) -> int: 88 | """ 89 | Retrieves the number of active agents. 90 | 91 | Returns: 92 | int: The count of active agents. 93 | """ 94 | active_agents = len(self.agent_registry.get_all_agents()) 95 | logger.debug("Active agents count: %d", active_agents) 96 | return active_agents 97 | 98 | def get_conflicts_detected_count(self) -> int: 99 | """ 100 | Retrieves the number of conflicts currently detected. 101 | 102 | Returns: 103 | int: The count of detected conflicts. 104 | """ 105 | conflicts_detected = len(self.conflict_detector.get_all_conflicts()) 106 | logger.debug("Conflicts detected count: %d", conflicts_detected) 107 | return conflicts_detected 108 | -------------------------------------------------------------------------------- /ovadare/monitoring/reporting.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/ovadare/monitoring/reporting.py -------------------------------------------------------------------------------- /ovadare/policies/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/ovadare/policies/__init__.py -------------------------------------------------------------------------------- /ovadare/policies/dynamic_policy_loader.py: -------------------------------------------------------------------------------- 1 | # ovadare/policies/dynamic_policy_loader.py 2 | 3 | """ 4 | Dynamic Policy Loader Module for the Ovadare Framework 5 | 6 | This module provides the DynamicPolicyLoader class, which is responsible for 7 | loading policies from various sources, such as JSON files, databases, or APIs. 8 | """ 9 | 10 | from typing import List, Optional, Callable 11 | import logging 12 | import json 13 | import os 14 | 15 | from ovadare.policies.policy import Policy, ConcretePolicy, PolicyRule, PolicyPriority 16 | 17 | # Configure the logger for this module 18 | logger = logging.getLogger(__name__) 19 | logger.setLevel(logging.DEBUG) 20 | 21 | 22 | class DynamicPolicyLoader: 23 | """ 24 | The DynamicPolicyLoader is responsible for loading policies from a specified source. 25 | """ 26 | 27 | def __init__(self): 28 | """ 29 | Initializes the DynamicPolicyLoader. 30 | """ 31 | logger.debug("DynamicPolicyLoader initialized.") 32 | 33 | def load_policies(self, source: Optional[str] = None) -> List[Policy]: 34 | """ 35 | Loads policies from the specified source. 36 | 37 | Args: 38 | source (Optional[str]): The source from which to load policies. 39 | If None, a default source is used (e.g., 'policies.json'). 40 | 41 | Returns: 42 | List[Policy]: A list of loaded Policy instances. 43 | """ 44 | source = source or 'policies.json' 45 | logger.debug(f"Loading policies from source: '{source}'") 46 | 47 | if not os.path.exists(source): 48 | logger.error(f"Policy source '{source}' not found.") 49 | return [] 50 | 51 | try: 52 | with open(source, 'r') as f: 53 | policies_data = json.load(f) 54 | except Exception as e: 55 | logger.error(f"Error reading policy source '{source}': {e}", exc_info=True) 56 | return [] 57 | 58 | policies = [] 59 | for policy_dict in policies_data.get('policies', []): 60 | try: 61 | policy = self._parse_policy(policy_dict) 62 | policies.append(policy) 63 | logger.debug(f"Policy '{policy.policy_id}' loaded.") 64 | except Exception as e: 65 | logger.error(f"Error parsing policy: {e}", exc_info=True) 66 | 67 | logger.info(f"Total policies loaded: {len(policies)}") 68 | return policies 69 | 70 | def _parse_policy(self, policy_dict: dict) -> Policy: 71 | """ 72 | Parses a dictionary representing a policy into a Policy instance. 73 | 74 | Args: 75 | policy_dict (dict): The dictionary containing policy data. 76 | 77 | Returns: 78 | Policy: An instance of a Policy. 79 | 80 | Raises: 81 | ValueError: If required fields are missing or invalid. 82 | """ 83 | policy_id = policy_dict.get('policy_id') 84 | name = policy_dict.get('name') 85 | description = policy_dict.get('description', '') 86 | priority_str = policy_dict.get('priority', 'MEDIUM') 87 | rules_data = policy_dict.get('rules', []) 88 | 89 | if not policy_id or not name or not rules_data: 90 | raise ValueError("Policy must have 'policy_id', 'name', and at least one 'rule'.") 91 | 92 | try: 93 | priority = PolicyPriority[priority_str.upper()] 94 | except KeyError: 95 | raise ValueError(f"Invalid policy priority: '{priority_str}'.") 96 | 97 | policy = ConcretePolicy( 98 | policy_id=policy_id, 99 | name=name, 100 | description=description, 101 | priority=priority 102 | ) 103 | 104 | for rule_dict in rules_data: 105 | rule = self._parse_rule(rule_dict) 106 | policy.add_rule(rule) 107 | 108 | return policy 109 | 110 | def _parse_rule(self, rule_dict: dict) -> PolicyRule: 111 | """ 112 | Parses a dictionary representing a policy rule into a PolicyRule instance. 113 | 114 | Args: 115 | rule_dict (dict): The dictionary containing rule data. 116 | 117 | Returns: 118 | PolicyRule: An instance of a PolicyRule. 119 | 120 | Raises: 121 | ValueError: If required fields are missing or invalid. 122 | """ 123 | rule_id = rule_dict.get('rule_id') 124 | condition_code = rule_dict.get('condition') 125 | message = rule_dict.get('message') 126 | 127 | if not rule_id or not condition_code or not message: 128 | raise ValueError("Rule must have 'rule_id', 'condition', and 'message'.") 129 | 130 | # Compile the condition code into a callable function 131 | condition = self._compile_condition(condition_code, rule_id) 132 | 133 | rule = PolicyRule( 134 | rule_id=rule_id, 135 | condition=condition, 136 | message=message 137 | ) 138 | 139 | return rule 140 | 141 | def _compile_condition(self, condition_code: str, rule_id: str) -> Callable: 142 | """ 143 | Compiles a condition code string into a callable function. 144 | 145 | Args: 146 | condition_code (str): The code representing the condition. 147 | rule_id (str): The ID of the rule, used for logging. 148 | 149 | Returns: 150 | Callable: A function that evaluates the condition. 151 | 152 | Raises: 153 | Exception: If the condition code cannot be compiled. 154 | """ 155 | try: 156 | # Define a safe namespace for execution 157 | exec_globals = {'__builtins__': {}} 158 | exec_locals = {} 159 | 160 | # Define allowed built-in functions (if any) 161 | allowed_builtins = { 162 | 'len': len, 163 | 'max': max, 164 | 'min': min, 165 | # Add other safe built-ins if needed 166 | } 167 | exec_globals['__builtins__'] = allowed_builtins 168 | 169 | # Compile the condition code 170 | compiled_code = compile(condition_code, '', 'exec') 171 | exec(compiled_code, exec_globals, exec_locals) 172 | condition_func = exec_locals.get('condition') 173 | if not condition_func: 174 | raise ValueError("Condition code must define a 'condition' function.") 175 | if not callable(condition_func): 176 | raise ValueError("'condition' must be a callable function.") 177 | logger.debug(f"Condition function compiled for rule '{rule_id}'.") 178 | return condition_func 179 | except Exception as e: 180 | logger.error(f"Error compiling condition code for rule '{rule_id}': {e}", exc_info=True) 181 | raise 182 | -------------------------------------------------------------------------------- /ovadare/policies/dynamic_policy_manager.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/ovadare/policies/dynamic_policy_manager.py -------------------------------------------------------------------------------- /ovadare/policies/policy.py: -------------------------------------------------------------------------------- 1 | # ovadare/policies/policy.py 2 | 3 | """ 4 | Policy Module for the Ovadare Framework 5 | 6 | This module provides the abstract Policy class and concrete implementations for defining 7 | and evaluating policies within the framework. It includes the PolicyRule class for 8 | defining individual rules that make up a policy. 9 | """ 10 | 11 | from abc import ABC, abstractmethod 12 | from typing import List, Callable 13 | import logging 14 | 15 | # Configure the logger for this module 16 | logger = logging.getLogger(__name__) 17 | logger.setLevel(logging.DEBUG) 18 | 19 | 20 | class EvaluationResult: 21 | """ 22 | Represents the result of evaluating a policy or rule. 23 | """ 24 | 25 | def __init__(self, success: bool, message: str = ""): 26 | self.success = success 27 | self.message = message 28 | 29 | def __bool__(self): 30 | return self.success 31 | 32 | def __repr__(self): 33 | return f"EvaluationResult(success={self.success}, message='{self.message}')" 34 | 35 | 36 | class PolicyPriority: 37 | """ 38 | Enum-like class for policy priorities. 39 | """ 40 | LOW = 'LOW' 41 | MEDIUM = 'MEDIUM' 42 | HIGH = 'HIGH' 43 | 44 | 45 | class PolicyRule: 46 | """ 47 | Represents a single rule within a policy. 48 | """ 49 | 50 | def __init__(self, rule_id: str, condition: Callable[[dict], bool], message: str): 51 | """ 52 | Initializes a PolicyRule. 53 | 54 | Args: 55 | rule_id (str): The unique identifier of the rule. 56 | condition (Callable[[dict], bool]): A function that evaluates the rule condition. 57 | message (str): The message to return if the rule is violated. 58 | """ 59 | self.rule_id = rule_id 60 | self.condition = condition 61 | self.message = message 62 | 63 | def evaluate(self, action: dict) -> EvaluationResult: 64 | """ 65 | Evaluates the rule against an action. 66 | 67 | Args: 68 | action (dict): The action to evaluate. 69 | 70 | Returns: 71 | EvaluationResult: The result of the evaluation. 72 | """ 73 | try: 74 | if self.condition(action): 75 | logger.debug(f"Rule '{self.rule_id}' passed.") 76 | return EvaluationResult(True) 77 | else: 78 | logger.info(f"Rule '{self.rule_id}' violated: {self.message}") 79 | return EvaluationResult(False, self.message) 80 | except Exception as e: 81 | logger.error(f"Error evaluating rule '{self.rule_id}': {e}", exc_info=True) 82 | return EvaluationResult(False, f"Error evaluating rule '{self.rule_id}': {e}") 83 | 84 | 85 | class Policy(ABC): 86 | """ 87 | Abstract base class for policies. 88 | """ 89 | 90 | @property 91 | @abstractmethod 92 | def policy_id(self) -> str: 93 | pass 94 | 95 | @abstractmethod 96 | def evaluate(self, action: dict) -> EvaluationResult: 97 | pass 98 | 99 | 100 | class ConcretePolicy(Policy): 101 | """ 102 | A concrete implementation of a policy consisting of multiple rules. 103 | """ 104 | 105 | def __init__(self, policy_id: str, name: str, description: str, priority: str = PolicyPriority.MEDIUM): 106 | """ 107 | Initializes a ConcretePolicy. 108 | 109 | Args: 110 | policy_id (str): The unique identifier of the policy. 111 | name (str): The name of the policy. 112 | description (str): A description of the policy. 113 | priority (str): The priority level of the policy. 114 | """ 115 | self._policy_id = policy_id 116 | self.name = name 117 | self.description = description 118 | self.priority = priority 119 | self.rules: List[PolicyRule] = [] 120 | logger.debug(f"Policy '{self.policy_id}' initialized with priority '{self.priority}'.") 121 | 122 | @property 123 | def policy_id(self) -> str: 124 | return self._policy_id 125 | 126 | def add_rule(self, rule: PolicyRule) -> None: 127 | """ 128 | Adds a rule to the policy. 129 | 130 | Args: 131 | rule (PolicyRule): The rule to add. 132 | """ 133 | if not isinstance(rule, PolicyRule): 134 | logger.error("Attempted to add an object that is not a PolicyRule.") 135 | raise TypeError("rule must be an instance of PolicyRule") 136 | self.rules.append(rule) 137 | logger.debug(f"Rule '{rule.rule_id}' added to policy '{self.policy_id}'.") 138 | 139 | def evaluate(self, action: dict) -> EvaluationResult: 140 | """ 141 | Evaluates the action against all rules in the policy. 142 | 143 | Args: 144 | action (dict): The action to evaluate. 145 | 146 | Returns: 147 | EvaluationResult: The overall result of the policy evaluation. 148 | """ 149 | logger.debug(f"Evaluating action against policy '{self.policy_id}'.") 150 | for rule in self.rules: 151 | result = rule.evaluate(action) 152 | if not result: 153 | logger.info(f"Policy '{self.policy_id}' violated: {result.message}") 154 | return EvaluationResult(False, result.message) 155 | logger.debug(f"Action complies with policy '{self.policy_id}'.") 156 | return EvaluationResult(True) 157 | -------------------------------------------------------------------------------- /ovadare/policies/policy_manager.py: -------------------------------------------------------------------------------- 1 | # ovadare/policies/policy_manager.py 2 | 3 | """ 4 | Policy Manager Module for the Ovadare Framework 5 | 6 | This module provides the PolicyManager class, which manages the loading, storage, 7 | and evaluation of policies within the framework. It integrates with the 8 | DynamicPolicyLoader to dynamically load policies from configured sources. 9 | """ 10 | 11 | import logging 12 | from typing import List, Optional, Dict, Any 13 | from threading import Lock 14 | 15 | from ovadare.policies.policy import Policy, EvaluationResult 16 | from ovadare.policies.dynamic_policy_loader import DynamicPolicyLoader 17 | from ovadare.utils.configuration import Configuration 18 | 19 | # Configure the logger for this module 20 | logger = logging.getLogger(__name__) 21 | logger.setLevel(logging.DEBUG) 22 | 23 | 24 | class PolicyManager: 25 | """ 26 | The PolicyManager manages the loading, storage, and evaluation of policies. 27 | It integrates with the DynamicPolicyLoader to load policies from configured sources. 28 | """ 29 | 30 | def __init__(self): 31 | """ 32 | Initializes the PolicyManager. 33 | """ 34 | self._policies: List[Policy] = [] 35 | self._lock = Lock() 36 | self._loader = DynamicPolicyLoader() 37 | logger.debug("PolicyManager initialized.") 38 | 39 | # Load policies during initialization 40 | self.load_policies() 41 | 42 | def load_policies(self) -> None: 43 | """ 44 | Loads policies using the DynamicPolicyLoader from the configured source. 45 | """ 46 | policy_source = Configuration.get('policy_source', 'policies.json') 47 | logger.debug(f"Loading policies from source: '{policy_source}'") 48 | 49 | try: 50 | policies = self._loader.load_policies(source=policy_source) 51 | with self._lock: 52 | self._policies = policies 53 | logger.info(f"{len(policies)} policy(ies) loaded.") 54 | except Exception as e: 55 | logger.error(f"Error loading policies from source '{policy_source}': {e}", exc_info=True) 56 | 57 | def get_policies(self) -> List[Policy]: 58 | """ 59 | Retrieves the list of loaded policies. 60 | 61 | Returns: 62 | List[Policy]: A list of Policy instances. 63 | """ 64 | with self._lock: 65 | policies_copy = self._policies.copy() 66 | logger.debug(f"Retrieved {len(policies_copy)} policy(ies).") 67 | return policies_copy 68 | 69 | def get_policy_by_id(self, policy_id: str) -> Optional[Policy]: 70 | """ 71 | Retrieves a policy by its ID. 72 | 73 | Args: 74 | policy_id (str): The unique identifier of the policy. 75 | 76 | Returns: 77 | Optional[Policy]: The Policy instance if found, else None. 78 | """ 79 | with self._lock: 80 | for policy in self._policies: 81 | if policy.policy_id == policy_id: 82 | logger.debug(f"Policy '{policy_id}' found.") 83 | return policy 84 | logger.warning(f"Policy '{policy_id}' not found.") 85 | return None 86 | 87 | def evaluate_policies(self, action: Dict[str, Any]) -> List[EvaluationResult]: 88 | """ 89 | Evaluates the action against all loaded policies. 90 | 91 | Args: 92 | action (Dict[str, Any]): The action to evaluate. 93 | 94 | Returns: 95 | List[EvaluationResult]: A list of evaluation results for each policy. 96 | """ 97 | results = [] 98 | with self._lock: 99 | policies_copy = self._policies.copy() 100 | 101 | logger.debug(f"Evaluating action against {len(policies_copy)} policy(ies).") 102 | 103 | for policy in policies_copy: 104 | try: 105 | result = policy.evaluate(action) 106 | results.append(result) 107 | if not result: 108 | logger.info(f"Policy '{policy.policy_id}' violated: {result.message}") 109 | except Exception as e: 110 | logger.error(f"Error evaluating policy '{policy.policy_id}': {e}", exc_info=True) 111 | results.append(EvaluationResult(False, f"Error evaluating policy '{policy.policy_id}': {e}")) 112 | 113 | return results 114 | 115 | def add_policy(self, policy: Policy) -> None: 116 | """ 117 | Adds a new policy to the manager. 118 | 119 | Args: 120 | policy (Policy): The policy to add. 121 | """ 122 | if not isinstance(policy, Policy): 123 | logger.error("Attempted to add an object that is not a Policy.") 124 | raise TypeError("policy must be an instance of Policy") 125 | 126 | with self._lock: 127 | if any(p.policy_id == policy.policy_id for p in self._policies): 128 | logger.warning(f"Policy '{policy.policy_id}' already exists. Skipping addition.") 129 | return 130 | self._policies.append(policy) 131 | logger.debug(f"Policy '{policy.policy_id}' added.") 132 | 133 | def remove_policy(self, policy_id: str) -> None: 134 | """ 135 | Removes a policy by its ID. 136 | 137 | Args: 138 | policy_id (str): The unique identifier of the policy to remove. 139 | """ 140 | with self._lock: 141 | original_count = len(self._policies) 142 | self._policies = [p for p in self._policies if p.policy_id != policy_id] 143 | if len(self._policies) < original_count: 144 | logger.debug(f"Policy '{policy_id}' removed.") 145 | else: 146 | logger.warning(f"Policy '{policy_id}' not found. Nothing removed.") 147 | 148 | def reload_policies(self) -> None: 149 | """ 150 | Reloads policies from the configured source. 151 | """ 152 | logger.info("Reloading policies...") 153 | self.load_policies() 154 | logger.info("Policies reloaded.") 155 | -------------------------------------------------------------------------------- /ovadare/policies/policy_rules.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/ovadare/policies/policy_rules.py -------------------------------------------------------------------------------- /ovadare/security/authentication.py: -------------------------------------------------------------------------------- 1 | # ovadare/security/authentication.py 2 | 3 | import hashlib 4 | import time 5 | import logging 6 | 7 | logger = logging.getLogger(__name__) 8 | 9 | class AuthenticationManager: 10 | def __init__(self, token_expiry_duration=3600): 11 | self._users = {} 12 | self._tokens = {} 13 | self.token_expiry_duration = token_expiry_duration 14 | 15 | def register_user(self, user_id, password): 16 | if user_id in self._users: 17 | raise ValueError("User already exists.") 18 | self._users[user_id] = self._hash_password(password) 19 | logger.info(f"User '{user_id}' registered.") 20 | 21 | def authenticate(self, user_id, password): 22 | hashed_password = self._users.get(user_id) 23 | if hashed_password and hashed_password == self._hash_password(password): 24 | token = self._generate_token(user_id) 25 | self._tokens[token] = (user_id, time.time()) 26 | logger.info(f"User '{user_id}' authenticated.") 27 | return token 28 | logger.warning(f"Authentication failed for user '{user_id}'.") 29 | return None 30 | 31 | def validate_token(self, token): 32 | user_data = self._tokens.get(token) 33 | if user_data: 34 | user_id, timestamp = user_data 35 | if time.time() - timestamp < self.token_expiry_duration: 36 | logger.debug(f"Token for user '{user_id}' is valid.") 37 | return True 38 | else: 39 | logger.debug(f"Token for user '{user_id}' has expired.") 40 | del self._tokens[token] 41 | logger.debug("Invalid token.") 42 | return False 43 | 44 | def get_user_id_from_token(self, token): 45 | if self.validate_token(token): 46 | return self._tokens[token][0] 47 | return None 48 | 49 | def _hash_password(self, password): 50 | return hashlib.sha256(password.encode()).hexdigest() 51 | 52 | def _generate_token(self, user_id): 53 | token_str = f"{user_id}{time.time()}" 54 | return hashlib.sha256(token_str.encode()).hexdigest() 55 | -------------------------------------------------------------------------------- /ovadare/security/authorization.py: -------------------------------------------------------------------------------- 1 | # ovadare/security/authorization.py 2 | 3 | """ 4 | Authorization Module for the Ovadare Framework 5 | 6 | This module provides the AuthorizationManager class, which handles role-based 7 | access control (RBAC) to manage permissions for users and agents. It defines 8 | roles, permissions, and provides methods to check if a user or agent is authorized 9 | to perform a specific action. 10 | """ 11 | 12 | import logging 13 | from typing import Dict, List, Set 14 | 15 | # Configure the logger for this module 16 | logger = logging.getLogger(__name__) 17 | logger.setLevel(logging.DEBUG) 18 | 19 | 20 | class AuthorizationManager: 21 | """ 22 | Manages authorization by assigning roles and permissions to users and agents. 23 | Provides methods to check if an entity is authorized to perform a specific action. 24 | """ 25 | 26 | def __init__(self) -> None: 27 | """ 28 | Initializes the AuthorizationManager with default roles and permissions. 29 | """ 30 | self._roles_permissions: Dict[str, Set[str]] = {} 31 | self._user_roles: Dict[str, Set[str]] = {} 32 | self._initialize_default_roles() 33 | logger.debug("AuthorizationManager initialized.") 34 | 35 | def _initialize_default_roles(self) -> None: 36 | """ 37 | Initializes default roles and their associated permissions. 38 | """ 39 | # Define default roles and permissions 40 | self._roles_permissions = { 41 | 'agent': {'submit_action', 'submit_feedback'}, 42 | 'admin': {'submit_action', 'submit_feedback', 'manage_agents', 'manage_policies'} 43 | } 44 | logger.debug("Default roles and permissions initialized.") 45 | 46 | def assign_role(self, user_id: str, role: str) -> None: 47 | """ 48 | Assigns a role to a user or agent. 49 | 50 | Args: 51 | user_id (str): The ID of the user or agent. 52 | role (str): The role to assign. 53 | 54 | Raises: 55 | ValueError: If the role is not defined. 56 | """ 57 | if role not in self._roles_permissions: 58 | logger.error(f"Role '{role}' is not defined.") 59 | raise ValueError(f"Role '{role}' is not defined.") 60 | 61 | roles = self._user_roles.setdefault(user_id, set()) 62 | roles.add(role) 63 | logger.info(f"Role '{role}' assigned to user '{user_id}'.") 64 | 65 | def revoke_role(self, user_id: str, role: str) -> None: 66 | """ 67 | Revokes a role from a user or agent. 68 | 69 | Args: 70 | user_id (str): The ID of the user or agent. 71 | role (str): The role to revoke. 72 | 73 | Raises: 74 | ValueError: If the role is not assigned to the user. 75 | """ 76 | roles = self._user_roles.get(user_id) 77 | if not roles or role not in roles: 78 | logger.error(f"User '{user_id}' does not have role '{role}'.") 79 | raise ValueError(f"User '{user_id}' does not have role '{role}'.") 80 | roles.remove(role) 81 | logger.info(f"Role '{role}' revoked from user '{user_id}'.") 82 | 83 | def is_authorized(self, user_id: str, permission: str) -> bool: 84 | """ 85 | Checks if the user or agent has the specified permission. 86 | 87 | Args: 88 | user_id (str): The ID of the user or agent. 89 | permission (str): The permission to check. 90 | 91 | Returns: 92 | bool: True if authorized, False otherwise. 93 | """ 94 | roles = self._user_roles.get(user_id, set()) 95 | for role in roles: 96 | permissions = self._roles_permissions.get(role, set()) 97 | if permission in permissions: 98 | logger.debug(f"User '{user_id}' is authorized for permission '{permission}'.") 99 | return True 100 | logger.warning(f"User '{user_id}' is not authorized for permission '{permission}'.") 101 | return False 102 | 103 | def add_role(self, role: str, permissions: List[str]) -> None: 104 | """ 105 | Adds a new role with the specified permissions. 106 | 107 | Args: 108 | role (str): The name of the new role. 109 | permissions (List[str]): A list of permissions for the role. 110 | """ 111 | if role in self._roles_permissions: 112 | logger.error(f"Role '{role}' already exists.") 113 | raise ValueError(f"Role '{role}' already exists.") 114 | self._roles_permissions[role] = set(permissions) 115 | logger.info(f"Role '{role}' added with permissions: {permissions}") 116 | 117 | def remove_role(self, role: str) -> None: 118 | """ 119 | Removes an existing role. 120 | 121 | Args: 122 | role (str): The name of the role to remove. 123 | 124 | Raises: 125 | ValueError: If the role does not exist. 126 | """ 127 | if role not in self._roles_permissions: 128 | logger.error(f"Role '{role}' does not exist.") 129 | raise ValueError(f"Role '{role}' does not exist.") 130 | del self._roles_permissions[role] 131 | logger.info(f"Role '{role}' removed.") 132 | 133 | def get_user_roles(self, user_id: str) -> List[str]: 134 | """ 135 | Retrieves the list of roles assigned to a user. 136 | 137 | Args: 138 | user_id (str): The ID of the user or agent. 139 | 140 | Returns: 141 | List[str]: A list of role names. 142 | """ 143 | roles = list(self._user_roles.get(user_id, set())) 144 | logger.debug(f"User '{user_id}' has roles: {roles}") 145 | return roles 146 | 147 | def get_role_permissions(self, role: str) -> List[str]: 148 | """ 149 | Retrieves the list of permissions associated with a role. 150 | 151 | Args: 152 | role (str): The name of the role. 153 | 154 | Returns: 155 | List[str]: A list of permissions. 156 | 157 | Raises: 158 | ValueError: If the role does not exist. 159 | """ 160 | if role not in self._roles_permissions: 161 | logger.error(f"Role '{role}' does not exist.") 162 | raise ValueError(f"Role '{role}' does not exist.") 163 | permissions = list(self._roles_permissions[role]) 164 | logger.debug(f"Role '{role}' has permissions: {permissions}") 165 | return permissions 166 | -------------------------------------------------------------------------------- /ovadare/tests/autogen_tests.py: -------------------------------------------------------------------------------- 1 | # tests/autogen_tests.py 2 | 3 | from autogen.agent import Agent 4 | from autogen.environment import Environment 5 | import requests 6 | import unittest 7 | import time 8 | 9 | # Import any other necessary modules 10 | 11 | class TestAgent(Agent): 12 | def __init__(self, agent_id, api_base_url): 13 | super().__init__(agent_id) 14 | self.api_base_url = api_base_url 15 | self.token = None # To store authentication token 16 | 17 | def run(self): 18 | # Simulate agent registration and login 19 | self.register() 20 | self.login() 21 | # Perform actions 22 | self.submit_action() 23 | # Submit feedback 24 | self.submit_feedback() 25 | 26 | def register(self): 27 | url = f"{self.api_base_url}/register" 28 | data = {'user_id': self.agent_id, 'password': 'password123'} 29 | response = requests.post(url, json=data) 30 | if response.status_code == 201: 31 | print(f"{self.agent_id}: Registration successful.") 32 | else: 33 | print(f"{self.agent_id}: Registration failed. Response: {response.text}") 34 | 35 | def login(self): 36 | url = f"{self.api_base_url}/login" 37 | data = {'user_id': self.agent_id, 'password': 'password123'} 38 | response = requests.post(url, json=data) 39 | if response.status_code == 200: 40 | self.token = response.json().get('token') 41 | print(f"{self.agent_id}: Login successful. Token obtained.") 42 | else: 43 | print(f"{self.agent_id}: Login failed. Response: {response.text}") 44 | 45 | def submit_action(self): 46 | url = f"{self.api_base_url}/submit_action" 47 | headers = {'Authorization': self.token} 48 | action_data = {'type': 'test_action', 'timestamp': time.time()} 49 | data = {'agent_id': self.agent_id, 'action': action_data} 50 | response = requests.post(url, headers=headers, json=data) 51 | if response.status_code == 200: 52 | print(f"{self.agent_id}: Action submitted successfully.") 53 | else: 54 | print(f"{self.agent_id}: Action submission failed. Response: {response.text}") 55 | 56 | def submit_feedback(self): 57 | url = f"{self.api_base_url}/submit_feedback" 58 | headers = {'Authorization': self.token} 59 | feedback_data = { 60 | 'agent_id': self.agent_id, 61 | 'feedback_type': 'test_feedback', 62 | 'message': 'This is a test feedback.' 63 | } 64 | response = requests.post(url, headers=headers, json=feedback_data) 65 | if response.status_code == 200: 66 | print(f"{self.agent_id}: Feedback submitted successfully.") 67 | else: 68 | print(f"{self.agent_id}: Feedback submission failed. Response: {response.text}") 69 | 70 | 71 | def fetch_conflicts(api_base_url): 72 | url = f"{api_base_url}/get_conflicts" 73 | response = requests.get(url) 74 | if response.status_code == 200: 75 | return response.json().get('conflicts', []) 76 | else: 77 | print(f"Failed to fetch conflicts. Response: {response.text}") 78 | return [] 79 | 80 | def fetch_resolutions(api_base_url): 81 | url = f"{api_base_url}/get_resolutions" 82 | response = requests.get(url) 83 | if response.status_code == 200: 84 | return response.json().get('resolutions', []) 85 | else: 86 | print(f"Failed to fetch resolutions. Response: {response.text}") 87 | return [] 88 | 89 | class AutoGenTestCases(unittest.TestCase): 90 | 91 | def test_conflict_scenario(self): 92 | api_base_url = 'http://localhost:5000' 93 | 94 | # Instantiate agents 95 | agent1 = TestAgent('agent1', api_base_url) 96 | agent2 = TestAgent('agent2', api_base_url) 97 | 98 | # Run agents in the environment 99 | env = Environment(agents=[agent1, agent2]) 100 | env.run() 101 | 102 | # Give some time for the framework to process 103 | time.sleep(2) 104 | 105 | # Fetch conflicts and resolutions 106 | conflicts = fetch_conflicts(api_base_url) 107 | resolutions = fetch_resolutions(api_base_url) 108 | 109 | # Assert that conflicts were detected 110 | self.assertTrue(len(conflicts) > 0, "No conflicts were detected when expected.") 111 | 112 | # Assert that resolutions were applied 113 | self.assertEqual(len(conflicts), len(resolutions), "Resolutions were not applied to all conflicts.") 114 | 115 | if __name__ == '__main__': 116 | unittest.main() 117 | -------------------------------------------------------------------------------- /ovadare/tests/test_agent_sdk.py: -------------------------------------------------------------------------------- 1 | # tests/test_agent_sdk.py 2 | 3 | import unittest 4 | from unittest.mock import patch 5 | from ovadare.agents.agent_sdk import AgentSDK 6 | 7 | class TestAgentSDK(unittest.TestCase): 8 | 9 | @patch('ovadare.agents.agent_sdk.requests.post') 10 | def test_register(self, mock_post): 11 | # Mock the response for registration 12 | mock_post.return_value.status_code = 201 13 | sdk = AgentSDK(api_base_url='http://localhost:5000', authentication_manager=None, authorization_manager=None) 14 | result = sdk.register('agent1', 'password1') 15 | self.assertTrue(result) 16 | 17 | @patch('ovadare.agents.agent_sdk.requests.post') 18 | def test_login(self, mock_post): 19 | # Mock the response for login 20 | mock_post.return_value.status_code = 200 21 | mock_post.return_value.json.return_value = {'token': 'validtoken'} 22 | sdk = AgentSDK(api_base_url='http://localhost:5000', authentication_manager=None, authorization_manager=None) 23 | result = sdk.login('agent1', 'password1') 24 | self.assertTrue(result) 25 | self.assertEqual(sdk.token, 'validtoken') 26 | 27 | @patch('ovadare.agents.agent_sdk.requests.post') 28 | def test_submit_action(self, mock_post): 29 | # Mock the response for action submission 30 | mock_post.return_value.status_code = 200 31 | sdk = AgentSDK(api_base_url='http://localhost:5000', authentication_manager=None, authorization_manager=None) 32 | sdk.agent_id = 'agent1' 33 | sdk.token = 'validtoken' 34 | result = sdk.submit_action({'action_type': 'test_action'}) 35 | self.assertTrue(result) 36 | 37 | @patch('ovadare.agents.agent_sdk.requests.post') 38 | def test_submit_feedback(self, mock_post): 39 | # Mock the response for feedback submission 40 | mock_post.return_value.status_code = 200 41 | sdk = AgentSDK(api_base_url='http://localhost:5000', authentication_manager=None, authorization_manager=None) 42 | sdk.agent_id = 'agent1' 43 | sdk.token = 'validtoken' 44 | result = sdk.submit_feedback('test_feedback', 'This is a test.') 45 | self.assertTrue(result) 46 | 47 | if __name__ == '__main__': 48 | unittest.main() 49 | -------------------------------------------------------------------------------- /ovadare/tests/test_api_endpoints.py: -------------------------------------------------------------------------------- 1 | # tests/test_api_endpoints.py 2 | 3 | import unittest 4 | from flask import Flask 5 | from ovadare.communication.api_endpoints import APIEndpoints 6 | from ovadare.agents.agent_registry import AgentRegistry 7 | from ovadare.core.event_dispatcher import EventDispatcher 8 | from ovadare.security.authentication import AuthenticationManager 9 | from ovadare.security.authorization import AuthorizationManager 10 | 11 | class TestAPIEndpoints(unittest.TestCase): 12 | 13 | def setUp(self): 14 | self.agent_registry = AgentRegistry() 15 | self.event_dispatcher = EventDispatcher() 16 | self.authentication_manager = AuthenticationManager() 17 | self.authorization_manager = AuthorizationManager() 18 | self.api = APIEndpoints( 19 | agent_registry=self.agent_registry, 20 | event_dispatcher=self.event_dispatcher, 21 | authentication_manager=self.authentication_manager, 22 | authorization_manager=self.authorization_manager 23 | ) 24 | self.app = self.api.app 25 | self.client = self.app.test_client() 26 | 27 | def test_register(self): 28 | # Successful registration 29 | response = self.client.post('/register', json={'user_id': 'user1', 'password': 'pass1'}) 30 | self.assertEqual(response.status_code, 201) 31 | self.assertIn('User registered successfully', response.get_data(as_text=True)) 32 | # Duplicate registration 33 | response = self.client.post('/register', json={'user_id': 'user1', 'password': 'pass1'}) 34 | self.assertEqual(response.status_code, 400) 35 | 36 | def test_login(self): 37 | # Register a user first 38 | self.client.post('/register', json={'user_id': 'user2', 'password': 'pass2'}) 39 | # Successful login 40 | response = self.client.post('/login', json={'user_id': 'user2', 'password': 'pass2'}) 41 | self.assertEqual(response.status_code, 200) 42 | token = response.get_json().get('token') 43 | self.assertIsNotNone(token) 44 | # Failed login 45 | response = self.client.post('/login', json={'user_id': 'user2', 'password': 'wrongpass'}) 46 | self.assertEqual(response.status_code, 401) 47 | 48 | def test_protected_endpoint(self): 49 | # Register and login to get a token 50 | self.client.post('/register', json={'user_id': 'user3', 'password': 'pass3'}) 51 | login_response = self.client.post('/login', json={'user_id': 'user3', 'password': 'pass3'}) 52 | token = login_response.get_json().get('token') 53 | # Access protected endpoint without token 54 | response = self.client.post('/submit_action', json={'agent_id': 'agent1', 'action': {}}) 55 | self.assertEqual(response.status_code, 401) 56 | # Access protected endpoint with token but without required permission 57 | # Revoke 'submit_action' permission 58 | self.authorization_manager.revoke_role('user3', 'agent') 59 | self.authorization_manager.assign_role('user3', 'limited_user') 60 | response = self.client.post('/submit_action', headers={'Authorization': token}, json={'agent_id': 'agent1', 'action': {}}) 61 | self.assertEqual(response.status_code, 403) 62 | # Assign correct role and try again 63 | self.authorization_manager.assign_role('user3', 'agent') 64 | response = self.client.post('/submit_action', headers={'Authorization': token}, json={'agent_id': 'agent1', 'action': {}}) 65 | self.assertEqual(response.status_code, 200) 66 | 67 | if __name__ == '__main__': 68 | unittest.main() 69 | -------------------------------------------------------------------------------- /ovadare/tests/test_authentication.py: -------------------------------------------------------------------------------- 1 | # tests/test_authentication.py 2 | 3 | import unittest 4 | from ovadare.security.authentication import AuthenticationManager 5 | import time 6 | 7 | class TestAuthenticationManager(unittest.TestCase): 8 | 9 | def setUp(self): 10 | self.auth_manager = AuthenticationManager() 11 | 12 | def test_register_user(self): 13 | # Test successful registration 14 | self.auth_manager.register_user('user1', 'password1') 15 | self.assertIn('user1', self.auth_manager._users) 16 | # Test registering an existing user 17 | with self.assertRaises(ValueError): 18 | self.auth_manager.register_user('user1', 'password1') 19 | 20 | def test_authenticate(self): 21 | # Register a user 22 | self.auth_manager.register_user('user2', 'password2') 23 | # Test successful authentication 24 | token = self.auth_manager.authenticate('user2', 'password2') 25 | self.assertIsNotNone(token) 26 | # Test authentication with incorrect password 27 | token = self.auth_manager.authenticate('user2', 'wrongpassword') 28 | self.assertIsNone(token) 29 | # Test authentication with non-existent user 30 | token = self.auth_manager.authenticate('nonexistent', 'password') 31 | self.assertIsNone(token) 32 | 33 | def test_validate_token(self): 34 | # Register and authenticate a user 35 | self.auth_manager.register_user('user3', 'password3') 36 | token = self.auth_manager.authenticate('user3', 'password3') 37 | # Test validating a valid token 38 | is_valid = self.auth_manager.validate_token(token) 39 | self.assertTrue(is_valid) 40 | # Test validating an invalid token 41 | is_valid = self.auth_manager.validate_token('invalidtoken') 42 | self.assertFalse(is_valid) 43 | 44 | def test_get_user_id_from_token(self): 45 | # Register and authenticate a user 46 | self.auth_manager.register_user('user4', 'password4') 47 | token = self.auth_manager.authenticate('user4', 'password4') 48 | # Test retrieving user ID from valid token 49 | user_id = self.auth_manager.get_user_id_from_token(token) 50 | self.assertEqual(user_id, 'user4') 51 | # Test retrieving user ID from invalid token 52 | user_id = self.auth_manager.get_user_id_from_token('invalidtoken') 53 | self.assertIsNone(user_id) 54 | 55 | def test_token_expiration(self): 56 | # Register and authenticate a user with short token expiration 57 | self.auth_manager = AuthenticationManager(token_expiry_duration=1) 58 | self.auth_manager.register_user('user5', 'password5') 59 | token = self.auth_manager.authenticate('user5', 'password5') 60 | # Token should be valid immediately after authentication 61 | is_valid = self.auth_manager.validate_token(token) 62 | self.assertTrue(is_valid) 63 | # Wait for token to expire 64 | time.sleep(2) 65 | is_valid = self.auth_manager.validate_token(token) 66 | self.assertFalse(is_valid) 67 | 68 | if __name__ == '__main__': 69 | unittest.main() 70 | -------------------------------------------------------------------------------- /ovadare/tests/test_authorization.py: -------------------------------------------------------------------------------- 1 | # tests/test_authorization.py 2 | 3 | import unittest 4 | from ovadare.security.authorization import AuthorizationManager 5 | 6 | class TestAuthorizationManager(unittest.TestCase): 7 | 8 | def setUp(self): 9 | self.authz_manager = AuthorizationManager() 10 | 11 | def test_assign_and_revoke_role(self): 12 | # Assign a role to a user 13 | self.authz_manager.assign_role('user1', 'agent') 14 | roles = self.authz_manager.get_user_roles('user1') 15 | self.assertIn('agent', roles) 16 | # Revoke the role 17 | self.authz_manager.revoke_role('user1', 'agent') 18 | roles = self.authz_manager.get_user_roles('user1') 19 | self.assertNotIn('agent', roles) 20 | # Attempt to revoke a non-assigned role 21 | with self.assertRaises(ValueError): 22 | self.authz_manager.revoke_role('user1', 'admin') 23 | 24 | def test_is_authorized(self): 25 | # Assign roles and test permissions 26 | self.authz_manager.assign_role('user2', 'agent') 27 | is_auth = self.authz_manager.is_authorized('user2', 'submit_action') 28 | self.assertTrue(is_auth) 29 | is_auth = self.authz_manager.is_authorized('user2', 'manage_agents') 30 | self.assertFalse(is_auth) 31 | 32 | def test_add_and_remove_role(self): 33 | # Add a new role 34 | self.authz_manager.add_role('supervisor', ['approve_actions', 'view_reports']) 35 | roles = self.authz_manager._roles_permissions 36 | self.assertIn('supervisor', roles) 37 | # Assign the new role to a user 38 | self.authz_manager.assign_role('user3', 'supervisor') 39 | is_auth = self.authz_manager.is_authorized('user3', 'approve_actions') 40 | self.assertTrue(is_auth) 41 | # Remove the role 42 | self.authz_manager.remove_role('supervisor') 43 | roles = self.authz_manager._roles_permissions 44 | self.assertNotIn('supervisor', roles) 45 | # Attempt to assign the removed role 46 | with self.assertRaises(ValueError): 47 | self.authz_manager.assign_role('user3', 'supervisor') 48 | 49 | if __name__ == '__main__': 50 | unittest.main() 51 | -------------------------------------------------------------------------------- /ovadare/tests/test_conflict_detector.py: -------------------------------------------------------------------------------- 1 | # tests/test_conflict_detector.py 2 | 3 | import unittest 4 | import os 5 | import json 6 | from ovadare.conflicts.conflict_detector import ConflictDetector 7 | from ovadare.policies.policy_manager import PolicyManager 8 | from ovadare.conflicts.conflict import Conflict 9 | 10 | class TestConflictDetector(unittest.TestCase): 11 | 12 | def setUp(self): 13 | # Set up a PolicyManager with a simple policy 14 | self.policy_manager = PolicyManager() 15 | self.policy_manager.policies = [ 16 | {'id': 'policy1', 'condition': lambda action: action.get('type') != 'forbidden', 'description': 'Action must not be forbidden'} 17 | ] 18 | # Set up a ConflictDetector 19 | self.conflict_detector = ConflictDetector(policy_manager=self.policy_manager) 20 | # Ensure a clean state 21 | self.conflict_detector.conflicts = [] 22 | self.conflict_detector.conflict_storage_file = 'test_conflicts_data.json' 23 | # Remove the test conflicts file if it exists 24 | if os.path.exists(self.conflict_detector.conflict_storage_file): 25 | os.remove(self.conflict_detector.conflict_storage_file) 26 | 27 | def tearDown(self): 28 | # Clean up the test conflicts file 29 | if os.path.exists(self.conflict_detector.conflict_storage_file): 30 | os.remove(self.conflict_detector.conflict_storage_file) 31 | 32 | def test_detect_conflict(self): 33 | # Action that violates the policy 34 | action = {'type': 'forbidden'} 35 | conflicts = self.conflict_detector.detect('agent1', action) 36 | self.assertEqual(len(conflicts), 1) 37 | self.assertEqual(conflicts[0].agent_id, 'agent1') 38 | # Action that complies with the policy 39 | action = {'type': 'allowed'} 40 | conflicts = self.conflict_detector.detect('agent1', action) 41 | self.assertEqual(len(conflicts), 0) 42 | 43 | def test_persistent_storage(self): 44 | # Detect a conflict to trigger saving 45 | action = {'type': 'forbidden'} 46 | self.conflict_detector.detect('agent2', action) 47 | # Load conflicts from storage 48 | self.conflict_detector.load_conflicts() 49 | self.assertEqual(len(self.conflict_detector.conflicts), 1) 50 | self.assertEqual(self.conflict_detector.conflicts[0].agent_id, 'agent2') 51 | 52 | if __name__ == '__main__': 53 | unittest.main() 54 | -------------------------------------------------------------------------------- /ovadare/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/ovadare/utils/__init__.py -------------------------------------------------------------------------------- /ovadare/utils/configuration.py: -------------------------------------------------------------------------------- 1 | # ovadare/utils/configuration.py 2 | 3 | """ 4 | Configuration Module for the Ovadare Framework 5 | 6 | This module provides the Configuration class, which manages configuration 7 | settings for the framework. It supports loading configurations from environment 8 | variables, JSON files, and default settings. 9 | """ 10 | 11 | import logging 12 | import os 13 | import json 14 | from typing import Any, Dict, Optional 15 | 16 | # Configure the logger for this module 17 | logger = logging.getLogger(__name__) 18 | logger.setLevel(logging.DEBUG) 19 | 20 | 21 | class Configuration: 22 | """ 23 | Manages configuration settings for the Ovadare Framework. 24 | """ 25 | _config: Dict[str, Any] = {} 26 | _default_config: Dict[str, Any] = { 27 | 'api_host': '0.0.0.0', 28 | 'api_port': 5000, 29 | 'api_base_url': 'http://localhost:5000', 30 | 'conflict_storage_file': 'conflicts_data.json', 31 | 'resolution_storage_file': 'resolutions_data.json', 32 | 'logging_level': 'INFO', 33 | 'config_file_path': 'config.json', 34 | } 35 | 36 | @classmethod 37 | def load(cls, config_file_path: Optional[str] = None) -> None: 38 | """ 39 | Loads configuration settings from a JSON file, environment variables, 40 | and merges them with default settings. 41 | 42 | Args: 43 | config_file_path (Optional[str]): Path to the configuration file. 44 | If None, uses the default path specified in the default configuration. 45 | """ 46 | # Start with default configuration 47 | cls._config = cls._default_config.copy() 48 | logger.debug("Default configuration loaded.") 49 | 50 | # Load configuration from file 51 | file_path = config_file_path or cls._config.get('config_file_path') 52 | if file_path and os.path.exists(file_path): 53 | try: 54 | with open(file_path, 'r', encoding='utf-8') as f: 55 | file_config = json.load(f) 56 | cls._config.update(file_config) 57 | logger.debug(f"Configuration loaded from file '{file_path}'.") 58 | except Exception as e: 59 | logger.error(f"Failed to load configuration file '{file_path}': {e}") 60 | else: 61 | logger.warning(f"Configuration file '{file_path}' not found. Using defaults and environment variables.") 62 | 63 | # Override with environment variables 64 | for key in cls._config.keys(): 65 | env_value = os.environ.get(key.upper()) 66 | if env_value is not None: 67 | cls._config[key] = env_value 68 | logger.debug(f"Configuration '{key}' set from environment variable.") 69 | 70 | @classmethod 71 | def get(cls, key: str, default: Any = None) -> Any: 72 | """ 73 | Retrieves a configuration value by key. 74 | 75 | Args: 76 | key (str): The configuration key. 77 | default (Any): The default value if the key is not found. 78 | 79 | Returns: 80 | Any: The configuration value. 81 | """ 82 | return cls._config.get(key, default) 83 | 84 | @classmethod 85 | def set(cls, key: str, value: Any) -> None: 86 | """ 87 | Sets a configuration value. 88 | 89 | Args: 90 | key (str): The configuration key. 91 | value (Any): The value to set. 92 | """ 93 | cls._config[key] = value 94 | logger.debug(f"Configuration '{key}' set to '{value}'.") 95 | 96 | @classmethod 97 | def get_all(cls) -> Dict[str, Any]: 98 | """ 99 | Retrieves all configuration settings. 100 | 101 | Returns: 102 | Dict[str, Any]: All configuration settings. 103 | """ 104 | return cls._config.copy() 105 | -------------------------------------------------------------------------------- /ovadare/utils/logging_config.py: -------------------------------------------------------------------------------- 1 | # ovadare/utils/logging_config.py 2 | 3 | """ 4 | Logging Configuration Module for the Ovadare Framework 5 | 6 | This module configures logging settings for the framework, including log 7 | formats, levels, handlers, and integration with the Configuration module. 8 | """ 9 | 10 | import logging 11 | import logging.config 12 | from ovadare.utils.configuration import Configuration 13 | 14 | def setup_logging() -> None: 15 | """ 16 | Sets up logging configuration based on settings from the Configuration module. 17 | """ 18 | logging_level = Configuration.get('logging_level', 'INFO').upper() 19 | log_format = Configuration.get('log_format', '%(asctime)s - %(name)s - %(levelname)s - %(message)s') 20 | log_datefmt = Configuration.get('log_datefmt', '%Y-%m-%d %H:%M:%S') 21 | log_file = Configuration.get('log_file', None) 22 | 23 | handlers = ['console'] 24 | if log_file: 25 | handlers.append('file') 26 | 27 | logging_config = { 28 | 'version': 1, 29 | 'disable_existing_loggers': False, 30 | 'formatters': { 31 | 'standard': { 32 | 'format': log_format, 33 | 'datefmt': log_datefmt 34 | }, 35 | }, 36 | 'handlers': { 37 | 'console': { 38 | 'class': 'logging.StreamHandler', 39 | 'formatter': 'standard', 40 | 'level': logging_level, 41 | 'stream': 'ext://sys.stdout' 42 | }, 43 | 'file': { 44 | 'class': 'logging.FileHandler', 45 | 'formatter': 'standard', 46 | 'level': logging_level, 47 | 'filename': log_file 48 | } 49 | }, 50 | 'root': { 51 | 'handlers': handlers, 52 | 'level': logging_level 53 | }, 54 | } 55 | 56 | logging.config.dictConfig(logging_config) 57 | logging.getLogger().info("Logging configuration has been set up.") 58 | 59 | 60 | # Initialize logging when the module is imported 61 | setup_logging() 62 | -------------------------------------------------------------------------------- /ovadare/utils/secrets_manager.py: -------------------------------------------------------------------------------- 1 | # ovadare/utils/secrets_manager.py 2 | 3 | """ 4 | Secrets Manager Module for the Ovadare Framework 5 | 6 | This module provides the SecretsManager class, which securely manages sensitive 7 | information like API keys, passwords, and encryption keys. It uses environment 8 | variables and, optionally, encrypted files to store secrets securely. 9 | """ 10 | 11 | import logging 12 | import os 13 | from typing import Optional 14 | from cryptography.fernet import Fernet 15 | 16 | # Configure the logger for this module 17 | logger = logging.getLogger(__name__) 18 | logger.setLevel(logging.DEBUG) 19 | 20 | 21 | class SecretsManager: 22 | """ 23 | Manages secrets securely using environment variables and encryption. 24 | """ 25 | 26 | def __init__(self, encryption_key: Optional[bytes] = None) -> None: 27 | """ 28 | Initializes the SecretsManager. 29 | 30 | Args: 31 | encryption_key (Optional[bytes]): The key used for encryption and decryption. 32 | If None, it attempts to load from the 'SECRETS_ENCRYPTION_KEY' environment variable. 33 | """ 34 | if encryption_key: 35 | self.encryption_key = encryption_key 36 | else: 37 | key = os.environ.get('SECRETS_ENCRYPTION_KEY') 38 | if not key: 39 | logger.error("Encryption key not found in environment variables.") 40 | raise ValueError("Encryption key is required.") 41 | self.encryption_key = key.encode('utf-8') 42 | self.cipher_suite = Fernet(self.encryption_key) 43 | logger.debug("SecretsManager initialized.") 44 | 45 | def get_secret(self, key: str) -> Optional[str]: 46 | """ 47 | Retrieves a secret value by its key. 48 | 49 | Args: 50 | key (str): The key of the secret. 51 | 52 | Returns: 53 | Optional[str]: The decrypted secret value, or None if not found. 54 | """ 55 | encrypted_value = os.environ.get(key) 56 | if encrypted_value: 57 | try: 58 | decrypted_value = self.cipher_suite.decrypt( 59 | encrypted_value.encode('utf-8') 60 | ).decode('utf-8') 61 | logger.debug(f"Secret '{key}' retrieved successfully.") 62 | return decrypted_value 63 | except Exception as e: 64 | logger.error(f"Failed to decrypt secret '{key}': {e}") 65 | return None 66 | else: 67 | logger.warning(f"Secret '{key}' not found.") 68 | return None 69 | 70 | def set_secret(self, key: str, value: str) -> None: 71 | """ 72 | Stores a secret value securely. 73 | 74 | Args: 75 | key (str): The key for the secret. 76 | value (str): The secret value to store. 77 | """ 78 | encrypted_value = self.cipher_suite.encrypt(value.encode('utf-8')).decode('utf-8') 79 | os.environ[key] = encrypted_value 80 | logger.debug(f"Secret '{key}' stored securely.") 81 | 82 | def generate_encryption_key(self) -> bytes: 83 | """ 84 | Generates a new encryption key. 85 | 86 | Returns: 87 | bytes: The generated encryption key. 88 | """ 89 | key = Fernet.generate_key() 90 | logger.info("New encryption key generated.") 91 | return key 92 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=42", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | testpaths = tests ovadare/tests 3 | python_files = test_*.py 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | autogen>=0.2.0 2 | sphinx>=5.3.0 3 | sphinx_rtd_theme>=1.2.0 4 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = ovadare 3 | version = 0.1.4 4 | author = Oren Mukades 5 | author_email = ai@ovadare.com 6 | description = A framework for conflict detection and resolution in multi-agent systems. 7 | long_description = file: README.md 8 | long_description_content_type = text/markdown 9 | url = https://github.com/nospecs/ovadare 10 | license = MIT 11 | classifiers = 12 | Programming Language :: Python :: 3 13 | License :: OSI Approved :: MIT License 14 | Operating System :: OS Independent 15 | 16 | [options] 17 | packages = find: 18 | install_requires = 19 | autogen>=0.2.0 20 | python_requires = >=3.7 21 | include_package_data = True 22 | 23 | [options.packages.find] 24 | include = 25 | ovadare 26 | ovadare.* 27 | 28 | [options.package_data] 29 | * = *.txt, *.md 30 | 31 | [options.extras_require] 32 | dev = 33 | sphinx>=5.3.0 34 | sphinx_rtd_theme>=1.2.0 35 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | if __name__ == "__main__": 4 | setup() 5 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_agents.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/tests/test_agents.py -------------------------------------------------------------------------------- /tests/test_communication.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/tests/test_communication.py -------------------------------------------------------------------------------- /tests/test_conflicts.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/tests/test_conflicts.py -------------------------------------------------------------------------------- /tests/test_core.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/tests/test_core.py -------------------------------------------------------------------------------- /tests/test_escalation.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/tests/test_escalation.py -------------------------------------------------------------------------------- /tests/test_feedback.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/tests/test_feedback.py -------------------------------------------------------------------------------- /tests/test_monitoring.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/tests/test_monitoring.py -------------------------------------------------------------------------------- /tests/test_policies.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/tests/test_policies.py -------------------------------------------------------------------------------- /tests/test_utils.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nospecs/ovadare/8cb0767a5f4f1388713490b49adf999ea26e4d7c/tests/test_utils.py --------------------------------------------------------------------------------