eggai 0.1.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
eggai-0.1.5/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 EggAI Technologies GmbH
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
eggai-0.1.5/PKG-INFO ADDED
@@ -0,0 +1,172 @@
1
+ Metadata-Version: 2.1
2
+ Name: eggai
3
+ Version: 0.1.5
4
+ Summary:
5
+ Author: Stefano Tucci
6
+ Author-email: stefanotucci89@gmail.com
7
+ Requires-Python: >=3.10,<4.0
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Requires-Dist: aiokafka (>=0.12.0,<0.13.0)
13
+ Description-Content-Type: text/markdown
14
+
15
+ # EggAI Multi-Agent Framework 🤖
16
+
17
+ [![Python 3.x](https://img.shields.io/badge/python-3.x-blue?style=for-the-badge&logo=python&logoColor=white)](https://www.python.org/downloads/)
18
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green?style=for-the-badge&logo=opensourceinitiative&logoColor=white)](https://opensource.org/licenses/MIT)
19
+ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen?style=for-the-badge&logo=github&logoColor=white)](https://github.com/eggai-tech/eggai/pulls)
20
+ [![GitHub Issues](https://img.shields.io/github/issues/eggai-tech/eggai?style=for-the-badge&logo=github&logoColor=white)](https://github.com/eggai-tech/eggai/issues)
21
+ [![GitHub Stars](https://img.shields.io/github/stars/eggai-tech/eggai?style=for-the-badge&logo=github&logoColor=white)](https://github.com/eggai-tech/eggai/stargazers)
22
+
23
+ `EggAI Multi-Agent Framework` is an async-first framework for building, deploying, and scaling multi-agent systems for modern enterprise environments.
24
+
25
+ ---
26
+
27
+ ## Table of Contents
28
+
29
+ [Overview](#overview) •
30
+ [Features](#features) •
31
+ [Installation](#installation) •
32
+ [Quick Start](#quick-start) •
33
+ [Core Concepts](#core-concepts) •
34
+ [Examples](#examples) •
35
+ [Development](#development) •
36
+ [License](#license)
37
+
38
+ ---
39
+
40
+ ## 🌟 Overview
41
+
42
+ `EggAI` is a Python library that simplifies the development of multi-agent systems by providing a high-level abstraction over Kafka. It allows developers to focus on business logic by handling the complexities of Kafka producers and consumers.
43
+
44
+ ---
45
+
46
+ ## ✨ Features
47
+
48
+ - **Event-Driven Architecture**: Build systems that react to events in real-time.
49
+ - **Agent Orchestration**: Manage multiple agents with ease.
50
+ - **Kafka Integration**: Seamlessly integrate with Kafka topics.
51
+ - **Async-First Design**: Utilize Python's async features for high performance.
52
+ - **Minimal Boilerplate**: Focus on business logic without worrying about Kafka details.
53
+
54
+ ---
55
+
56
+ ## Installation
57
+
58
+ Install `EggAI` via pip:
59
+
60
+ ```bash
61
+ pip install eggai
62
+ ```
63
+
64
+ ---
65
+
66
+ ## Quick Start
67
+
68
+ Here's how you can quickly set up an agent to handle events in an event-driven system:
69
+
70
+ ```python
71
+ import asyncio
72
+
73
+ from eggai.agent import Agent
74
+ from eggai.channel import Channel
75
+
76
+ CHANNEL_NAME = "events"
77
+
78
+ agent = Agent("OrderAgent")
79
+
80
+ @agent.subscribe(channel_name=CHANNEL_NAME, event_name="order_requested")
81
+ async def handle_order_requested(event):
82
+ print(f"[ORDER AGENT]: Received order request. Event: {event}")
83
+ await Channel(name=CHANNEL_NAME).publish({"event_name": "order_created", "payload": event})
84
+
85
+
86
+ @agent.subscribe(channel_name=CHANNEL_NAME, event_name="order_created")
87
+ async def handle_order_created(event):
88
+ print(f"[ORDER AGENT]: Order created. Event: {event}")
89
+
90
+
91
+ async def main():
92
+ task = agent.start()
93
+ await asyncio.sleep(2)
94
+
95
+ await Channel(CHANNEL_NAME).publish({
96
+ "event_name": "order_requested",
97
+ "payload": {
98
+ "product": "Laptop",
99
+ "quantity": 1
100
+ }
101
+ })
102
+
103
+ await asyncio.sleep(2)
104
+ task.cancel()
105
+ await Channel.stop()
106
+
107
+
108
+ if __name__ == "__main__":
109
+ try:
110
+ asyncio.run(main())
111
+ except KeyboardInterrupt:
112
+ print("Shutting down...")
113
+
114
+ ```
115
+
116
+ This code demonstrates how to define an `Agent` and use it to process events from Kafka topics.
117
+
118
+ This repository contains a few applications you can use as a reference:
119
+
120
+ ---
121
+
122
+ ## Core Concepts
123
+
124
+ ### Agent
125
+
126
+ An `Agent` is responsible for subscribing to Kafka topics, processing events, and orchestrating tasks using user-defined handlers. The key features of the `Agent` class include:
127
+
128
+ - **Event Subscription**: Bind event handlers to specific events using the `subscribe` decorator.
129
+ - **Lifecycle Management**: Manage producer and consumer lifecycles seamlessly.
130
+ - **Minimal Boilerplate**: Focus on business logic without worrying about Kafka details.
131
+
132
+ ### Channel
133
+
134
+ A `Channel` abstracts the Kafka producer interface to publish events to specific Kafka topics. Key features include:
135
+
136
+ - **Event Publishing**: Send events to Kafka topics easily.
137
+ - **Singleton Producers**: Manage Kafka producers efficiently across multiple channels.
138
+
139
+ ---
140
+
141
+ ## Examples
142
+
143
+ For detailed examples, please refer to [examples](examples).
144
+
145
+ ---
146
+
147
+ ## Development
148
+
149
+ ### Setting Up
150
+
151
+ Clone the repository and install the development dependencies:
152
+
153
+ ```bash
154
+ git clone https://github.com/eggai-tech/eggai.git
155
+ cd eggai
156
+ pip install -r requirements.txt
157
+ ```
158
+
159
+ ### Running Tests
160
+
161
+ Run the tests using:
162
+
163
+ ```bash
164
+ pytest
165
+ ```
166
+
167
+ ---
168
+
169
+ ## License
170
+
171
+ This project is licensed under the MIT License. See the [LICENSE.md](LICENSE.md) file for details.
172
+
eggai-0.1.5/README.md ADDED
@@ -0,0 +1,157 @@
1
+ # EggAI Multi-Agent Framework 🤖
2
+
3
+ [![Python 3.x](https://img.shields.io/badge/python-3.x-blue?style=for-the-badge&logo=python&logoColor=white)](https://www.python.org/downloads/)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green?style=for-the-badge&logo=opensourceinitiative&logoColor=white)](https://opensource.org/licenses/MIT)
5
+ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen?style=for-the-badge&logo=github&logoColor=white)](https://github.com/eggai-tech/eggai/pulls)
6
+ [![GitHub Issues](https://img.shields.io/github/issues/eggai-tech/eggai?style=for-the-badge&logo=github&logoColor=white)](https://github.com/eggai-tech/eggai/issues)
7
+ [![GitHub Stars](https://img.shields.io/github/stars/eggai-tech/eggai?style=for-the-badge&logo=github&logoColor=white)](https://github.com/eggai-tech/eggai/stargazers)
8
+
9
+ `EggAI Multi-Agent Framework` is an async-first framework for building, deploying, and scaling multi-agent systems for modern enterprise environments.
10
+
11
+ ---
12
+
13
+ ## Table of Contents
14
+
15
+ [Overview](#overview) •
16
+ [Features](#features) •
17
+ [Installation](#installation) •
18
+ [Quick Start](#quick-start) •
19
+ [Core Concepts](#core-concepts) •
20
+ [Examples](#examples) •
21
+ [Development](#development) •
22
+ [License](#license)
23
+
24
+ ---
25
+
26
+ ## 🌟 Overview
27
+
28
+ `EggAI` is a Python library that simplifies the development of multi-agent systems by providing a high-level abstraction over Kafka. It allows developers to focus on business logic by handling the complexities of Kafka producers and consumers.
29
+
30
+ ---
31
+
32
+ ## ✨ Features
33
+
34
+ - **Event-Driven Architecture**: Build systems that react to events in real-time.
35
+ - **Agent Orchestration**: Manage multiple agents with ease.
36
+ - **Kafka Integration**: Seamlessly integrate with Kafka topics.
37
+ - **Async-First Design**: Utilize Python's async features for high performance.
38
+ - **Minimal Boilerplate**: Focus on business logic without worrying about Kafka details.
39
+
40
+ ---
41
+
42
+ ## Installation
43
+
44
+ Install `EggAI` via pip:
45
+
46
+ ```bash
47
+ pip install eggai
48
+ ```
49
+
50
+ ---
51
+
52
+ ## Quick Start
53
+
54
+ Here's how you can quickly set up an agent to handle events in an event-driven system:
55
+
56
+ ```python
57
+ import asyncio
58
+
59
+ from eggai.agent import Agent
60
+ from eggai.channel import Channel
61
+
62
+ CHANNEL_NAME = "events"
63
+
64
+ agent = Agent("OrderAgent")
65
+
66
+ @agent.subscribe(channel_name=CHANNEL_NAME, event_name="order_requested")
67
+ async def handle_order_requested(event):
68
+ print(f"[ORDER AGENT]: Received order request. Event: {event}")
69
+ await Channel(name=CHANNEL_NAME).publish({"event_name": "order_created", "payload": event})
70
+
71
+
72
+ @agent.subscribe(channel_name=CHANNEL_NAME, event_name="order_created")
73
+ async def handle_order_created(event):
74
+ print(f"[ORDER AGENT]: Order created. Event: {event}")
75
+
76
+
77
+ async def main():
78
+ task = agent.start()
79
+ await asyncio.sleep(2)
80
+
81
+ await Channel(CHANNEL_NAME).publish({
82
+ "event_name": "order_requested",
83
+ "payload": {
84
+ "product": "Laptop",
85
+ "quantity": 1
86
+ }
87
+ })
88
+
89
+ await asyncio.sleep(2)
90
+ task.cancel()
91
+ await Channel.stop()
92
+
93
+
94
+ if __name__ == "__main__":
95
+ try:
96
+ asyncio.run(main())
97
+ except KeyboardInterrupt:
98
+ print("Shutting down...")
99
+
100
+ ```
101
+
102
+ This code demonstrates how to define an `Agent` and use it to process events from Kafka topics.
103
+
104
+ This repository contains a few applications you can use as a reference:
105
+
106
+ ---
107
+
108
+ ## Core Concepts
109
+
110
+ ### Agent
111
+
112
+ An `Agent` is responsible for subscribing to Kafka topics, processing events, and orchestrating tasks using user-defined handlers. The key features of the `Agent` class include:
113
+
114
+ - **Event Subscription**: Bind event handlers to specific events using the `subscribe` decorator.
115
+ - **Lifecycle Management**: Manage producer and consumer lifecycles seamlessly.
116
+ - **Minimal Boilerplate**: Focus on business logic without worrying about Kafka details.
117
+
118
+ ### Channel
119
+
120
+ A `Channel` abstracts the Kafka producer interface to publish events to specific Kafka topics. Key features include:
121
+
122
+ - **Event Publishing**: Send events to Kafka topics easily.
123
+ - **Singleton Producers**: Manage Kafka producers efficiently across multiple channels.
124
+
125
+ ---
126
+
127
+ ## Examples
128
+
129
+ For detailed examples, please refer to [examples](examples).
130
+
131
+ ---
132
+
133
+ ## Development
134
+
135
+ ### Setting Up
136
+
137
+ Clone the repository and install the development dependencies:
138
+
139
+ ```bash
140
+ git clone https://github.com/eggai-tech/eggai.git
141
+ cd eggai
142
+ pip install -r requirements.txt
143
+ ```
144
+
145
+ ### Running Tests
146
+
147
+ Run the tests using:
148
+
149
+ ```bash
150
+ pytest
151
+ ```
152
+
153
+ ---
154
+
155
+ ## License
156
+
157
+ This project is licensed under the MIT License. See the [LICENSE.md](LICENSE.md) file for details.
File without changes
@@ -0,0 +1,129 @@
1
+ import asyncio
2
+ import json
3
+ from typing import Dict, List, Callable
4
+
5
+ from aiokafka import AIOKafkaProducer, AIOKafkaConsumer
6
+
7
+ from eggai.constants import DEFAULT_CHANNEL_NAME
8
+ from eggai.settings.kafka import KafkaSettings
9
+
10
+ class Agent:
11
+ """
12
+ A message-based agent for subscribing to events and handling messages with user-defined functions.
13
+ """
14
+
15
+ def __init__(self, name: str):
16
+ """
17
+ Initialize the Agent.
18
+
19
+ Args:
20
+ name (str): The name of the agent.
21
+ """
22
+ self.name = name
23
+ self.kafka_settings = KafkaSettings()
24
+ self.producer = None
25
+ self.consumer = None
26
+ self.handlers: Dict[str, List[Callable]] = {} # Maps channel:event_name to list of handlers
27
+ self.channels = set() # Keep track of all subscribed channels
28
+ self.running_task = None # Task to manage the lifecycle of the agent
29
+
30
+ def subscribe(self, event_name: str, channel_name: str = DEFAULT_CHANNEL_NAME):
31
+ """
32
+ Decorator for subscribing to a channel and binding a handler to an event name.
33
+
34
+ Args:
35
+ event_name (str): Name of the event within the channel.
36
+ channel_name (str): The name of the channel.
37
+
38
+ Returns:
39
+ Callable: The decorator for wrapping the handler function.
40
+ """
41
+ def decorator(func: Callable):
42
+ topic_event = f"{channel_name}:{event_name}"
43
+ if topic_event not in self.handlers:
44
+ self.handlers[topic_event] = []
45
+ self.handlers[topic_event].append(func) # Allow multiple handlers for the same event
46
+ self.channels.add(channel_name) # Register the channel name
47
+ return func
48
+ return decorator
49
+
50
+ async def _start_producer(self):
51
+ """
52
+ Start the message producer.
53
+ """
54
+ self.producer = AIOKafkaProducer(
55
+ bootstrap_servers=self.kafka_settings.BOOTSTRAP_SERVERS
56
+ )
57
+ await self.producer.start()
58
+
59
+ async def _start_consumer(self):
60
+ """
61
+ Start the message consumer and process incoming messages.
62
+ """
63
+ self.consumer = AIOKafkaConsumer(
64
+ *self.channels, # Use the registered channels
65
+ bootstrap_servers=self.kafka_settings.BOOTSTRAP_SERVERS,
66
+ #group_id=f"{self.name}_group",
67
+ auto_offset_reset="latest",
68
+ )
69
+ await self.consumer.start()
70
+
71
+ print(f"Agent '{self.name}' is ready to receive messages. Channels: {self.channels}")
72
+
73
+ try:
74
+ async for msg in self.consumer:
75
+ channel_name = msg.topic
76
+ try:
77
+ message = json.loads(msg.value.decode("utf-8"))
78
+ event_name = message.get("event_name")
79
+ payload = message.get("payload")
80
+
81
+ topic_event = f"{channel_name}:{event_name}"
82
+ if topic_event in self.handlers:
83
+ # Call all handlers for this event
84
+ for handler in self.handlers[topic_event]:
85
+ await handler(payload)
86
+ except Exception as e:
87
+ print(f"Failed to process message from {channel_name}: {e}")
88
+ except asyncio.CancelledError:
89
+ pass
90
+ finally:
91
+ print(f"Stopping agent '{self.name}'...")
92
+ await self.consumer.stop()
93
+ print(f"Agent '{self.name}' stopped.")
94
+
95
+ async def _lifecycle(self):
96
+ """
97
+ Internal lifecycle management for the agent.
98
+ """
99
+ try:
100
+ await self._start_producer()
101
+ await self._start_consumer()
102
+ finally:
103
+ await self.producer.stop()
104
+
105
+ async def run(self):
106
+ """
107
+ Run the agent lifecycle, blocking until it is stopped.
108
+ """
109
+ if self.running_task:
110
+ raise RuntimeError("Agent is already running.")
111
+ try:
112
+ self.running_task = asyncio.create_task(self._lifecycle())
113
+ await self.running_task # Block until the lifecycle completes
114
+ except asyncio.CancelledError:
115
+ print("Agent lifecycle was cancelled.")
116
+ finally:
117
+ self.running_task = None
118
+
119
+ async def stop(self):
120
+ """
121
+ Stop the agent gracefully by cancelling the running task.
122
+ """
123
+ if not self.running_task:
124
+ raise RuntimeError("Agent is not running.")
125
+ self.running_task.cancel()
126
+ try:
127
+ await self.running_task # Ensure the task finishes
128
+ except asyncio.CancelledError:
129
+ pass # Task was cancelled
@@ -0,0 +1,58 @@
1
+ import asyncio
2
+ import json
3
+ from aiokafka import AIOKafkaProducer
4
+
5
+ from eggai.constants import DEFAULT_CHANNEL_NAME
6
+ from eggai.settings.kafka import KafkaSettings
7
+
8
+
9
+ class Channel:
10
+ """
11
+ A standalone class to send events to Kafka channels.
12
+ """
13
+
14
+ _producers = {} # Singleton dictionary to hold producers for each channel
15
+
16
+ def __init__(self, name: str = DEFAULT_CHANNEL_NAME):
17
+ """
18
+ Initialize the Channel.
19
+
20
+ Args:
21
+ name (str): The name of the Kafka channel (topic).
22
+ """
23
+ self.name = name
24
+ self.kafka_settings = KafkaSettings()
25
+
26
+ async def _get_producer(self):
27
+ """
28
+ Get or create a Kafka producer for the channel.
29
+
30
+ Returns:
31
+ AIOKafkaProducer: Kafka producer for the channel.
32
+ """
33
+ if self.name not in Channel._producers:
34
+ producer = AIOKafkaProducer(
35
+ bootstrap_servers=self.kafka_settings.BOOTSTRAP_SERVERS,
36
+ )
37
+ await producer.start()
38
+ Channel._producers[self.name] = producer
39
+ return Channel._producers[self.name]
40
+
41
+ async def publish(self, event: dict):
42
+ """
43
+ Publish an event to the Kafka channel.
44
+
45
+ Args:
46
+ event (dict): The event payload.
47
+ """
48
+ producer = await self._get_producer()
49
+ await producer.send_and_wait(self.name, json.dumps(event).encode("utf-8"))
50
+
51
+ @staticmethod
52
+ async def stop():
53
+ """
54
+ Close all Kafka producers in the singleton list.
55
+ """
56
+ for producer in Channel._producers.values():
57
+ await producer.stop()
58
+ Channel._producers.clear()
@@ -0,0 +1 @@
1
+ DEFAULT_CHANNEL_NAME = "eggai.events"
@@ -0,0 +1,9 @@
1
+ import os
2
+ from dataclasses import dataclass
3
+
4
+
5
+ @dataclass
6
+ class KafkaSettings:
7
+ BOOTSTRAP_SERVERS: str = os.getenv("KAFKA_BOOTSTRAP_SERVERS", "localhost:19092")
8
+ USE_SSL: bool = os.getenv("KAFKA_USE_SSL", False)
9
+ CA_CONTENT: str | None = os.getenv("KAFKA_CA_CONTENT", None)
@@ -0,0 +1,10 @@
1
+ import ssl
2
+ from aiokafka.helpers import create_ssl_context
3
+
4
+ def create_ssl_context_from_settings(ca_content: str) -> ssl.SSLContext:
5
+ context = create_ssl_context()
6
+ context.check_hostname = False
7
+ context.verify_mode = ssl.CERT_NONE
8
+
9
+ context.load_verify_locations(cadata=ca_content)
10
+ return context
@@ -0,0 +1,18 @@
1
+ [tool.poetry]
2
+ name = "eggai"
3
+ version = "0.1.5"
4
+ description = ""
5
+ authors = ["Stefano Tucci <stefanotucci89@gmail.com>"]
6
+ readme = "README.md"
7
+
8
+ [tool.poetry.dependencies]
9
+ python = "^3.10"
10
+ aiokafka = "^0.12.0"
11
+
12
+
13
+ [tool.poetry.group.dev.dependencies]
14
+ twine = "^6.0.1"
15
+
16
+ [build-system]
17
+ requires = ["poetry-core"]
18
+ build-backend = "poetry.core.masonry.api"