multiagent-init 0.1.0__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.
- multiagent_init/__init__.py +1 -0
- multiagent_init/cli.py +38 -0
- multiagent_init/generator.py +315 -0
- multiagent_init/templates_data.py +370 -0
- multiagent_init/wizard.py +175 -0
- multiagent_init-0.1.0.dist-info/METADATA +147 -0
- multiagent_init-0.1.0.dist-info/RECORD +11 -0
- multiagent_init-0.1.0.dist-info/WHEEL +5 -0
- multiagent_init-0.1.0.dist-info/entry_points.txt +2 -0
- multiagent_init-0.1.0.dist-info/licenses/LICENSE +21 -0
- multiagent_init-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
multiagent_init/cli.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
from .generator import generate_project
|
|
6
|
+
from .wizard import run_wizard
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def main() -> int:
|
|
10
|
+
try:
|
|
11
|
+
config = run_wizard()
|
|
12
|
+
destination = generate_project(config)
|
|
13
|
+
|
|
14
|
+
print()
|
|
15
|
+
print("🎉 Done!")
|
|
16
|
+
print()
|
|
17
|
+
print("Next steps:")
|
|
18
|
+
print()
|
|
19
|
+
print(f" cd {destination.name}")
|
|
20
|
+
print(" python3 -m venv .venv")
|
|
21
|
+
print(" source .venv/bin/activate # macOS/Linux")
|
|
22
|
+
print(" pip install .")
|
|
23
|
+
print(" python main.py")
|
|
24
|
+
print()
|
|
25
|
+
|
|
26
|
+
return 0
|
|
27
|
+
|
|
28
|
+
except KeyboardInterrupt:
|
|
29
|
+
print("\n\nCancelled.")
|
|
30
|
+
return 130
|
|
31
|
+
|
|
32
|
+
except (ValueError, FileExistsError, OSError) as exc:
|
|
33
|
+
print(f"\nError: {exc}", file=sys.stderr)
|
|
34
|
+
return 1
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
if __name__ == "__main__":
|
|
38
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from .templates_data import (
|
|
7
|
+
AGENT_TEMPLATES,
|
|
8
|
+
CONFIG_TEMPLATE,
|
|
9
|
+
DOCKER_FILE,
|
|
10
|
+
FRAMEWORK_FILES,
|
|
11
|
+
ORCHESTRATOR_TEMPLATES,
|
|
12
|
+
PROVIDER_ENV_VARS,
|
|
13
|
+
TEST_TEMPLATE,
|
|
14
|
+
TOOL_FILE,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
DEFAULT_AGENT_CLASSES = {
|
|
18
|
+
"Researcher": "ResearcherAgent",
|
|
19
|
+
"Writer": "WriterAgent",
|
|
20
|
+
"Reviewer": "ReviewerAgent",
|
|
21
|
+
"Planner": "PlannerAgent",
|
|
22
|
+
"Executor": "ExecutorAgent",
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _safe_project_name(value: str) -> str:
|
|
27
|
+
value = value.strip()
|
|
28
|
+
|
|
29
|
+
if not value:
|
|
30
|
+
raise ValueError("project name cannot be empty")
|
|
31
|
+
|
|
32
|
+
if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9_-]*", value):
|
|
33
|
+
raise ValueError("invalid project name")
|
|
34
|
+
|
|
35
|
+
return value
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _safe_package_name(project_name: str) -> str:
|
|
39
|
+
value = re.sub(r"[^A-Za-z0-9_]", "_", project_name).lower()
|
|
40
|
+
|
|
41
|
+
if value[0].isdigit():
|
|
42
|
+
value = f"_{value}"
|
|
43
|
+
|
|
44
|
+
return value
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def agent_to_module(name: str) -> str:
|
|
48
|
+
value = re.sub(r"[^A-Za-z0-9]+", "_", name.strip())
|
|
49
|
+
value = re.sub(r"_+", "_", value).strip("_").lower()
|
|
50
|
+
|
|
51
|
+
if not value:
|
|
52
|
+
value = "agent"
|
|
53
|
+
|
|
54
|
+
if value[0].isdigit():
|
|
55
|
+
value = f"agent_{value}"
|
|
56
|
+
|
|
57
|
+
return value
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def agent_to_class(name: str) -> str:
|
|
61
|
+
parts = re.findall(r"[A-Za-z0-9]+", name.strip())
|
|
62
|
+
|
|
63
|
+
if not parts:
|
|
64
|
+
return "Agent"
|
|
65
|
+
|
|
66
|
+
class_name = "".join(part.capitalize() for part in parts)
|
|
67
|
+
|
|
68
|
+
if class_name[0].isdigit():
|
|
69
|
+
class_name = f"Agent{class_name}"
|
|
70
|
+
|
|
71
|
+
return f"{class_name}Agent"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _write(path: Path, content: str) -> None:
|
|
75
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
76
|
+
path.write_text(content, encoding="utf-8")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def generate_project(config: dict) -> Path:
|
|
80
|
+
project_name = _safe_project_name(config["project_name"])
|
|
81
|
+
package_name = _safe_package_name(project_name)
|
|
82
|
+
destination = Path.cwd() / project_name
|
|
83
|
+
|
|
84
|
+
if destination.exists():
|
|
85
|
+
if not destination.is_dir():
|
|
86
|
+
raise FileExistsError(f"'{project_name}' already exists and is not a directory")
|
|
87
|
+
|
|
88
|
+
if any(destination.iterdir()):
|
|
89
|
+
raise FileExistsError(
|
|
90
|
+
f"directory '{project_name}' already exists and is not empty"
|
|
91
|
+
)
|
|
92
|
+
else:
|
|
93
|
+
destination.mkdir(parents=True)
|
|
94
|
+
|
|
95
|
+
package_dir = destination / "src" / package_name
|
|
96
|
+
|
|
97
|
+
framework_pyproject = FRAMEWORK_FILES[config["framework"]]
|
|
98
|
+
framework_pyproject = framework_pyproject.replace("{{PROJECT_NAME}}", project_name)
|
|
99
|
+
framework_pyproject = framework_pyproject.replace("{{PACKAGE_NAME}}", package_name)
|
|
100
|
+
|
|
101
|
+
_write(destination / "pyproject.toml", framework_pyproject)
|
|
102
|
+
|
|
103
|
+
_write(
|
|
104
|
+
destination / "README.md",
|
|
105
|
+
_render_readme(config, package_name),
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
provider_env_var = PROVIDER_ENV_VARS[config["provider"]]
|
|
109
|
+
|
|
110
|
+
_write(
|
|
111
|
+
destination / ".env",
|
|
112
|
+
f"MODEL_NAME=your-model\n{provider_env_var}=\n",
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
_write(
|
|
116
|
+
destination / ".gitignore",
|
|
117
|
+
"__pycache__/\n*.py[cod]\n.venv/\n.env\n.pytest_cache/\n",
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
_write(package_dir / "__init__.py", '"""Generated package."""\n')
|
|
121
|
+
|
|
122
|
+
config_content = CONFIG_TEMPLATE.replace("{{PROVIDER_ENV_VAR}}", provider_env_var)
|
|
123
|
+
config_content = config_content.replace("{{PROVIDER}}", config["provider"])
|
|
124
|
+
_write(package_dir / "config.py", config_content)
|
|
125
|
+
|
|
126
|
+
agent_template = AGENT_TEMPLATES.get(config["framework"], AGENT_TEMPLATES["basic"])
|
|
127
|
+
orchestrator_template = ORCHESTRATOR_TEMPLATES.get(
|
|
128
|
+
config["framework"], ORCHESTRATOR_TEMPLATES["basic"]
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
agent_files = []
|
|
132
|
+
|
|
133
|
+
for agent_name in config["agent_names"]:
|
|
134
|
+
module_name = agent_to_module(agent_name)
|
|
135
|
+
class_name = agent_to_class(agent_name)
|
|
136
|
+
|
|
137
|
+
agent_files.append(
|
|
138
|
+
{
|
|
139
|
+
"name": agent_name,
|
|
140
|
+
"module": module_name,
|
|
141
|
+
"class": class_name,
|
|
142
|
+
}
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
content = agent_template
|
|
146
|
+
content = content.replace("{{AGENT_NAME}}", agent_name)
|
|
147
|
+
content = content.replace("{{AGENT_CLASS}}", class_name)
|
|
148
|
+
|
|
149
|
+
_write(
|
|
150
|
+
package_dir / "agents" / f"{module_name}.py",
|
|
151
|
+
content,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
_write(
|
|
155
|
+
package_dir / "agents" / "__init__.py",
|
|
156
|
+
'"""Generated agents."""\n',
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
imports = "\n".join(
|
|
160
|
+
f"from {package_name}.agents.{item['module']} import {item['class']}"
|
|
161
|
+
for item in agent_files
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
instances = ",\n ".join(
|
|
165
|
+
f"{item['class']}()"
|
|
166
|
+
for item in agent_files
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
main_content = f"""from __future__ import annotations
|
|
170
|
+
|
|
171
|
+
import logging
|
|
172
|
+
|
|
173
|
+
{imports}
|
|
174
|
+
|
|
175
|
+
from {package_name} import config
|
|
176
|
+
from {package_name}.workflow.orchestrator import Orchestrator
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def main() -> None:
|
|
180
|
+
logging.basicConfig(
|
|
181
|
+
level=logging.INFO,
|
|
182
|
+
format="%(asctime)s %(levelname)s %(name)s: %(message)s",
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
config.validate()
|
|
186
|
+
|
|
187
|
+
agents = [
|
|
188
|
+
{instances}
|
|
189
|
+
]
|
|
190
|
+
|
|
191
|
+
orchestrator = Orchestrator(agents)
|
|
192
|
+
|
|
193
|
+
result = orchestrator.run(
|
|
194
|
+
"Build a short report about multi-agent systems."
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
print("\\n=== Final result ===")
|
|
198
|
+
print(result)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
if __name__ == "__main__":
|
|
202
|
+
main()
|
|
203
|
+
"""
|
|
204
|
+
|
|
205
|
+
_write(destination / "main.py", main_content)
|
|
206
|
+
|
|
207
|
+
_write(
|
|
208
|
+
package_dir / "workflow" / "__init__.py",
|
|
209
|
+
'"""Workflow and orchestration."""\n',
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
_write(
|
|
213
|
+
package_dir / "workflow" / "orchestrator.py",
|
|
214
|
+
orchestrator_template,
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
if config["include_tools"]:
|
|
218
|
+
_write(
|
|
219
|
+
package_dir / "tools" / "__init__.py",
|
|
220
|
+
'"""Tools available to agents."""\n',
|
|
221
|
+
)
|
|
222
|
+
_write(package_dir / "tools" / "web_search.py", TOOL_FILE)
|
|
223
|
+
|
|
224
|
+
if config["include_tests"]:
|
|
225
|
+
test_content = TEST_TEMPLATE.replace(
|
|
226
|
+
"{{TEST_IMPORTS}}",
|
|
227
|
+
"\n".join(
|
|
228
|
+
f"from {package_name}.agents.{item['module']} import {item['class']}"
|
|
229
|
+
for item in agent_files
|
|
230
|
+
),
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
assertions = "\n".join(
|
|
234
|
+
f' self.assertIn("{item["name"]}", {item["class"]}().run(task))'
|
|
235
|
+
for item in agent_files
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
test_content = test_content.replace("{{ASSERTIONS}}", assertions)
|
|
239
|
+
|
|
240
|
+
_write(
|
|
241
|
+
destination / "tests" / "test_agents.py",
|
|
242
|
+
test_content,
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
if config["include_docker"]:
|
|
246
|
+
docker_content = DOCKER_FILE.replace("{{PACKAGE_NAME}}", package_name)
|
|
247
|
+
_write(destination / "Dockerfile", docker_content)
|
|
248
|
+
|
|
249
|
+
print()
|
|
250
|
+
print("Creating project...")
|
|
251
|
+
print()
|
|
252
|
+
print("✓ Agents")
|
|
253
|
+
print("✓ Workflow")
|
|
254
|
+
|
|
255
|
+
if config["include_tools"]:
|
|
256
|
+
print("✓ Tools")
|
|
257
|
+
|
|
258
|
+
print("✓ Configuration")
|
|
259
|
+
|
|
260
|
+
if config["include_tests"]:
|
|
261
|
+
print("✓ Tests")
|
|
262
|
+
|
|
263
|
+
if config["include_docker"]:
|
|
264
|
+
print("✓ Docker")
|
|
265
|
+
|
|
266
|
+
print("✓ README")
|
|
267
|
+
|
|
268
|
+
return destination
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def _render_readme(config: dict, package_name: str) -> str:
|
|
272
|
+
agents = "\n".join(
|
|
273
|
+
f"- `{name}`"
|
|
274
|
+
for name in config["agent_names"]
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
return f"""# {config["project_name"]}
|
|
278
|
+
|
|
279
|
+
Generated by **multiagent-init**.
|
|
280
|
+
|
|
281
|
+
## Configuration
|
|
282
|
+
|
|
283
|
+
- Framework: `{config["framework"]}`
|
|
284
|
+
- Model provider: `{config["provider"]}`
|
|
285
|
+
- Agent configuration: `{config["agent_mode"]}`
|
|
286
|
+
- Number of agents: `{config["agent_count"]}`
|
|
287
|
+
|
|
288
|
+
## Agents
|
|
289
|
+
|
|
290
|
+
{agents}
|
|
291
|
+
|
|
292
|
+
## Structure
|
|
293
|
+
|
|
294
|
+
```text
|
|
295
|
+
src/{package_name}/
|
|
296
|
+
agents/ Individual agents
|
|
297
|
+
workflow/ Orchestration
|
|
298
|
+
tools/ Optional tools
|
|
299
|
+
config.py Configuration
|
|
300
|
+
tests/ Optional tests
|
|
301
|
+
main.py Entry point (python main.py)
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## Run
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
pip install .
|
|
308
|
+
python main.py
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Set your provider's API key in `.env` before running - `config.validate()` fails
|
|
312
|
+
fast with a clear error if it's missing.
|
|
313
|
+
|
|
314
|
+
Replace the placeholder agent logic with your actual LLM/framework implementation.
|
|
315
|
+
"""
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
FRAMEWORK_FILES = {
|
|
2
|
+
"basic": """[build-system]
|
|
3
|
+
requires = ["setuptools>=68"]
|
|
4
|
+
build-backend = "setuptools.build_meta"
|
|
5
|
+
|
|
6
|
+
[project]
|
|
7
|
+
name = "{{PROJECT_NAME}}"
|
|
8
|
+
version = "0.1.0"
|
|
9
|
+
description = "A multi-agent AI application."
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
dependencies = []
|
|
12
|
+
|
|
13
|
+
[tool.setuptools]
|
|
14
|
+
package-dir = {"" = "src"}
|
|
15
|
+
|
|
16
|
+
[tool.setuptools.packages.find]
|
|
17
|
+
where = ["src"]
|
|
18
|
+
""",
|
|
19
|
+
"langgraph": """[build-system]
|
|
20
|
+
requires = ["setuptools>=68"]
|
|
21
|
+
build-backend = "setuptools.build_meta"
|
|
22
|
+
|
|
23
|
+
[project]
|
|
24
|
+
name = "{{PROJECT_NAME}}"
|
|
25
|
+
version = "0.1.0"
|
|
26
|
+
description = "A LangGraph multi-agent AI application."
|
|
27
|
+
requires-python = ">=3.9"
|
|
28
|
+
dependencies = [
|
|
29
|
+
"langgraph>=0.2",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[tool.setuptools]
|
|
33
|
+
package-dir = {"" = "src"}
|
|
34
|
+
|
|
35
|
+
[tool.setuptools.packages.find]
|
|
36
|
+
where = ["src"]
|
|
37
|
+
""",
|
|
38
|
+
"crewai": """[build-system]
|
|
39
|
+
requires = ["setuptools>=68"]
|
|
40
|
+
build-backend = "setuptools.build_meta"
|
|
41
|
+
|
|
42
|
+
[project]
|
|
43
|
+
name = "{{PROJECT_NAME}}"
|
|
44
|
+
version = "0.1.0"
|
|
45
|
+
description = "A CrewAI multi-agent AI application."
|
|
46
|
+
requires-python = ">=3.9"
|
|
47
|
+
dependencies = [
|
|
48
|
+
"crewai>=0.80",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
[tool.setuptools]
|
|
52
|
+
package-dir = {"" = "src"}
|
|
53
|
+
|
|
54
|
+
[tool.setuptools.packages.find]
|
|
55
|
+
where = ["src"]
|
|
56
|
+
""",
|
|
57
|
+
"autogen": """[build-system]
|
|
58
|
+
requires = ["setuptools>=68"]
|
|
59
|
+
build-backend = "setuptools.build_meta"
|
|
60
|
+
|
|
61
|
+
[project]
|
|
62
|
+
name = "{{PROJECT_NAME}}"
|
|
63
|
+
version = "0.1.0"
|
|
64
|
+
description = "An AutoGen multi-agent AI application."
|
|
65
|
+
requires-python = ">=3.9"
|
|
66
|
+
dependencies = [
|
|
67
|
+
"autogen-agentchat>=0.4",
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
[tool.setuptools]
|
|
71
|
+
package-dir = {"" = "src"}
|
|
72
|
+
|
|
73
|
+
[tool.setuptools.packages.find]
|
|
74
|
+
where = ["src"]
|
|
75
|
+
""",
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
PROVIDER_ENV_VARS = {
|
|
79
|
+
"openai": "OPENAI_API_KEY",
|
|
80
|
+
"anthropic": "ANTHROPIC_API_KEY",
|
|
81
|
+
"google": "GOOGLE_API_KEY",
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
CONFIG_TEMPLATE = """import os
|
|
85
|
+
|
|
86
|
+
MODEL_NAME = os.getenv("MODEL_NAME", "your-model")
|
|
87
|
+
{{PROVIDER_ENV_VAR}} = os.getenv("{{PROVIDER_ENV_VAR}}")
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def validate() -> None:
|
|
91
|
+
\"\"\"Fail fast if the required API key is missing.\"\"\"
|
|
92
|
+
if not {{PROVIDER_ENV_VAR}}:
|
|
93
|
+
raise RuntimeError(
|
|
94
|
+
"Missing required environment variable '{{PROVIDER_ENV_VAR}}' for "
|
|
95
|
+
"provider '{{PROVIDER}}'. Set it in your .env file."
|
|
96
|
+
)
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
AGENT_TEMPLATES = {
|
|
100
|
+
"basic": """class {{AGENT_CLASS}}:
|
|
101
|
+
\"\"\"{{AGENT_NAME}} agent.\"\"\"
|
|
102
|
+
|
|
103
|
+
name = "{{AGENT_NAME}}"
|
|
104
|
+
|
|
105
|
+
def run(self, task: str) -> str:
|
|
106
|
+
# TODO: connect this agent to your LLM/framework.
|
|
107
|
+
return f"[{{AGENT_NAME}}] Completed: {task}"
|
|
108
|
+
""",
|
|
109
|
+
"langgraph": """class {{AGENT_CLASS}}:
|
|
110
|
+
\"\"\"{{AGENT_NAME}} node.\"\"\"
|
|
111
|
+
|
|
112
|
+
name = "{{AGENT_NAME}}"
|
|
113
|
+
|
|
114
|
+
def run(self, task: str) -> str:
|
|
115
|
+
# TODO: connect this node to your LLM/framework.
|
|
116
|
+
return f"[{{AGENT_NAME}}] Completed: {task}"
|
|
117
|
+
|
|
118
|
+
def __call__(self, state: dict) -> dict:
|
|
119
|
+
\"\"\"LangGraph node entry point - transforms and returns the graph state.\"\"\"
|
|
120
|
+
state["result"] = self.run(state["task"])
|
|
121
|
+
state["task"] = state["result"]
|
|
122
|
+
return state
|
|
123
|
+
""",
|
|
124
|
+
"crewai": """from crewai import Agent
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class {{AGENT_CLASS}}:
|
|
128
|
+
\"\"\"{{AGENT_NAME}} agent.\"\"\"
|
|
129
|
+
|
|
130
|
+
name = "{{AGENT_NAME}}"
|
|
131
|
+
|
|
132
|
+
def __init__(self) -> None:
|
|
133
|
+
self.agent = Agent(
|
|
134
|
+
role="{{AGENT_NAME}}",
|
|
135
|
+
goal="Complete tasks assigned to the {{AGENT_NAME}} role.",
|
|
136
|
+
backstory="An AI agent specialized in {{AGENT_NAME}} tasks.",
|
|
137
|
+
# TODO: configure an LLM, e.g. llm="gpt-4o-mini".
|
|
138
|
+
allow_delegation=False,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
def run(self, task: str) -> str:
|
|
142
|
+
# TODO: wrap `task` in a crewai.Task and execute it via a Crew.
|
|
143
|
+
return f"[{{AGENT_NAME}}] Completed: {task}"
|
|
144
|
+
""",
|
|
145
|
+
"autogen": """from autogen_agentchat.agents import AssistantAgent
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class {{AGENT_CLASS}}:
|
|
149
|
+
\"\"\"{{AGENT_NAME}} agent.\"\"\"
|
|
150
|
+
|
|
151
|
+
name = "{{AGENT_NAME}}"
|
|
152
|
+
|
|
153
|
+
def build(self, model_client) -> AssistantAgent:
|
|
154
|
+
\"\"\"Build a real AutoGen AssistantAgent once you have a model client.\"\"\"
|
|
155
|
+
return AssistantAgent(
|
|
156
|
+
name="{{AGENT_NAME}}",
|
|
157
|
+
model_client=model_client,
|
|
158
|
+
system_message="You are the {{AGENT_NAME}} agent.",
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
def run(self, task: str) -> str:
|
|
162
|
+
# TODO: connect this agent to your LLM/framework.
|
|
163
|
+
return f"[{{AGENT_NAME}}] Completed: {task}"
|
|
164
|
+
""",
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
ORCHESTRATOR_TEMPLATES = {
|
|
168
|
+
"basic": """from __future__ import annotations
|
|
169
|
+
|
|
170
|
+
import logging
|
|
171
|
+
|
|
172
|
+
logger = logging.getLogger(__name__)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class AgentExecutionError(RuntimeError):
|
|
176
|
+
\"\"\"Raised when an agent fails during orchestration.\"\"\"
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class Orchestrator:
|
|
180
|
+
\"\"\"Simple sequential multi-agent orchestrator.\"\"\"
|
|
181
|
+
|
|
182
|
+
def __init__(self, agents):
|
|
183
|
+
self.agents = list(agents)
|
|
184
|
+
|
|
185
|
+
def run(self, task: str) -> str:
|
|
186
|
+
current = task
|
|
187
|
+
|
|
188
|
+
for agent in self.agents:
|
|
189
|
+
logger.info("Running agent: %s", agent.name)
|
|
190
|
+
|
|
191
|
+
try:
|
|
192
|
+
current = agent.run(current)
|
|
193
|
+
except Exception as exc:
|
|
194
|
+
logger.exception("Agent '%s' failed", agent.name)
|
|
195
|
+
raise AgentExecutionError(f"Agent '{agent.name}' failed: {exc}") from exc
|
|
196
|
+
|
|
197
|
+
return current
|
|
198
|
+
""",
|
|
199
|
+
"langgraph": """from __future__ import annotations
|
|
200
|
+
|
|
201
|
+
import logging
|
|
202
|
+
|
|
203
|
+
from langgraph.graph import END, START, StateGraph
|
|
204
|
+
|
|
205
|
+
logger = logging.getLogger(__name__)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class AgentExecutionError(RuntimeError):
|
|
209
|
+
\"\"\"Raised when the LangGraph execution fails.\"\"\"
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class Orchestrator:
|
|
213
|
+
\"\"\"LangGraph-based sequential multi-agent orchestrator.\"\"\"
|
|
214
|
+
|
|
215
|
+
def __init__(self, agents):
|
|
216
|
+
self.agents = list(agents)
|
|
217
|
+
self.graph = self._build_graph()
|
|
218
|
+
|
|
219
|
+
def _build_graph(self):
|
|
220
|
+
graph = StateGraph(dict)
|
|
221
|
+
|
|
222
|
+
previous = START
|
|
223
|
+
for index, agent in enumerate(self.agents):
|
|
224
|
+
node_id = f"node_{index}"
|
|
225
|
+
graph.add_node(node_id, agent)
|
|
226
|
+
graph.add_edge(previous, node_id)
|
|
227
|
+
previous = node_id
|
|
228
|
+
|
|
229
|
+
graph.add_edge(previous, END)
|
|
230
|
+
|
|
231
|
+
return graph.compile()
|
|
232
|
+
|
|
233
|
+
def run(self, task: str) -> str:
|
|
234
|
+
logger.info("Invoking LangGraph with %d node(s)", len(self.agents))
|
|
235
|
+
|
|
236
|
+
try:
|
|
237
|
+
final_state = self.graph.invoke({"task": task, "result": ""})
|
|
238
|
+
except Exception as exc:
|
|
239
|
+
logger.exception("LangGraph execution failed")
|
|
240
|
+
raise AgentExecutionError(f"LangGraph execution failed: {exc}") from exc
|
|
241
|
+
|
|
242
|
+
return final_state["result"]
|
|
243
|
+
""",
|
|
244
|
+
"crewai": """from __future__ import annotations
|
|
245
|
+
|
|
246
|
+
import logging
|
|
247
|
+
|
|
248
|
+
from crewai import Crew, Task
|
|
249
|
+
|
|
250
|
+
logger = logging.getLogger(__name__)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
class AgentExecutionError(RuntimeError):
|
|
254
|
+
\"\"\"Raised when an agent fails during orchestration.\"\"\"
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
class Orchestrator:
|
|
258
|
+
\"\"\"CrewAI-based multi-agent orchestrator.\"\"\"
|
|
259
|
+
|
|
260
|
+
def __init__(self, agents):
|
|
261
|
+
self.agents = list(agents)
|
|
262
|
+
|
|
263
|
+
def build_crew(self, task: str) -> Crew:
|
|
264
|
+
\"\"\"Build a real CrewAI Crew for the given task.
|
|
265
|
+
|
|
266
|
+
Once your agents have an LLM configured, use
|
|
267
|
+
`orchestrator.build_crew(task).kickoff()` instead of `run()`.
|
|
268
|
+
\"\"\"
|
|
269
|
+
tasks = [
|
|
270
|
+
Task(
|
|
271
|
+
description=task,
|
|
272
|
+
agent=agent.agent,
|
|
273
|
+
expected_output="A completed response for the assigned step.",
|
|
274
|
+
)
|
|
275
|
+
for agent in self.agents
|
|
276
|
+
]
|
|
277
|
+
|
|
278
|
+
return Crew(agents=[agent.agent for agent in self.agents], tasks=tasks)
|
|
279
|
+
|
|
280
|
+
def run(self, task: str) -> str:
|
|
281
|
+
current = task
|
|
282
|
+
|
|
283
|
+
for agent in self.agents:
|
|
284
|
+
logger.info("Running agent: %s", agent.name)
|
|
285
|
+
|
|
286
|
+
try:
|
|
287
|
+
current = agent.run(current)
|
|
288
|
+
except Exception as exc:
|
|
289
|
+
logger.exception("Agent '%s' failed", agent.name)
|
|
290
|
+
raise AgentExecutionError(f"Agent '{agent.name}' failed: {exc}") from exc
|
|
291
|
+
|
|
292
|
+
return current
|
|
293
|
+
""",
|
|
294
|
+
"autogen": """from __future__ import annotations
|
|
295
|
+
|
|
296
|
+
import logging
|
|
297
|
+
|
|
298
|
+
from autogen_agentchat.teams import RoundRobinGroupChat
|
|
299
|
+
|
|
300
|
+
logger = logging.getLogger(__name__)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
class AgentExecutionError(RuntimeError):
|
|
304
|
+
\"\"\"Raised when an agent fails during orchestration.\"\"\"
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
class Orchestrator:
|
|
308
|
+
\"\"\"AutoGen-based multi-agent orchestrator.\"\"\"
|
|
309
|
+
|
|
310
|
+
def __init__(self, agents):
|
|
311
|
+
self.agents = list(agents)
|
|
312
|
+
|
|
313
|
+
def build_team(self, model_client) -> RoundRobinGroupChat:
|
|
314
|
+
\"\"\"Build a real AutoGen team once you have a model client.
|
|
315
|
+
|
|
316
|
+
Once your agents have an LLM configured, run the team with
|
|
317
|
+
`await team.run(task=...)` instead of `run()`.
|
|
318
|
+
\"\"\"
|
|
319
|
+
participants = [agent.build(model_client) for agent in self.agents]
|
|
320
|
+
|
|
321
|
+
return RoundRobinGroupChat(participants)
|
|
322
|
+
|
|
323
|
+
def run(self, task: str) -> str:
|
|
324
|
+
current = task
|
|
325
|
+
|
|
326
|
+
for agent in self.agents:
|
|
327
|
+
logger.info("Running agent: %s", agent.name)
|
|
328
|
+
|
|
329
|
+
try:
|
|
330
|
+
current = agent.run(current)
|
|
331
|
+
except Exception as exc:
|
|
332
|
+
logger.exception("Agent '%s' failed", agent.name)
|
|
333
|
+
raise AgentExecutionError(f"Agent '{agent.name}' failed: {exc}") from exc
|
|
334
|
+
|
|
335
|
+
return current
|
|
336
|
+
""",
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
TOOL_FILE = """class WebSearchTool:
|
|
340
|
+
\"\"\"Placeholder for a web-search integration.\"\"\"
|
|
341
|
+
|
|
342
|
+
def search(self, query: str) -> str:
|
|
343
|
+
return f"Search provider placeholder: {query}"
|
|
344
|
+
"""
|
|
345
|
+
|
|
346
|
+
TEST_TEMPLATE = """import unittest
|
|
347
|
+
|
|
348
|
+
{{TEST_IMPORTS}}
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
class AgentTests(unittest.TestCase):
|
|
352
|
+
def test_agents_return_output(self):
|
|
353
|
+
task = "test task"
|
|
354
|
+
{{ASSERTIONS}}
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
if __name__ == "__main__":
|
|
358
|
+
unittest.main()
|
|
359
|
+
"""
|
|
360
|
+
|
|
361
|
+
DOCKER_FILE = """FROM python:3.12-slim
|
|
362
|
+
|
|
363
|
+
WORKDIR /app
|
|
364
|
+
|
|
365
|
+
COPY . .
|
|
366
|
+
|
|
367
|
+
RUN pip install --no-cache-dir .
|
|
368
|
+
|
|
369
|
+
CMD ["python", "-m", "{{PACKAGE_NAME}}"]
|
|
370
|
+
"""
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
import questionary
|
|
6
|
+
from questionary import Style
|
|
7
|
+
|
|
8
|
+
NAME_PATTERN = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_-]*$")
|
|
9
|
+
AGENT_PATTERN = re.compile(r"^[A-Za-z][A-Za-z0-9 _-]*$")
|
|
10
|
+
|
|
11
|
+
STYLE = Style(
|
|
12
|
+
[
|
|
13
|
+
("qmark", "fg:cyan bold"),
|
|
14
|
+
("question", "bold"),
|
|
15
|
+
("answer", "fg:cyan"),
|
|
16
|
+
("pointer", "fg:cyan bold"),
|
|
17
|
+
("highlighted", "fg:cyan bold"),
|
|
18
|
+
]
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _valid_project_name(value: str) -> bool:
|
|
23
|
+
return bool(NAME_PATTERN.fullmatch(value.strip()))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _valid_agent_name(value: str) -> bool:
|
|
27
|
+
return bool(AGENT_PATTERN.fullmatch(value.strip()))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _ask_agent_names(count: int) -> list[str]:
|
|
31
|
+
names = []
|
|
32
|
+
|
|
33
|
+
for index in range(1, count + 1):
|
|
34
|
+
name = questionary.text(
|
|
35
|
+
f"Agent {index} name:",
|
|
36
|
+
validate=lambda value: (
|
|
37
|
+
True
|
|
38
|
+
if _valid_agent_name(value)
|
|
39
|
+
else "Use letters, numbers, spaces, '-' or '_'."
|
|
40
|
+
),
|
|
41
|
+
style=STYLE,
|
|
42
|
+
).ask()
|
|
43
|
+
|
|
44
|
+
if name is None:
|
|
45
|
+
raise KeyboardInterrupt
|
|
46
|
+
|
|
47
|
+
names.append(name.strip())
|
|
48
|
+
|
|
49
|
+
return names
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def run_wizard() -> dict:
|
|
53
|
+
print()
|
|
54
|
+
print("🚀 MultiAgent Init")
|
|
55
|
+
print()
|
|
56
|
+
|
|
57
|
+
project_name = questionary.text(
|
|
58
|
+
"Project name:",
|
|
59
|
+
validate=lambda value: (
|
|
60
|
+
True
|
|
61
|
+
if _valid_project_name(value)
|
|
62
|
+
else "Use letters, numbers, '-' or '_' and start with a letter/number."
|
|
63
|
+
),
|
|
64
|
+
style=STYLE,
|
|
65
|
+
).ask()
|
|
66
|
+
|
|
67
|
+
if project_name is None:
|
|
68
|
+
raise KeyboardInterrupt
|
|
69
|
+
|
|
70
|
+
framework = questionary.select(
|
|
71
|
+
"Select a framework:",
|
|
72
|
+
choices=[
|
|
73
|
+
questionary.Choice("Basic", value="basic"),
|
|
74
|
+
questionary.Choice("LangGraph", value="langgraph"),
|
|
75
|
+
questionary.Choice("CrewAI", value="crewai"),
|
|
76
|
+
questionary.Choice("AutoGen", value="autogen"),
|
|
77
|
+
],
|
|
78
|
+
default="basic",
|
|
79
|
+
style=STYLE,
|
|
80
|
+
).ask()
|
|
81
|
+
|
|
82
|
+
provider = questionary.select(
|
|
83
|
+
"Select model provider:",
|
|
84
|
+
choices=[
|
|
85
|
+
questionary.Choice("OpenAI", value="openai"),
|
|
86
|
+
questionary.Choice("Anthropic", value="anthropic"),
|
|
87
|
+
questionary.Choice("Google", value="google"),
|
|
88
|
+
],
|
|
89
|
+
default="openai",
|
|
90
|
+
style=STYLE,
|
|
91
|
+
).ask()
|
|
92
|
+
|
|
93
|
+
agent_count_choice = questionary.select(
|
|
94
|
+
"Number of agents:",
|
|
95
|
+
choices=["3", "2", "4", "5", "Custom"],
|
|
96
|
+
default="3",
|
|
97
|
+
style=STYLE,
|
|
98
|
+
).ask()
|
|
99
|
+
|
|
100
|
+
if agent_count_choice == "Custom":
|
|
101
|
+
agent_count = questionary.text(
|
|
102
|
+
"How many agents?",
|
|
103
|
+
default="6",
|
|
104
|
+
validate=lambda value: (
|
|
105
|
+
True
|
|
106
|
+
if value.isdigit() and 1 <= int(value) <= 50
|
|
107
|
+
else "Enter a number between 1 and 50."
|
|
108
|
+
),
|
|
109
|
+
style=STYLE,
|
|
110
|
+
).ask()
|
|
111
|
+
|
|
112
|
+
if agent_count is None:
|
|
113
|
+
raise KeyboardInterrupt
|
|
114
|
+
|
|
115
|
+
agent_count = int(agent_count)
|
|
116
|
+
else:
|
|
117
|
+
agent_count = int(agent_count_choice)
|
|
118
|
+
|
|
119
|
+
agent_mode = questionary.select(
|
|
120
|
+
"Agent configuration:",
|
|
121
|
+
choices=[
|
|
122
|
+
questionary.Choice("Default agents", value="default"),
|
|
123
|
+
questionary.Choice("Custom agents", value="custom"),
|
|
124
|
+
],
|
|
125
|
+
default="default",
|
|
126
|
+
style=STYLE,
|
|
127
|
+
).ask()
|
|
128
|
+
|
|
129
|
+
default_names = [
|
|
130
|
+
"Researcher",
|
|
131
|
+
"Writer",
|
|
132
|
+
"Reviewer",
|
|
133
|
+
"Planner",
|
|
134
|
+
"Executor",
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
if agent_mode == "default":
|
|
138
|
+
if agent_count <= len(default_names):
|
|
139
|
+
agent_names = default_names[:agent_count]
|
|
140
|
+
else:
|
|
141
|
+
agent_names = default_names[:]
|
|
142
|
+
for index in range(len(default_names) + 1, agent_count + 1):
|
|
143
|
+
agent_names.append(f"Agent {index}")
|
|
144
|
+
else:
|
|
145
|
+
agent_names = _ask_agent_names(agent_count)
|
|
146
|
+
|
|
147
|
+
include_tools = questionary.confirm(
|
|
148
|
+
"Include tools?",
|
|
149
|
+
default=True,
|
|
150
|
+
style=STYLE,
|
|
151
|
+
).ask()
|
|
152
|
+
|
|
153
|
+
include_tests = questionary.confirm(
|
|
154
|
+
"Include tests?",
|
|
155
|
+
default=True,
|
|
156
|
+
style=STYLE,
|
|
157
|
+
).ask()
|
|
158
|
+
|
|
159
|
+
include_docker = questionary.confirm(
|
|
160
|
+
"Include Docker?",
|
|
161
|
+
default=False,
|
|
162
|
+
style=STYLE,
|
|
163
|
+
).ask()
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
"project_name": project_name.strip(),
|
|
167
|
+
"framework": framework,
|
|
168
|
+
"provider": provider,
|
|
169
|
+
"agent_count": agent_count,
|
|
170
|
+
"agent_mode": agent_mode,
|
|
171
|
+
"agent_names": agent_names,
|
|
172
|
+
"include_tools": include_tools,
|
|
173
|
+
"include_tests": include_tests,
|
|
174
|
+
"include_docker": include_docker,
|
|
175
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: multiagent-init
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Interactive CLI for generating multi-agent AI project boilerplates.
|
|
5
|
+
Author-email: Pavan Sekhar Mandavilli <pavanmandavilli2004@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Pavanmandavilli/multiagent-init
|
|
8
|
+
Project-URL: Repository, https://github.com/Pavanmandavilli/multiagent-init
|
|
9
|
+
Project-URL: Issues, https://github.com/Pavanmandavilli/multiagent-init/issues
|
|
10
|
+
Keywords: multi-agent,agents,ai,scaffolding,cli,generator
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: questionary<3.0.0,>=2.0.0
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
22
|
+
# 🚀 MultiAgent Init
|
|
23
|
+
|
|
24
|
+
Interactive CLI for generating a clean multi-agent AI project.
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install multiagent-init
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Run
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
multiagent-init
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Wizard
|
|
39
|
+
|
|
40
|
+
The CLI asks for:
|
|
41
|
+
|
|
42
|
+
1. Project name
|
|
43
|
+
2. Framework
|
|
44
|
+
3. Model provider
|
|
45
|
+
4. Number of agents
|
|
46
|
+
5. Default or custom agents
|
|
47
|
+
6. Optional tools
|
|
48
|
+
7. Optional tests
|
|
49
|
+
8. Optional Docker
|
|
50
|
+
|
|
51
|
+
### Example
|
|
52
|
+
|
|
53
|
+
```text
|
|
54
|
+
🚀 MultiAgent Init
|
|
55
|
+
|
|
56
|
+
? Project name: research-team
|
|
57
|
+
|
|
58
|
+
? Select a framework:
|
|
59
|
+
❯ Basic
|
|
60
|
+
LangGraph
|
|
61
|
+
CrewAI
|
|
62
|
+
AutoGen
|
|
63
|
+
|
|
64
|
+
? Select model provider:
|
|
65
|
+
❯ OpenAI
|
|
66
|
+
Anthropic
|
|
67
|
+
Google
|
|
68
|
+
|
|
69
|
+
? Number of agents:
|
|
70
|
+
❯ 3
|
|
71
|
+
2
|
|
72
|
+
4
|
|
73
|
+
5
|
|
74
|
+
Custom
|
|
75
|
+
|
|
76
|
+
? Agent configuration:
|
|
77
|
+
❯ Default agents
|
|
78
|
+
Custom agents
|
|
79
|
+
|
|
80
|
+
? Agent 1 name: Market Researcher
|
|
81
|
+
? Agent 2 name: Data Analyst
|
|
82
|
+
? Agent 3 name: Report Writer
|
|
83
|
+
|
|
84
|
+
? Include tools? Yes
|
|
85
|
+
? Include tests? Yes
|
|
86
|
+
? Include Docker? No
|
|
87
|
+
|
|
88
|
+
Creating project...
|
|
89
|
+
|
|
90
|
+
✓ Agents
|
|
91
|
+
✓ Workflow
|
|
92
|
+
✓ Tools
|
|
93
|
+
✓ Configuration
|
|
94
|
+
✓ Tests
|
|
95
|
+
✓ README
|
|
96
|
+
|
|
97
|
+
🎉 Done!
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Generated project
|
|
101
|
+
|
|
102
|
+
Custom agent names are converted into safe Python filenames and class names.
|
|
103
|
+
|
|
104
|
+
Example:
|
|
105
|
+
|
|
106
|
+
```text
|
|
107
|
+
Market Researcher → market_researcher.py → MarketResearcherAgent
|
|
108
|
+
Data Analyst → data_analyst.py → DataAnalystAgent
|
|
109
|
+
Report Writer → report_writer.py → ReportWriterAgent
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Development
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
python3 -m venv .venv
|
|
116
|
+
source .venv/bin/activate
|
|
117
|
+
python -m pip install -e .
|
|
118
|
+
python -m unittest discover -s tests -v
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Build:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
python -m pip install --upgrade build
|
|
125
|
+
python -m build
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Publish
|
|
129
|
+
|
|
130
|
+
TestPyPI:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
python -m pip install --upgrade twine
|
|
134
|
+
python -m twine upload --repository testpypi dist/*
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
PyPI:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
python -m twine upload dist/*
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
For CI/CD, configure PyPI Trusted Publishing with GitHub Actions.
|
|
144
|
+
|
|
145
|
+
## License
|
|
146
|
+
|
|
147
|
+
MIT
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
multiagent_init/__init__.py,sha256=kUR5RAFc7HCeiqdlX36dZOHkUI5wI6V_43RpEcD8b-0,22
|
|
2
|
+
multiagent_init/cli.py,sha256=KSCkKerzzimHHFErQLfy2bFYv9x5VDDyyO6ki0vT9Fg,842
|
|
3
|
+
multiagent_init/generator.py,sha256=1AONIxfB9dYToaEcJqJKdpQoFetQGsmF_RnfQTsOSJs,7726
|
|
4
|
+
multiagent_init/templates_data.py,sha256=qRWw9vVceVjkt9QVcihFA4nvJ1d4KYDEUkD2myhwBFk,9468
|
|
5
|
+
multiagent_init/wizard.py,sha256=3d3YA9e1MFIf00KdbOlA9w-kYvBEYPsipZUDzAhClRc,4527
|
|
6
|
+
multiagent_init-0.1.0.dist-info/licenses/LICENSE,sha256=qAWvkf1ylseZfzIyN09FEw7XxS6MDeJ7ZiL1qH5fVqA,1081
|
|
7
|
+
multiagent_init-0.1.0.dist-info/METADATA,sha256=6dHalPlVeoBdroiWLRgzm6zeROkd5o2h4MeAEi6wBzc,2628
|
|
8
|
+
multiagent_init-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
9
|
+
multiagent_init-0.1.0.dist-info/entry_points.txt,sha256=-Yzt8FY_8k0fuLS7zS3oqEvmLX-qqVQYXUKL42mJjsY,61
|
|
10
|
+
multiagent_init-0.1.0.dist-info/top_level.txt,sha256=rLfcUGuLZ-VSZxeFphJKtzsXupC2ZObn5b0TEJrAwJI,16
|
|
11
|
+
multiagent_init-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mandavilli. Pavan sekhar
|
|
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.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
multiagent_init
|