pygeai-orchestration 0.1.0b2__py3-none-any.whl
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.
- pygeai_orchestration/__init__.py +99 -0
- pygeai_orchestration/cli/__init__.py +7 -0
- pygeai_orchestration/cli/__main__.py +11 -0
- pygeai_orchestration/cli/commands/__init__.py +13 -0
- pygeai_orchestration/cli/commands/base.py +192 -0
- pygeai_orchestration/cli/error_handler.py +123 -0
- pygeai_orchestration/cli/formatters.py +419 -0
- pygeai_orchestration/cli/geai_orch.py +270 -0
- pygeai_orchestration/cli/interactive.py +265 -0
- pygeai_orchestration/cli/texts/help.py +169 -0
- pygeai_orchestration/core/__init__.py +130 -0
- pygeai_orchestration/core/base/__init__.py +23 -0
- pygeai_orchestration/core/base/agent.py +121 -0
- pygeai_orchestration/core/base/geai_agent.py +144 -0
- pygeai_orchestration/core/base/geai_orchestrator.py +77 -0
- pygeai_orchestration/core/base/orchestrator.py +142 -0
- pygeai_orchestration/core/base/pattern.py +161 -0
- pygeai_orchestration/core/base/tool.py +149 -0
- pygeai_orchestration/core/common/__init__.py +18 -0
- pygeai_orchestration/core/common/context.py +140 -0
- pygeai_orchestration/core/common/memory.py +176 -0
- pygeai_orchestration/core/common/message.py +50 -0
- pygeai_orchestration/core/common/state.py +181 -0
- pygeai_orchestration/core/composition.py +190 -0
- pygeai_orchestration/core/config.py +356 -0
- pygeai_orchestration/core/exceptions.py +400 -0
- pygeai_orchestration/core/handlers.py +380 -0
- pygeai_orchestration/core/utils/__init__.py +37 -0
- pygeai_orchestration/core/utils/cache.py +138 -0
- pygeai_orchestration/core/utils/config.py +94 -0
- pygeai_orchestration/core/utils/logging.py +57 -0
- pygeai_orchestration/core/utils/metrics.py +184 -0
- pygeai_orchestration/core/utils/validators.py +140 -0
- pygeai_orchestration/dev/__init__.py +15 -0
- pygeai_orchestration/dev/debug.py +288 -0
- pygeai_orchestration/dev/templates.py +321 -0
- pygeai_orchestration/dev/testing.py +301 -0
- pygeai_orchestration/patterns/__init__.py +15 -0
- pygeai_orchestration/patterns/multi_agent.py +237 -0
- pygeai_orchestration/patterns/planning.py +219 -0
- pygeai_orchestration/patterns/react.py +221 -0
- pygeai_orchestration/patterns/reflection.py +134 -0
- pygeai_orchestration/patterns/tool_use.py +170 -0
- pygeai_orchestration/tests/__init__.py +1 -0
- pygeai_orchestration/tests/test_base_classes.py +187 -0
- pygeai_orchestration/tests/test_cache.py +184 -0
- pygeai_orchestration/tests/test_cli_formatters.py +232 -0
- pygeai_orchestration/tests/test_common.py +214 -0
- pygeai_orchestration/tests/test_composition.py +265 -0
- pygeai_orchestration/tests/test_config.py +301 -0
- pygeai_orchestration/tests/test_dev_utils.py +337 -0
- pygeai_orchestration/tests/test_exceptions.py +327 -0
- pygeai_orchestration/tests/test_handlers.py +307 -0
- pygeai_orchestration/tests/test_metrics.py +171 -0
- pygeai_orchestration/tests/test_patterns.py +165 -0
- pygeai_orchestration-0.1.0b2.dist-info/METADATA +290 -0
- pygeai_orchestration-0.1.0b2.dist-info/RECORD +61 -0
- pygeai_orchestration-0.1.0b2.dist-info/WHEEL +5 -0
- pygeai_orchestration-0.1.0b2.dist-info/entry_points.txt +2 -0
- pygeai_orchestration-0.1.0b2.dist-info/licenses/LICENSE +8 -0
- pygeai_orchestration-0.1.0b2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import unittest
|
|
3
|
+
|
|
4
|
+
from pygeai_orchestration.core.utils.metrics import (
|
|
5
|
+
GlobalMetrics,
|
|
6
|
+
Metric,
|
|
7
|
+
MetricsCollector,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TestMetricsCollector(unittest.TestCase):
|
|
12
|
+
def setUp(self):
|
|
13
|
+
self.collector = MetricsCollector()
|
|
14
|
+
|
|
15
|
+
def test_increment_counter(self):
|
|
16
|
+
self.collector.increment("requests")
|
|
17
|
+
self.assertEqual(self.collector.get_counter("requests"), 1.0)
|
|
18
|
+
|
|
19
|
+
self.collector.increment("requests", 2.0)
|
|
20
|
+
self.assertEqual(self.collector.get_counter("requests"), 3.0)
|
|
21
|
+
|
|
22
|
+
def test_counter_with_labels(self):
|
|
23
|
+
self.collector.increment("requests", labels={"method": "GET"})
|
|
24
|
+
self.collector.increment("requests", labels={"method": "POST"})
|
|
25
|
+
self.collector.increment("requests", labels={"method": "GET"})
|
|
26
|
+
|
|
27
|
+
self.assertEqual(
|
|
28
|
+
self.collector.get_counter("requests", labels={"method": "GET"}), 2.0
|
|
29
|
+
)
|
|
30
|
+
self.assertEqual(
|
|
31
|
+
self.collector.get_counter("requests", labels={"method": "POST"}), 1.0
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
def test_set_gauge(self):
|
|
35
|
+
self.collector.set_gauge("memory_usage", 1024.0)
|
|
36
|
+
self.assertEqual(self.collector.get_gauge("memory_usage"), 1024.0)
|
|
37
|
+
|
|
38
|
+
self.collector.set_gauge("memory_usage", 2048.0)
|
|
39
|
+
self.assertEqual(self.collector.get_gauge("memory_usage"), 2048.0)
|
|
40
|
+
|
|
41
|
+
def test_gauge_with_labels(self):
|
|
42
|
+
self.collector.set_gauge("cpu_usage", 50.0, labels={"core": "0"})
|
|
43
|
+
self.collector.set_gauge("cpu_usage", 75.0, labels={"core": "1"})
|
|
44
|
+
|
|
45
|
+
self.assertEqual(
|
|
46
|
+
self.collector.get_gauge("cpu_usage", labels={"core": "0"}), 50.0
|
|
47
|
+
)
|
|
48
|
+
self.assertEqual(
|
|
49
|
+
self.collector.get_gauge("cpu_usage", labels={"core": "1"}), 75.0
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
def test_record_histogram(self):
|
|
53
|
+
values = [10.0, 20.0, 30.0, 40.0, 50.0]
|
|
54
|
+
for value in values:
|
|
55
|
+
self.collector.record_histogram("response_size", value)
|
|
56
|
+
|
|
57
|
+
stats = self.collector.get_histogram_stats("response_size")
|
|
58
|
+
self.assertEqual(stats["count"], 5)
|
|
59
|
+
self.assertEqual(stats["sum"], 150.0)
|
|
60
|
+
self.assertEqual(stats["min"], 10.0)
|
|
61
|
+
self.assertEqual(stats["max"], 50.0)
|
|
62
|
+
self.assertEqual(stats["mean"], 30.0)
|
|
63
|
+
|
|
64
|
+
def test_record_timer(self):
|
|
65
|
+
durations = [0.1, 0.2, 0.3, 0.4, 0.5]
|
|
66
|
+
for duration in durations:
|
|
67
|
+
self.collector.record_timer("request_duration", duration)
|
|
68
|
+
|
|
69
|
+
stats = self.collector.get_timer_stats("request_duration")
|
|
70
|
+
self.assertEqual(stats["count"], 5)
|
|
71
|
+
self.assertAlmostEqual(stats["sum"], 1.5, places=1)
|
|
72
|
+
self.assertEqual(stats["min"], 0.1)
|
|
73
|
+
self.assertEqual(stats["max"], 0.5)
|
|
74
|
+
|
|
75
|
+
def test_timer_context(self):
|
|
76
|
+
with self.collector.timer("operation_duration"):
|
|
77
|
+
time.sleep(0.01)
|
|
78
|
+
|
|
79
|
+
stats = self.collector.get_timer_stats("operation_duration")
|
|
80
|
+
self.assertEqual(stats["count"], 1)
|
|
81
|
+
self.assertGreater(stats["min"], 0.0)
|
|
82
|
+
|
|
83
|
+
def test_timer_context_with_labels(self):
|
|
84
|
+
with self.collector.timer("db_query", labels={"table": "users"}):
|
|
85
|
+
time.sleep(0.01)
|
|
86
|
+
|
|
87
|
+
stats = self.collector.get_timer_stats("db_query", labels={"table": "users"})
|
|
88
|
+
self.assertEqual(stats["count"], 1)
|
|
89
|
+
|
|
90
|
+
def test_percentiles(self):
|
|
91
|
+
values = list(range(1, 101))
|
|
92
|
+
for value in values:
|
|
93
|
+
self.collector.record_histogram("test_percentiles", float(value))
|
|
94
|
+
|
|
95
|
+
stats = self.collector.get_histogram_stats("test_percentiles")
|
|
96
|
+
self.assertAlmostEqual(stats["p50"], 50.0, delta=5.0)
|
|
97
|
+
self.assertAlmostEqual(stats["p95"], 95.0, delta=5.0)
|
|
98
|
+
self.assertAlmostEqual(stats["p99"], 99.0, delta=5.0)
|
|
99
|
+
|
|
100
|
+
def test_get_all_metrics(self):
|
|
101
|
+
self.collector.increment("counter1")
|
|
102
|
+
self.collector.set_gauge("gauge1", 100.0)
|
|
103
|
+
self.collector.record_histogram("histogram1", 50.0)
|
|
104
|
+
|
|
105
|
+
metrics = self.collector.get_all_metrics()
|
|
106
|
+
self.assertGreaterEqual(len(metrics), 3)
|
|
107
|
+
self.assertTrue(all(isinstance(m, Metric) for m in metrics))
|
|
108
|
+
|
|
109
|
+
def test_get_summary(self):
|
|
110
|
+
self.collector.increment("requests")
|
|
111
|
+
self.collector.set_gauge("memory", 1024.0)
|
|
112
|
+
self.collector.record_histogram("sizes", 100.0)
|
|
113
|
+
self.collector.record_timer("durations", 0.5)
|
|
114
|
+
|
|
115
|
+
summary = self.collector.get_summary()
|
|
116
|
+
self.assertIn("counters", summary)
|
|
117
|
+
self.assertIn("gauges", summary)
|
|
118
|
+
self.assertIn("histograms", summary)
|
|
119
|
+
self.assertIn("timers", summary)
|
|
120
|
+
|
|
121
|
+
def test_clear(self):
|
|
122
|
+
self.collector.increment("counter")
|
|
123
|
+
self.collector.set_gauge("gauge", 100.0)
|
|
124
|
+
self.collector.record_histogram("histogram", 50.0)
|
|
125
|
+
|
|
126
|
+
self.collector.clear()
|
|
127
|
+
|
|
128
|
+
self.assertEqual(self.collector.get_counter("counter"), 0.0)
|
|
129
|
+
self.assertIsNone(self.collector.get_gauge("gauge"))
|
|
130
|
+
self.assertEqual(len(self.collector.get_all_metrics()), 0)
|
|
131
|
+
|
|
132
|
+
def test_empty_histogram_stats(self):
|
|
133
|
+
stats = self.collector.get_histogram_stats("nonexistent")
|
|
134
|
+
self.assertEqual(stats, {})
|
|
135
|
+
|
|
136
|
+
def test_empty_timer_stats(self):
|
|
137
|
+
stats = self.collector.get_timer_stats("nonexistent")
|
|
138
|
+
self.assertEqual(stats, {})
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class TestGlobalMetrics(unittest.TestCase):
|
|
142
|
+
def setUp(self):
|
|
143
|
+
GlobalMetrics.reset()
|
|
144
|
+
|
|
145
|
+
def tearDown(self):
|
|
146
|
+
GlobalMetrics.reset()
|
|
147
|
+
|
|
148
|
+
def test_singleton(self):
|
|
149
|
+
collector1 = GlobalMetrics.get_collector()
|
|
150
|
+
collector2 = GlobalMetrics.get_collector()
|
|
151
|
+
self.assertIs(collector1, collector2)
|
|
152
|
+
|
|
153
|
+
def test_global_metrics_persistence(self):
|
|
154
|
+
collector1 = GlobalMetrics.get_collector()
|
|
155
|
+
collector1.increment("global_counter")
|
|
156
|
+
|
|
157
|
+
collector2 = GlobalMetrics.get_collector()
|
|
158
|
+
self.assertEqual(collector2.get_counter("global_counter"), 1.0)
|
|
159
|
+
|
|
160
|
+
def test_reset(self):
|
|
161
|
+
collector1 = GlobalMetrics.get_collector()
|
|
162
|
+
collector1.increment("counter")
|
|
163
|
+
|
|
164
|
+
GlobalMetrics.reset()
|
|
165
|
+
|
|
166
|
+
collector2 = GlobalMetrics.get_collector()
|
|
167
|
+
self.assertEqual(collector2.get_counter("counter"), 0.0)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
if __name__ == "__main__":
|
|
171
|
+
unittest.main()
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
import asyncio
|
|
3
|
+
from pygeai_orchestration import (
|
|
4
|
+
ReflectionPattern,
|
|
5
|
+
ToolUsePattern,
|
|
6
|
+
ReActPattern,
|
|
7
|
+
PlanningPattern,
|
|
8
|
+
MultiAgentPattern,
|
|
9
|
+
AgentRole,
|
|
10
|
+
BaseTool,
|
|
11
|
+
ToolConfig,
|
|
12
|
+
ToolResult,
|
|
13
|
+
ToolCategory,
|
|
14
|
+
PatternType
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class MockAgent:
|
|
19
|
+
"""Mock agent for testing."""
|
|
20
|
+
|
|
21
|
+
async def generate(self, prompt: str) -> str:
|
|
22
|
+
if "Review your previous response" in prompt:
|
|
23
|
+
return "Improved response"
|
|
24
|
+
elif "TOOL_CALL" in prompt or "FINAL_ANSWER" in prompt:
|
|
25
|
+
return "FINAL_ANSWER: Test answer"
|
|
26
|
+
elif "Thought:" in prompt or "Action:" in prompt:
|
|
27
|
+
return "Thought: Test\nAction: ANSWER Test result\nObservation: Done"
|
|
28
|
+
elif "step-by-step plan" in prompt:
|
|
29
|
+
return "1. First step\n2. Second step"
|
|
30
|
+
elif "Execute the following step" in prompt:
|
|
31
|
+
return "Step completed"
|
|
32
|
+
elif "synthesized answer" in prompt or "Synthesize" in prompt:
|
|
33
|
+
return "Final synthesized result"
|
|
34
|
+
elif "coordination plan" in prompt:
|
|
35
|
+
return "Coordination plan created"
|
|
36
|
+
return "Test response"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class MockTool(BaseTool):
|
|
40
|
+
"""Mock tool for testing."""
|
|
41
|
+
|
|
42
|
+
def __init__(self):
|
|
43
|
+
config = ToolConfig(
|
|
44
|
+
name="mock_tool",
|
|
45
|
+
description="Mock tool",
|
|
46
|
+
category=ToolCategory.CUSTOM
|
|
47
|
+
)
|
|
48
|
+
super().__init__(config)
|
|
49
|
+
|
|
50
|
+
async def execute(self, **kwargs) -> ToolResult:
|
|
51
|
+
return ToolResult(success=True, result="Tool result")
|
|
52
|
+
|
|
53
|
+
def validate_parameters(self, parameters):
|
|
54
|
+
return True
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class TestReflectionPattern(unittest.TestCase):
|
|
58
|
+
|
|
59
|
+
def test_creation(self):
|
|
60
|
+
agent = MockAgent()
|
|
61
|
+
pattern = ReflectionPattern(agent=agent)
|
|
62
|
+
|
|
63
|
+
self.assertEqual(pattern.name, "reflection")
|
|
64
|
+
self.assertEqual(pattern.pattern_type, PatternType.REFLECTION)
|
|
65
|
+
self.assertEqual(pattern.current_iteration, 0)
|
|
66
|
+
|
|
67
|
+
def test_execute(self):
|
|
68
|
+
agent = MockAgent()
|
|
69
|
+
pattern = ReflectionPattern(agent=agent)
|
|
70
|
+
|
|
71
|
+
result = asyncio.run(pattern.execute("Test task"))
|
|
72
|
+
|
|
73
|
+
self.assertTrue(result.success)
|
|
74
|
+
self.assertIsNotNone(result.result)
|
|
75
|
+
self.assertGreater(result.iterations, 0)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class TestToolUsePattern(unittest.TestCase):
|
|
79
|
+
|
|
80
|
+
def test_creation(self):
|
|
81
|
+
agent = MockAgent()
|
|
82
|
+
tool = MockTool()
|
|
83
|
+
pattern = ToolUsePattern(agent=agent, tools=[tool])
|
|
84
|
+
|
|
85
|
+
self.assertEqual(pattern.name, "tool_use")
|
|
86
|
+
self.assertEqual(pattern.pattern_type, PatternType.TOOL_USE)
|
|
87
|
+
self.assertIn("mock_tool", pattern.tools)
|
|
88
|
+
|
|
89
|
+
def test_execute(self):
|
|
90
|
+
agent = MockAgent()
|
|
91
|
+
tool = MockTool()
|
|
92
|
+
pattern = ToolUsePattern(agent=agent, tools=[tool])
|
|
93
|
+
|
|
94
|
+
result = asyncio.run(pattern.execute("Test task"))
|
|
95
|
+
|
|
96
|
+
self.assertTrue(result.success)
|
|
97
|
+
self.assertIsNotNone(result.result)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class TestReActPattern(unittest.TestCase):
|
|
101
|
+
|
|
102
|
+
def test_creation(self):
|
|
103
|
+
agent = MockAgent()
|
|
104
|
+
pattern = ReActPattern(agent=agent)
|
|
105
|
+
|
|
106
|
+
self.assertEqual(pattern.name, "react")
|
|
107
|
+
self.assertEqual(pattern.pattern_type, PatternType.REACT)
|
|
108
|
+
|
|
109
|
+
def test_execute(self):
|
|
110
|
+
agent = MockAgent()
|
|
111
|
+
pattern = ReActPattern(agent=agent, tools=[])
|
|
112
|
+
|
|
113
|
+
result = asyncio.run(pattern.execute("Test task"))
|
|
114
|
+
|
|
115
|
+
self.assertTrue(result.success)
|
|
116
|
+
self.assertIn('thoughts', result.metadata)
|
|
117
|
+
self.assertIn('actions', result.metadata)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class TestPlanningPattern(unittest.TestCase):
|
|
121
|
+
|
|
122
|
+
def test_creation(self):
|
|
123
|
+
agent = MockAgent()
|
|
124
|
+
pattern = PlanningPattern(agent=agent)
|
|
125
|
+
|
|
126
|
+
self.assertEqual(pattern.name, "planning")
|
|
127
|
+
self.assertEqual(pattern.pattern_type, PatternType.PLANNING)
|
|
128
|
+
|
|
129
|
+
def test_execute(self):
|
|
130
|
+
agent = MockAgent()
|
|
131
|
+
pattern = PlanningPattern(agent=agent)
|
|
132
|
+
|
|
133
|
+
result = asyncio.run(pattern.execute("Test task"))
|
|
134
|
+
|
|
135
|
+
self.assertTrue(result.success)
|
|
136
|
+
self.assertIn('plan', result.metadata)
|
|
137
|
+
self.assertGreater(len(pattern.get_plan()), 0)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class TestMultiAgentPattern(unittest.TestCase):
|
|
141
|
+
|
|
142
|
+
def test_creation(self):
|
|
143
|
+
agent = MockAgent()
|
|
144
|
+
role = AgentRole(name="test", agent=agent, role_description="Test role")
|
|
145
|
+
pattern = MultiAgentPattern(agents=[role], coordinator_agent=agent)
|
|
146
|
+
|
|
147
|
+
self.assertEqual(pattern.name, "multi_agent")
|
|
148
|
+
self.assertEqual(pattern.pattern_type, PatternType.MULTI_AGENT)
|
|
149
|
+
self.assertIn("test", pattern.agent_roles)
|
|
150
|
+
|
|
151
|
+
def test_execute(self):
|
|
152
|
+
agent = MockAgent()
|
|
153
|
+
role1 = AgentRole(name="agent1", agent=agent, role_description="Role 1")
|
|
154
|
+
role2 = AgentRole(name="agent2", agent=agent, role_description="Role 2")
|
|
155
|
+
pattern = MultiAgentPattern(agents=[role1, role2], coordinator_agent=agent)
|
|
156
|
+
|
|
157
|
+
result = asyncio.run(pattern.execute("Test task"))
|
|
158
|
+
|
|
159
|
+
self.assertTrue(result.success)
|
|
160
|
+
self.assertIn('contributions', result.metadata)
|
|
161
|
+
self.assertEqual(len(result.metadata['contributions']), 2)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
if __name__ == '__main__':
|
|
165
|
+
unittest.main()
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pygeai-orchestration
|
|
3
|
+
Version: 0.1.0b2
|
|
4
|
+
Summary: Agentic AI orchestration patterns built on Globant Enterprise AI
|
|
5
|
+
Author-email: Globant <geai-sdk@globant.com>
|
|
6
|
+
Keywords: geai,pygeai,orchestration,agents,ai,multi-agent,autogen,crewai
|
|
7
|
+
Classifier: Development Status :: 4 - Beta
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
10
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: pygeai>=0.7.0b9
|
|
21
|
+
Requires-Dist: pydantic>=2.11.3
|
|
22
|
+
Requires-Dist: typing-extensions>=4.13.2
|
|
23
|
+
Provides-Extra: docs
|
|
24
|
+
Requires-Dist: sphinx; extra == "docs"
|
|
25
|
+
Requires-Dist: sphinx-rtd-theme; extra == "docs"
|
|
26
|
+
Requires-Dist: sphinx-markdown-builder; extra == "docs"
|
|
27
|
+
Provides-Extra: tests
|
|
28
|
+
Requires-Dist: coverage; extra == "tests"
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
# PyGEAI-Orchestration - Agentic AI Orchestration Patterns
|
|
32
|
+
|
|
33
|
+
PyGEAI-Orchestration is a complementary package to [PyGEAI](https://pypi.org/project/pygeai/) that implements agentic AI orchestration patterns built on top of [Globant Enterprise AI](https://wiki.genexus.com/enterprise-ai/wiki?8,Table+of+contents%3AEnterprise+AI). It provides powerful orchestration capabilities similar to AutoGen and CrewAI, designed specifically for the Globant Enterprise AI platform.
|
|
34
|
+
|
|
35
|
+
> [!WARNING]
|
|
36
|
+
> This project is in Alpha stage and it's NOT suitable for production yet.
|
|
37
|
+
> While you can install the package and try it out, it's recommended to avoid using it in production until version 1.0.0
|
|
38
|
+
> is released
|
|
39
|
+
|
|
40
|
+
## Features
|
|
41
|
+
|
|
42
|
+
**Multiple Orchestration Patterns**
|
|
43
|
+
- **Reflection Pattern**: Self-critique and iterative improvement
|
|
44
|
+
- **Tool Use Pattern**: Function calling and tool integration
|
|
45
|
+
- **ReAct Pattern**: Reasoning + Acting loop for complex problem-solving
|
|
46
|
+
- **Planning Pattern**: Multi-step planning and execution
|
|
47
|
+
- **Multi-Agent Pattern**: Collaborative agent coordination
|
|
48
|
+
|
|
49
|
+
**Built on PyGEAI**
|
|
50
|
+
- Leverages PyGEAI's robust SDK capabilities
|
|
51
|
+
- Seamless integration with Globant Enterprise AI
|
|
52
|
+
- No code duplication - reuses PyGEAI infrastructure
|
|
53
|
+
|
|
54
|
+
**Easy to Use**
|
|
55
|
+
- Simple CLI tool: `geai-orch`
|
|
56
|
+
- Pythonic API for programmatic access
|
|
57
|
+
- Rich examples and documentation
|
|
58
|
+
|
|
59
|
+
## Installation
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install pygeai-orchestration
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Requirements:**
|
|
66
|
+
- Python >= 3.10
|
|
67
|
+
- PyGEAI >= 0.7.0b9
|
|
68
|
+
|
|
69
|
+
## Quick Start
|
|
70
|
+
|
|
71
|
+
### CLI Usage
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# Run a reflection pattern
|
|
75
|
+
geai-orch reflection --agent my-agent --iterations 3
|
|
76
|
+
|
|
77
|
+
# Execute a ReAct pattern
|
|
78
|
+
geai-orch react --agent reasoning-agent --task "Solve complex problem"
|
|
79
|
+
|
|
80
|
+
# Multi-agent collaboration
|
|
81
|
+
geai-orch multi-agent --config agents.yaml
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Python API
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
import asyncio
|
|
88
|
+
from pygeai_orchestration import (
|
|
89
|
+
GEAIAgent,
|
|
90
|
+
AgentConfig,
|
|
91
|
+
ReflectionPattern
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
async def main():
|
|
95
|
+
# Create an agent
|
|
96
|
+
config = AgentConfig(
|
|
97
|
+
name="my-agent",
|
|
98
|
+
model="gpt-4",
|
|
99
|
+
temperature=0.7
|
|
100
|
+
)
|
|
101
|
+
agent = GEAIAgent(config)
|
|
102
|
+
|
|
103
|
+
# Create and execute pattern
|
|
104
|
+
pattern = ReflectionPattern(agent=agent)
|
|
105
|
+
result = await pattern.execute("Improve this text: Hello world")
|
|
106
|
+
|
|
107
|
+
print(f"Success: {result.success}")
|
|
108
|
+
print(f"Result: {result.result}")
|
|
109
|
+
print(f"Iterations: {result.iterations}")
|
|
110
|
+
|
|
111
|
+
asyncio.run(main())
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Configuration
|
|
115
|
+
|
|
116
|
+
PyGEAI-Orchestration uses the same configuration as PyGEAI. Set up your credentials using one of these methods:
|
|
117
|
+
|
|
118
|
+
**Environment Variables:**
|
|
119
|
+
```bash
|
|
120
|
+
export GEAI_API_KEY=<your-api-key>
|
|
121
|
+
export GEAI_API_BASE_URL=<base-url>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Credentials File:**
|
|
125
|
+
Create `$USER_HOME/.geai/credentials`:
|
|
126
|
+
```ini
|
|
127
|
+
[default]
|
|
128
|
+
geai_api_key = <API_TOKEN>
|
|
129
|
+
geai_api_base_url = <GEAI_BASE_URL>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
See [PyGEAI Configuration](https://github.com/genexus-books/pygeai#configuration) for more details.
|
|
133
|
+
|
|
134
|
+
## Orchestration Patterns
|
|
135
|
+
|
|
136
|
+
### 1. Reflection Pattern
|
|
137
|
+
Enables agents to self-critique and iteratively improve their outputs.
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
from pygeai_orchestration.patterns.reflection import ReflectionAgent
|
|
141
|
+
|
|
142
|
+
agent = ReflectionAgent(session=session, agent_id="critic-agent")
|
|
143
|
+
result = agent.reflect_and_improve("Initial output", iterations=3)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Use Cases:**
|
|
147
|
+
- Content quality improvement
|
|
148
|
+
- Code review and refinement
|
|
149
|
+
- Self-correcting responses
|
|
150
|
+
|
|
151
|
+
### 2. Tool Use Pattern
|
|
152
|
+
Integrates function calling and tool execution into agent workflows.
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
from pygeai_orchestration.patterns.tool_use import ToolUseAgent
|
|
156
|
+
|
|
157
|
+
agent = ToolUseAgent(session=session)
|
|
158
|
+
agent.register_tool("search", search_function)
|
|
159
|
+
result = agent.execute("Search for information about AI")
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Use Cases:**
|
|
163
|
+
- API integration
|
|
164
|
+
- External data retrieval
|
|
165
|
+
- Action execution
|
|
166
|
+
|
|
167
|
+
### 3. ReAct Pattern
|
|
168
|
+
Implements the Reasoning + Acting loop for step-by-step problem solving.
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
from pygeai_orchestration.patterns.react import ReActAgent
|
|
172
|
+
|
|
173
|
+
agent = ReActAgent(session=session, agent_id="reasoning-agent")
|
|
174
|
+
result = agent.solve("Complex multi-step problem")
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**Use Cases:**
|
|
178
|
+
- Complex problem solving
|
|
179
|
+
- Research tasks
|
|
180
|
+
- Multi-step workflows
|
|
181
|
+
|
|
182
|
+
### 4. Planning Pattern
|
|
183
|
+
Creates and executes multi-step plans with adaptive execution.
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
from pygeai_orchestration.patterns.planning import PlanningAgent
|
|
187
|
+
|
|
188
|
+
agent = PlanningAgent(session=session, planner_id="planner")
|
|
189
|
+
plan = agent.create_plan("Build a web application")
|
|
190
|
+
result = agent.execute_plan(plan)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
**Use Cases:**
|
|
194
|
+
- Project planning
|
|
195
|
+
- Task decomposition
|
|
196
|
+
- Workflow automation
|
|
197
|
+
|
|
198
|
+
### 5. Multi-Agent Pattern
|
|
199
|
+
Coordinates multiple agents working collaboratively on complex tasks.
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
from pygeai_orchestration.patterns.multi_agent import MultiAgentCoordinator
|
|
203
|
+
|
|
204
|
+
coordinator = MultiAgentCoordinator(session=session)
|
|
205
|
+
coordinator.add_agent("researcher", researcher_agent)
|
|
206
|
+
coordinator.add_agent("writer", writer_agent)
|
|
207
|
+
result = coordinator.execute("Create research report")
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**Use Cases:**
|
|
211
|
+
- Team collaboration simulation
|
|
212
|
+
- Complex task delegation
|
|
213
|
+
- Specialized agent workflows
|
|
214
|
+
|
|
215
|
+
## Documentation
|
|
216
|
+
|
|
217
|
+
- [Getting Started Guide](docs/getting-started.md)
|
|
218
|
+
- [Pattern Documentation](docs/patterns/)
|
|
219
|
+
- [API Reference](docs/api-reference/)
|
|
220
|
+
- [Examples](snippets/)
|
|
221
|
+
|
|
222
|
+
## Development
|
|
223
|
+
|
|
224
|
+
### Setup Development Environment
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
git clone https://github.com/genexus-books/pygeai-orchestration.git
|
|
228
|
+
cd pygeai-orchestration
|
|
229
|
+
python -m venv venv
|
|
230
|
+
source venv/bin/activate # On Windows: venv\Scripts\activate
|
|
231
|
+
pip install -r requirements.txt
|
|
232
|
+
pip install -e .
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Running Tests
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
# Run all tests
|
|
239
|
+
python testing.py
|
|
240
|
+
|
|
241
|
+
# Run specific pattern tests
|
|
242
|
+
python -m unittest pygeai_orchestration.tests.patterns.test_reflection
|
|
243
|
+
|
|
244
|
+
# Check coverage
|
|
245
|
+
python testing.py --coverage
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed development guidelines.
|
|
249
|
+
|
|
250
|
+
## Examples
|
|
251
|
+
|
|
252
|
+
Check the [snippets/](snippets/) directory for working examples of each pattern:
|
|
253
|
+
|
|
254
|
+
- [Reflection Pattern Example](snippets/reflection/basic_reflection.py)
|
|
255
|
+
- [Tool Use Pattern Example](snippets/tool_use/function_calling.py)
|
|
256
|
+
- [ReAct Pattern Example](snippets/react/simple_react_loop.py)
|
|
257
|
+
- [Planning Pattern Example](snippets/planning/multi_step_plan.py)
|
|
258
|
+
- [Multi-Agent Pattern Example](snippets/multi_agent/collaborative_agents.py)
|
|
259
|
+
|
|
260
|
+
## Contributing
|
|
261
|
+
|
|
262
|
+
We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
263
|
+
|
|
264
|
+
## License
|
|
265
|
+
|
|
266
|
+
This project is licensed under the MIT License - see [LICENSE](LICENSE) for details.
|
|
267
|
+
|
|
268
|
+
## Terms and Conditions
|
|
269
|
+
|
|
270
|
+
By using this SDK, you agree to the [Globant Enterprise AI Terms of Use](https://www.globant.com/enterprise-ai/terms-of-use).
|
|
271
|
+
|
|
272
|
+
## Support
|
|
273
|
+
|
|
274
|
+
- [Documentation](docs/)
|
|
275
|
+
- [Issue Tracker](https://github.com/genexus-books/pygeai-orchestration/issues)
|
|
276
|
+
- [Discussions](https://github.com/genexus-books/pygeai-orchestration/discussions)
|
|
277
|
+
|
|
278
|
+
## Related Projects
|
|
279
|
+
|
|
280
|
+
- [PyGEAI](https://github.com/genexus-books/pygeai) - Core SDK for Globant Enterprise AI
|
|
281
|
+
- [AutoGen](https://github.com/microsoft/autogen) - Multi-agent framework by Microsoft
|
|
282
|
+
- [CrewAI](https://github.com/joaomdmoura/crewAI) - Framework for orchestrating AI agents
|
|
283
|
+
|
|
284
|
+
## Compatibility
|
|
285
|
+
|
|
286
|
+
This package is compatible with Globant Enterprise AI release from February 2026 and requires PyGEAI >= 0.7.0b9.
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
**Made by Globant**
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
pygeai_orchestration/__init__.py,sha256=phPtSd0SgtSOicRoZAbgkSQCPv0wPeWHWz-lpcEujx0,1820
|
|
2
|
+
pygeai_orchestration/cli/__init__.py,sha256=d6wFWuMlcMLng3lBfjSOfhnO_tZINHfNUOmA7-zwPX4,134
|
|
3
|
+
pygeai_orchestration/cli/__main__.py,sha256=2AmJiRvdlVSu3T6FmFAzQuu4a5llrUeMfUX8ShQxhus,157
|
|
4
|
+
pygeai_orchestration/cli/error_handler.py,sha256=ReeeeX2TvJZBgdCc0hzvLZd8LYSQVqYI5hAEqJ4FCCM,3862
|
|
5
|
+
pygeai_orchestration/cli/formatters.py,sha256=pCS8gZ0k7AKWkLFDPrSQ_UpLGqv4nPyGxnNDwCO4wFc,11037
|
|
6
|
+
pygeai_orchestration/cli/geai_orch.py,sha256=2LjVU4hlgmAQ2FBp6j0rWcXXy1EiflpxcuVTLvikCAs,11307
|
|
7
|
+
pygeai_orchestration/cli/interactive.py,sha256=KVZzgjMS89dDQM25XohcFb3dTbjpMsWV_Z9uMzZ-fjY,7912
|
|
8
|
+
pygeai_orchestration/cli/commands/__init__.py,sha256=PLWgWUXomwNCqR-wrl_-fWOYFN8PeIAvPnYvmzvzg0w,232
|
|
9
|
+
pygeai_orchestration/cli/commands/base.py,sha256=BWjuxk06KkRVozCMbwjD_pkzrsAHWE9TgVKQegkIHoM,6558
|
|
10
|
+
pygeai_orchestration/cli/texts/help.py,sha256=vHAA-2SSrSYpQLfLp3Ruz0-WEVb8UmCahgDkOZEcGGI,5423
|
|
11
|
+
pygeai_orchestration/core/__init__.py,sha256=muVm1M6yfTgYDL6Fe1h6hdKB8QLJKQrWBgRj3svOSlk,2380
|
|
12
|
+
pygeai_orchestration/core/composition.py,sha256=j_mV0iNJfPkCO8h1rrzKY0eL00g77C8D_Hy6qPnDaMw,6654
|
|
13
|
+
pygeai_orchestration/core/config.py,sha256=m2dbBejao9ku0od4jYo4Xn-HmDvq_d44EYJkZKGgd4I,11493
|
|
14
|
+
pygeai_orchestration/core/exceptions.py,sha256=nZWQlPFQtyGmx-qtErydXvIfiqkXuBlsxoofDyDoRLQ,13282
|
|
15
|
+
pygeai_orchestration/core/handlers.py,sha256=6OMo3xZvHwITdn8J4kX7Wi65iWGRwnL0ioHLNmFWwBQ,13474
|
|
16
|
+
pygeai_orchestration/core/base/__init__.py,sha256=VuUeoAVxKN1DOXMLKwkhPSkfMFVcKPwU0LP5M9jKeaM,622
|
|
17
|
+
pygeai_orchestration/core/base/agent.py,sha256=J_xRVE1MXPbXWC1ndwMHMVWY44S0myIzNfVPJzoE7Lo,4446
|
|
18
|
+
pygeai_orchestration/core/base/geai_agent.py,sha256=PG0X8gmKiU0MxkRKJchvcBOztcVCk_eXwJ3kmcRi4aU,5097
|
|
19
|
+
pygeai_orchestration/core/base/geai_orchestrator.py,sha256=sGEdUc4TeKJ_WiPw91FBkXCkEWO986WOXbm9LMp6CL8,2730
|
|
20
|
+
pygeai_orchestration/core/base/orchestrator.py,sha256=83BIjYerW1ADnzWG_GGIzLTZF_nS93cb4uXp9KWryAU,4835
|
|
21
|
+
pygeai_orchestration/core/base/pattern.py,sha256=vuj1UXLuaZvbhjiifwqThnQ_4uaRBkv2VQ5FBrY8Xxs,6114
|
|
22
|
+
pygeai_orchestration/core/base/tool.py,sha256=sypjwrMKiiuwi6VmbXIVoIOkQ4RZWpZD0aSlOCKYOSs,5491
|
|
23
|
+
pygeai_orchestration/core/common/__init__.py,sha256=akhDVqt8uFH0u2k4w_STrKZJ3ndjtpRDkp46baxCn6U,416
|
|
24
|
+
pygeai_orchestration/core/common/context.py,sha256=mMch8-9mLReYglBMbIB2RCDYn7EGjzsp2kgaO3mwHG8,4321
|
|
25
|
+
pygeai_orchestration/core/common/memory.py,sha256=1jYrqc1sPJujuIcNQLITXbKnQ_WMGjkvcNTMDkoQU9o,5841
|
|
26
|
+
pygeai_orchestration/core/common/message.py,sha256=4JB3ZPeegaRvEYf1MKy7NBHm76oCBYf8V6cuNIf9kzw,1732
|
|
27
|
+
pygeai_orchestration/core/common/state.py,sha256=mHo7ozjvPdHZfd-sbVg6t9HLND7ll1xD6sm6v8nCrb4,5690
|
|
28
|
+
pygeai_orchestration/core/utils/__init__.py,sha256=-DVGUjf1t4XeWNk4SPrMTJcVI1Nf87-j-QJ_7EagsPI,819
|
|
29
|
+
pygeai_orchestration/core/utils/cache.py,sha256=TJLtj115EXqlLrKLMoK7JCT-fZ1TGSjVxmJyW6n2p8c,4108
|
|
30
|
+
pygeai_orchestration/core/utils/config.py,sha256=S2gzAsL20-h_AEFDj0izjlXhWucBLwbE7Vv4ovUiJIM,2775
|
|
31
|
+
pygeai_orchestration/core/utils/logging.py,sha256=BPi0s9dU4i6e4E2HZU1omqI79JhaJ9js533RG_BFAtE,1812
|
|
32
|
+
pygeai_orchestration/core/utils/metrics.py,sha256=Kw8hA-PS6UHMLf0pEG2XQZvcBbuIFhHdo1kWEMf6Vz8,6359
|
|
33
|
+
pygeai_orchestration/core/utils/validators.py,sha256=EoVQSrJDmM8dGlqAoM_1q3B7SBAyI3yuLfcj3weZuio,5146
|
|
34
|
+
pygeai_orchestration/dev/__init__.py,sha256=e-GSMmpkN1cINRDtInrjKTQogSJ2h-Q1ya_YaaMRF5s,476
|
|
35
|
+
pygeai_orchestration/dev/debug.py,sha256=0RJed1TCreaSm7HC9ijbd9EeMoh3zFn4osjrYDcY5sQ,8444
|
|
36
|
+
pygeai_orchestration/dev/templates.py,sha256=J57-8Stg_m-f-cj_Zl9Yz_VNEZA-trGM8SKdsdzH7NQ,8143
|
|
37
|
+
pygeai_orchestration/dev/testing.py,sha256=_zXvj3JHJNFEbZJnK-OdtL_tD-3xQeCHMrJ39UYqaqg,7826
|
|
38
|
+
pygeai_orchestration/patterns/__init__.py,sha256=W5qROE1pbeVOgPx6xiKZtQp0eQPL9kEhL6Et3t6kBA8,376
|
|
39
|
+
pygeai_orchestration/patterns/multi_agent.py,sha256=fe_9AS2L7b_M0-ahcwCgBjh_s8zYUi1nz7nS6OD5E6I,9857
|
|
40
|
+
pygeai_orchestration/patterns/planning.py,sha256=JITIexKfmc1H2KTVCxbq9EhMGbwbYo4xwfVttW5zsc0,8885
|
|
41
|
+
pygeai_orchestration/patterns/react.py,sha256=P1aNgjgrEPP-zUO5OfUx0q2GO9J_yvRakirRFiPJfho,9320
|
|
42
|
+
pygeai_orchestration/patterns/reflection.py,sha256=KRthEpuGKH3LM0ceY0cahpC9kAmmH8HqXeF63r_kqUY,5728
|
|
43
|
+
pygeai_orchestration/patterns/tool_use.py,sha256=trJPV_SzXOD6mXIAivJASHaahxYQZjBZcVmV57QQUv4,7594
|
|
44
|
+
pygeai_orchestration/tests/__init__.py,sha256=nzx1KsiYalL_YuXKE6acW8Dj5flmMg0-i4gyYO0gV54,22
|
|
45
|
+
pygeai_orchestration/tests/test_base_classes.py,sha256=9VcME1C1fY1cijblzh639ZQhV6-iWGuEx80kDRpDr6k,5673
|
|
46
|
+
pygeai_orchestration/tests/test_cache.py,sha256=t_iYB_La1mkKBEnFinl10kNa1IVtW_yWjKq0dz8-Z4w,5705
|
|
47
|
+
pygeai_orchestration/tests/test_cli_formatters.py,sha256=Gpg6OOstFR3L7Mwk8cpmFSupP67tr3PAgFTvaKYYfA4,7856
|
|
48
|
+
pygeai_orchestration/tests/test_common.py,sha256=AiRIWh7_vlSVs35ZSPvTY5YukZbtpMO_q1LK5_o7lKA,5659
|
|
49
|
+
pygeai_orchestration/tests/test_composition.py,sha256=N1BC8_EEehm5wUuYlxe3UwhtcrAodmUjrDS2Ae4iR8M,9765
|
|
50
|
+
pygeai_orchestration/tests/test_config.py,sha256=vvzAApuVgwQyFt6J7hPDwrxHfthI8ENWPDLApWDe3i4,10253
|
|
51
|
+
pygeai_orchestration/tests/test_dev_utils.py,sha256=XaG-xWcLTNFDYIOaLAQvC6pfaCtLddksuW9FWWQV2Ao,10701
|
|
52
|
+
pygeai_orchestration/tests/test_exceptions.py,sha256=l_YlVxZguM-xcUNlOl8u9R3vx_Oa70vtFHQPDd_0PSQ,10919
|
|
53
|
+
pygeai_orchestration/tests/test_handlers.py,sha256=t3ZCbif4npxsgoYM6vpCrxOru0q-7f0ARjYAE1SuK7Y,12166
|
|
54
|
+
pygeai_orchestration/tests/test_metrics.py,sha256=GP36BuTxp7ydptG4nGo804yB91UkBHuskEfJuZwkZvE,6078
|
|
55
|
+
pygeai_orchestration/tests/test_patterns.py,sha256=9qarHU_9R70nfWf98TzMiJO_XCfz9-kOn9Ju2RQnogs,5051
|
|
56
|
+
pygeai_orchestration-0.1.0b2.dist-info/licenses/LICENSE,sha256=HrOw5fbeVfHobmqgadP4r7dOPCMDA4uOfFX8pE3MDT0,1072
|
|
57
|
+
pygeai_orchestration-0.1.0b2.dist-info/METADATA,sha256=KEMGd394CJL1hTk9pugoM0dzTnBsN9_BaVqdgU8KsGc,8383
|
|
58
|
+
pygeai_orchestration-0.1.0b2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
59
|
+
pygeai_orchestration-0.1.0b2.dist-info/entry_points.txt,sha256=p8ODRFqLwrrhoS6FlQW330_J59-0ZjSd6x8kAYYLm7E,69
|
|
60
|
+
pygeai_orchestration-0.1.0b2.dist-info/top_level.txt,sha256=8gLeyR8RXLump7AkakhU7kFBh6O7XTvrJi1bKK4e39g,21
|
|
61
|
+
pygeai_orchestration-0.1.0b2.dist-info/RECORD,,
|