├── .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 | 
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 | [](https://github.com/nospecs/ovadare)
16 | [](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
--------------------------------------------------------------------------------