naas-abi 1.0.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.
- naas_abi/__init__.py +35 -0
- naas_abi/agents/AbiAgent.py +442 -0
- naas_abi/agents/AbiAgent_test.py +157 -0
- naas_abi/agents/EntitytoSPARQLAgent.py +952 -0
- naas_abi/agents/EntitytoSPARQLAgent_test.py +66 -0
- naas_abi/agents/KnowledgeGraphBuilderAgent.py +321 -0
- naas_abi/agents/KnowledgeGraphBuilderAgent_test.py +86 -0
- naas_abi/agents/OntologyEngineerAgent.py +115 -0
- naas_abi/agents/OntologyEngineerAgent_test.py +42 -0
- naas_abi/apps/oxigraph_admin/main.py +392 -0
- naas_abi/apps/oxigraph_admin/terminal_style.py +151 -0
- naas_abi/apps/sparql_terminal/main.py +68 -0
- naas_abi/apps/sparql_terminal/terminal_style.py +236 -0
- naas_abi/apps/terminal_agent/main.py +553 -0
- naas_abi/apps/terminal_agent/terminal_style.py +175 -0
- naas_abi/cli.py +714 -0
- naas_abi/mappings.py +83 -0
- naas_abi/models/airgap_gemma.py +220 -0
- naas_abi/models/airgap_qwen.py +24 -0
- naas_abi/models/default.py +23 -0
- naas_abi/models/gpt_4_1.py +25 -0
- naas_abi/pipelines/AIAgentOntologyGenerationPipeline.py +635 -0
- naas_abi/pipelines/AIAgentOntologyGenerationPipeline_test.py +133 -0
- naas_abi/pipelines/AddIndividualPipeline.py +215 -0
- naas_abi/pipelines/AddIndividualPipeline_test.py +66 -0
- naas_abi/pipelines/InsertDataSPARQLPipeline.py +197 -0
- naas_abi/pipelines/InsertDataSPARQLPipeline_test.py +96 -0
- naas_abi/pipelines/MergeIndividualsPipeline.py +245 -0
- naas_abi/pipelines/MergeIndividualsPipeline_test.py +98 -0
- naas_abi/pipelines/RemoveIndividualPipeline.py +166 -0
- naas_abi/pipelines/RemoveIndividualPipeline_test.py +58 -0
- naas_abi/pipelines/UpdateCommercialOrganizationPipeline.py +198 -0
- naas_abi/pipelines/UpdateDataPropertyPipeline.py +175 -0
- naas_abi/pipelines/UpdateLegalNamePipeline.py +107 -0
- naas_abi/pipelines/UpdateLinkedInPagePipeline.py +179 -0
- naas_abi/pipelines/UpdatePersonPipeline.py +184 -0
- naas_abi/pipelines/UpdateSkillPipeline.py +118 -0
- naas_abi/pipelines/UpdateTickerPipeline.py +104 -0
- naas_abi/pipelines/UpdateWebsitePipeline.py +106 -0
- naas_abi/triggers.py +131 -0
- naas_abi/workflows/AgentRecommendationWorkflow.py +321 -0
- naas_abi/workflows/AgentRecommendationWorkflow_test.py +160 -0
- naas_abi/workflows/ArtificialAnalysisWorkflow.py +337 -0
- naas_abi/workflows/ArtificialAnalysisWorkflow_test.py +57 -0
- naas_abi/workflows/ConvertOntologyGraphToYamlWorkflow.py +210 -0
- naas_abi/workflows/ConvertOntologyGraphToYamlWorkflow_test.py +78 -0
- naas_abi/workflows/CreateClassOntologyYamlWorkflow.py +208 -0
- naas_abi/workflows/CreateClassOntologyYamlWorkflow_test.py +65 -0
- naas_abi/workflows/CreateIndividualOntologyYamlWorkflow.py +183 -0
- naas_abi/workflows/CreateIndividualOntologyYamlWorkflow_test.py +86 -0
- naas_abi/workflows/ExportGraphInstancesToExcelWorkflow.py +450 -0
- naas_abi/workflows/ExportGraphInstancesToExcelWorkflow_test.py +33 -0
- naas_abi/workflows/GetObjectPropertiesFromClassWorkflow.py +385 -0
- naas_abi/workflows/GetObjectPropertiesFromClassWorkflow_test.py +57 -0
- naas_abi/workflows/GetSubjectGraphWorkflow.py +84 -0
- naas_abi/workflows/GetSubjectGraphWorkflow_test.py +71 -0
- naas_abi/workflows/SearchIndividualWorkflow.py +190 -0
- naas_abi/workflows/SearchIndividualWorkflow_test.py +98 -0
- naas_abi-1.0.0.dist-info/METADATA +9 -0
- naas_abi-1.0.0.dist-info/RECORD +62 -0
- naas_abi-1.0.0.dist-info/WHEEL +5 -0
- naas_abi-1.0.0.dist-info/top_level.txt +1 -0
naas_abi/cli.py
ADDED
|
@@ -0,0 +1,714 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ABI CLI Commands Module
|
|
3
|
+
|
|
4
|
+
This module provides command-line interface commands for creating new modules and agents.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import re
|
|
9
|
+
import shutil
|
|
10
|
+
|
|
11
|
+
import dotenv
|
|
12
|
+
import yaml
|
|
13
|
+
from rich.console import Console
|
|
14
|
+
from rich.prompt import Prompt
|
|
15
|
+
|
|
16
|
+
console = Console(style="")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def format_module_name(name: str) -> str:
|
|
20
|
+
"""Format module name to lowercase with proper formatting."""
|
|
21
|
+
# Convert to lowercase
|
|
22
|
+
formatted = name.lower()
|
|
23
|
+
# Replace spaces with underscores
|
|
24
|
+
formatted = formatted.replace(" ", "_")
|
|
25
|
+
# Replace dots with underscores
|
|
26
|
+
formatted = formatted.replace(".", "_")
|
|
27
|
+
# Remove any other special characters except letters, numbers, and underscores
|
|
28
|
+
formatted = re.sub(r"[^a-z0-9_]", "", formatted)
|
|
29
|
+
# Remove multiple consecutive underscores
|
|
30
|
+
formatted = re.sub(r"_+", "_", formatted)
|
|
31
|
+
# Remove leading/trailing underscores
|
|
32
|
+
formatted = formatted.strip("_")
|
|
33
|
+
return formatted
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def get_component_selection():
|
|
37
|
+
"""Get user selection for which components to include in the module."""
|
|
38
|
+
console.print(
|
|
39
|
+
"\nš§ Which components would you like to include?\n", style="bright_blue"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# List all available components
|
|
43
|
+
console.print("Available components:", style="cyan")
|
|
44
|
+
component_descriptions = {
|
|
45
|
+
"agents": "AI agents for conversational interfaces (includes models)",
|
|
46
|
+
"integrations": "External service integrations and API wrappers",
|
|
47
|
+
"workflows": "Business logic workflows and processes",
|
|
48
|
+
"pipelines": "Data processing and transformation pipelines",
|
|
49
|
+
"ontologies": "Semantic ontologies and SPARQL queries",
|
|
50
|
+
"orchestrations": "Dagster orchestration definitions",
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
for component, description in component_descriptions.items():
|
|
54
|
+
console.print(f"⢠{component}: {description}", style="dim")
|
|
55
|
+
|
|
56
|
+
console.print() # Empty line for spacing
|
|
57
|
+
|
|
58
|
+
# Ask if they want all components
|
|
59
|
+
include_all = Prompt.ask(
|
|
60
|
+
"Include all template components?", choices=["y", "n"], default="y"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
if include_all == "y":
|
|
64
|
+
return {
|
|
65
|
+
"agents": True,
|
|
66
|
+
"integrations": True,
|
|
67
|
+
"workflows": True,
|
|
68
|
+
"pipelines": True,
|
|
69
|
+
"ontologies": True,
|
|
70
|
+
"models": True,
|
|
71
|
+
"orchestrations": True,
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
# Individual component selection
|
|
75
|
+
console.print("Select components to include:", style="cyan")
|
|
76
|
+
components = {}
|
|
77
|
+
|
|
78
|
+
for component in component_descriptions.keys():
|
|
79
|
+
choice = Prompt.ask(f"Include {component}?", choices=["y", "n"], default="y")
|
|
80
|
+
components[component] = choice == "y"
|
|
81
|
+
|
|
82
|
+
# Models are automatically included if agents are selected
|
|
83
|
+
components["models"] = components.get("agents", False)
|
|
84
|
+
|
|
85
|
+
if components.get("agents", False):
|
|
86
|
+
console.print(
|
|
87
|
+
"š Models component automatically included (required for agents)",
|
|
88
|
+
style="yellow",
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
return components
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def enable_module_in_config(module_path: str):
|
|
95
|
+
"""Enable the module in config files if they exist."""
|
|
96
|
+
dotenv.load_dotenv()
|
|
97
|
+
|
|
98
|
+
env = os.getenv("ENV")
|
|
99
|
+
|
|
100
|
+
config_files = [f"config.{env}.yaml"]
|
|
101
|
+
|
|
102
|
+
for config_file in config_files:
|
|
103
|
+
if os.path.exists(config_file):
|
|
104
|
+
try:
|
|
105
|
+
console.print(f"š Updating {config_file}...", style="yellow")
|
|
106
|
+
|
|
107
|
+
with open(config_file, "r", encoding="utf-8") as f:
|
|
108
|
+
config = yaml.safe_load(f) or {}
|
|
109
|
+
|
|
110
|
+
# Ensure modules section exists
|
|
111
|
+
if "modules" not in config:
|
|
112
|
+
config["modules"] = []
|
|
113
|
+
|
|
114
|
+
# Check if module already exists
|
|
115
|
+
module_exists = False
|
|
116
|
+
for module in config["modules"]:
|
|
117
|
+
if module.get("path") == module_path:
|
|
118
|
+
module["enabled"] = True
|
|
119
|
+
module_exists = True
|
|
120
|
+
break
|
|
121
|
+
|
|
122
|
+
# Add module if it doesn't exist
|
|
123
|
+
if not module_exists:
|
|
124
|
+
config["modules"].append({"path": module_path, "enabled": True})
|
|
125
|
+
|
|
126
|
+
# Sort modules by path for consistency
|
|
127
|
+
config["modules"] = sorted(
|
|
128
|
+
config["modules"], key=lambda x: x.get("path", "")
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
with open(config_file, "w", encoding="utf-8") as f:
|
|
132
|
+
yaml.dump(config, f, default_flow_style=False, sort_keys=False)
|
|
133
|
+
|
|
134
|
+
console.print(f"ā
Module enabled in {config_file}", style="green")
|
|
135
|
+
|
|
136
|
+
except Exception as e:
|
|
137
|
+
console.print(f"ā ļø Could not update {config_file}: {e}", style="yellow")
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def create_new_module():
|
|
141
|
+
"""Create a new module by duplicating the __templates__ folder."""
|
|
142
|
+
console.print("š Creating a new module...\n", style="bright_cyan")
|
|
143
|
+
|
|
144
|
+
# Get module name and validate format
|
|
145
|
+
while True:
|
|
146
|
+
raw_module_name = Prompt.ask("What is the module name?")
|
|
147
|
+
if not raw_module_name.strip():
|
|
148
|
+
console.print("ā Module name cannot be empty.", style="red")
|
|
149
|
+
continue
|
|
150
|
+
|
|
151
|
+
module_name = format_module_name(raw_module_name)
|
|
152
|
+
if not module_name or not re.match(r"^[a-z][a-z0-9_\-\.]*$", module_name):
|
|
153
|
+
console.print(
|
|
154
|
+
"ā Invalid module name. Must start with a lowercase letter and can contain letters, numbers, underscores, dots, or hyphens.",
|
|
155
|
+
style="red",
|
|
156
|
+
)
|
|
157
|
+
continue
|
|
158
|
+
|
|
159
|
+
if raw_module_name != module_name:
|
|
160
|
+
console.print(f"š Module name formatted as: {module_name}", style="yellow")
|
|
161
|
+
break
|
|
162
|
+
|
|
163
|
+
# Get target path with explanations
|
|
164
|
+
console.print(
|
|
165
|
+
"\nWhere would you like to create this module?\n", style="bright_blue"
|
|
166
|
+
)
|
|
167
|
+
console.print(
|
|
168
|
+
"1. src/core - Core ABI functionality and essential modules", style="cyan"
|
|
169
|
+
)
|
|
170
|
+
console.print(" ⢠Built-in ABI features", style="dim")
|
|
171
|
+
console.print(" ⢠System-level integrations", style="dim")
|
|
172
|
+
console.print(" ⢠Foundation components\n", style="dim")
|
|
173
|
+
|
|
174
|
+
console.print("2. src/custom - Organization-specific implementations", style="cyan")
|
|
175
|
+
console.print(" ⢠Company-specific adaptations", style="dim")
|
|
176
|
+
console.print(" ⢠Private customizations", style="dim")
|
|
177
|
+
console.print(" ⢠Internal tools and workflows\n", style="dim")
|
|
178
|
+
|
|
179
|
+
console.print(
|
|
180
|
+
"3. src/marketplace/applications - Third-party service integrations",
|
|
181
|
+
style="cyan",
|
|
182
|
+
)
|
|
183
|
+
console.print(" ⢠External API integrations", style="dim")
|
|
184
|
+
console.print(" ⢠SaaS platform connectors", style="dim")
|
|
185
|
+
console.print(" ⢠Public service wrappers\n", style="dim")
|
|
186
|
+
|
|
187
|
+
console.print(
|
|
188
|
+
"4. src/marketplace/domains - Business domain specialists", style="cyan"
|
|
189
|
+
)
|
|
190
|
+
console.print(" ⢠Role-based agents (HR, Sales, etc.)", style="dim")
|
|
191
|
+
console.print(" ⢠Industry-specific solutions", style="dim")
|
|
192
|
+
console.print(" ⢠Professional domain experts\n", style="dim")
|
|
193
|
+
|
|
194
|
+
path_choice = Prompt.ask(
|
|
195
|
+
"Choose location", choices=["1", "2", "3", "4"], default="1"
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
path_mapping = {
|
|
199
|
+
"1": "src/core",
|
|
200
|
+
"2": "src/custom",
|
|
201
|
+
"3": "src/marketplace/applications",
|
|
202
|
+
"4": "src/marketplace/domains",
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
target_base_path = path_mapping[path_choice]
|
|
206
|
+
target_path = os.path.join(target_base_path, module_name)
|
|
207
|
+
|
|
208
|
+
# Check if target path already exists
|
|
209
|
+
if os.path.exists(target_path):
|
|
210
|
+
console.print(f"ā Module already exists at {target_path}", style="red")
|
|
211
|
+
return
|
|
212
|
+
|
|
213
|
+
# Get component selection
|
|
214
|
+
selected_components = get_component_selection()
|
|
215
|
+
|
|
216
|
+
# Create the module directory
|
|
217
|
+
console.print(f"\nš Creating module at {target_path}...", style="green")
|
|
218
|
+
|
|
219
|
+
try:
|
|
220
|
+
# Create base module directory
|
|
221
|
+
os.makedirs(target_path, exist_ok=True)
|
|
222
|
+
|
|
223
|
+
# Copy selected components from template
|
|
224
|
+
template_path = "src/core/__templates__"
|
|
225
|
+
|
|
226
|
+
# Always copy base files
|
|
227
|
+
base_files = ["__init__.py", "README.md"]
|
|
228
|
+
for file in base_files:
|
|
229
|
+
source_file = os.path.join(template_path, file)
|
|
230
|
+
if os.path.exists(source_file):
|
|
231
|
+
target_file = os.path.join(target_path, file)
|
|
232
|
+
shutil.copy2(source_file, target_file)
|
|
233
|
+
|
|
234
|
+
# Copy selected component directories
|
|
235
|
+
for component, include in selected_components.items():
|
|
236
|
+
if include:
|
|
237
|
+
source_component_path = os.path.join(template_path, component)
|
|
238
|
+
target_component_path = os.path.join(target_path, component)
|
|
239
|
+
|
|
240
|
+
if os.path.exists(source_component_path):
|
|
241
|
+
console.print(f"š¦ Adding {component} component...", style="dim")
|
|
242
|
+
shutil.copytree(source_component_path, target_component_path)
|
|
243
|
+
|
|
244
|
+
# Update all file contents with module name replacements
|
|
245
|
+
console.print("š Updating template references...", style="yellow")
|
|
246
|
+
|
|
247
|
+
for root, dirs, files in os.walk(target_path):
|
|
248
|
+
for file in files:
|
|
249
|
+
if file.endswith((".py", ".md", ".ttl")):
|
|
250
|
+
file_path = os.path.join(root, file)
|
|
251
|
+
|
|
252
|
+
# Read file content
|
|
253
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
|
254
|
+
content = f.read()
|
|
255
|
+
|
|
256
|
+
# Replace template references
|
|
257
|
+
content = content.replace(
|
|
258
|
+
"Template", module_name.replace("_", "").title()
|
|
259
|
+
)
|
|
260
|
+
content = content.replace(
|
|
261
|
+
template_path.replace("\\", "/").replace("/", "."),
|
|
262
|
+
target_path.replace("\\", "/").replace("/", "."),
|
|
263
|
+
)
|
|
264
|
+
content = content.replace("__templates__", module_name)
|
|
265
|
+
content = content.replace(
|
|
266
|
+
"template", module_name.replace("_", "").lower()
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
# Update path references
|
|
270
|
+
old_path_pattern = "src/core/__templates__"
|
|
271
|
+
new_path_pattern = target_path.replace("\\", "/")
|
|
272
|
+
content = content.replace(old_path_pattern, new_path_pattern)
|
|
273
|
+
|
|
274
|
+
# Write updated content
|
|
275
|
+
with open(file_path, "w", encoding="utf-8") as f:
|
|
276
|
+
f.write(content)
|
|
277
|
+
|
|
278
|
+
# Rename files that contain "Template" in their names
|
|
279
|
+
for root, dirs, files in os.walk(target_path):
|
|
280
|
+
for file in files:
|
|
281
|
+
if "Template" in file:
|
|
282
|
+
old_file_path = os.path.join(root, file)
|
|
283
|
+
new_file_name = file.replace(
|
|
284
|
+
"Template", module_name.replace("_", "").title()
|
|
285
|
+
)
|
|
286
|
+
new_file_path = os.path.join(root, new_file_name)
|
|
287
|
+
os.rename(old_file_path, new_file_path)
|
|
288
|
+
|
|
289
|
+
# Enable module in config files
|
|
290
|
+
module_config_path = target_path.replace("\\", "/")
|
|
291
|
+
enable_module_in_config(module_config_path)
|
|
292
|
+
|
|
293
|
+
console.print(
|
|
294
|
+
f"ā
Module '{module_name}' created successfully at {target_path}",
|
|
295
|
+
style="bright_green",
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
# Show what was included
|
|
299
|
+
included_components = [
|
|
300
|
+
comp for comp, include in selected_components.items() if include
|
|
301
|
+
]
|
|
302
|
+
if included_components:
|
|
303
|
+
console.print(
|
|
304
|
+
f"\nš¦ Included components: {', '.join(included_components)}",
|
|
305
|
+
style="cyan",
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
console.print("\nš Next steps:", style="bright_blue")
|
|
309
|
+
console.print(
|
|
310
|
+
f"1. Edit {target_path}/README.md to describe your module", style="dim"
|
|
311
|
+
)
|
|
312
|
+
console.print(
|
|
313
|
+
f"2. Configure your module requirements in {target_path}/__init__.py",
|
|
314
|
+
style="dim",
|
|
315
|
+
)
|
|
316
|
+
console.print(
|
|
317
|
+
"3. Implement your components based on the templates", style="dim"
|
|
318
|
+
)
|
|
319
|
+
console.print(
|
|
320
|
+
"4. Module has been automatically enabled in config files", style="dim"
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
except Exception as e:
|
|
324
|
+
console.print(f"ā Error creating module: {e}", style="red")
|
|
325
|
+
# Clean up partial creation
|
|
326
|
+
if os.path.exists(target_path):
|
|
327
|
+
shutil.rmtree(target_path)
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def create_agent():
|
|
331
|
+
"""Create a new agent by duplicating TemplateAgent files."""
|
|
332
|
+
console.print("š¤ Creating a new agent...\n", style="bright_cyan")
|
|
333
|
+
|
|
334
|
+
# Get agent name
|
|
335
|
+
while True:
|
|
336
|
+
agent_name = Prompt.ask("What is the agent name?")
|
|
337
|
+
if re.match(r"^[a-zA-Z][a-zA-Z0-9_]*$", agent_name):
|
|
338
|
+
break
|
|
339
|
+
console.print(
|
|
340
|
+
"ā Invalid agent name. Use only letters, numbers, and underscores. Must start with a letter.",
|
|
341
|
+
style="red",
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
# Get target path
|
|
345
|
+
target_path = Prompt.ask(
|
|
346
|
+
"Where should the agent be created? (e.g., src/core/mymodule/agents)"
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
# Validate path exists
|
|
350
|
+
if not os.path.exists(target_path):
|
|
351
|
+
create_path = Prompt.ask(
|
|
352
|
+
f"Path {target_path} doesn't exist. Create it?",
|
|
353
|
+
choices=["y", "n"],
|
|
354
|
+
default="y",
|
|
355
|
+
)
|
|
356
|
+
if create_path == "y":
|
|
357
|
+
try:
|
|
358
|
+
os.makedirs(target_path, exist_ok=True)
|
|
359
|
+
console.print(f"š Created directory {target_path}", style="green")
|
|
360
|
+
except Exception as e:
|
|
361
|
+
console.print(f"ā Error creating directory: {e}", style="red")
|
|
362
|
+
return
|
|
363
|
+
else:
|
|
364
|
+
console.print("ā Agent creation cancelled", style="red")
|
|
365
|
+
return
|
|
366
|
+
|
|
367
|
+
# Define source and target files
|
|
368
|
+
template_agent_file = "src/core/__templates__/agents/TemplateAgent.py"
|
|
369
|
+
template_test_file = "src/core/__templates__/agents/TemplateAgent_test.py"
|
|
370
|
+
|
|
371
|
+
target_agent_file = os.path.join(target_path, f"{agent_name}Agent.py")
|
|
372
|
+
target_test_file = os.path.join(target_path, f"{agent_name}Agent_test.py")
|
|
373
|
+
|
|
374
|
+
# Check if files already exist
|
|
375
|
+
if os.path.exists(target_agent_file) or os.path.exists(target_test_file):
|
|
376
|
+
console.print(f"ā Agent files already exist at {target_path}", style="red")
|
|
377
|
+
return
|
|
378
|
+
|
|
379
|
+
try:
|
|
380
|
+
console.print(f"š Creating {agent_name}Agent.py...", style="yellow")
|
|
381
|
+
|
|
382
|
+
# Copy and update agent file
|
|
383
|
+
with open(template_agent_file, "r", encoding="utf-8") as f:
|
|
384
|
+
agent_content = f.read()
|
|
385
|
+
|
|
386
|
+
# Replace Template with agent name
|
|
387
|
+
agent_content = agent_content.replace("Template", agent_name)
|
|
388
|
+
agent_content = agent_content.replace("template", agent_name.lower())
|
|
389
|
+
|
|
390
|
+
# Update import paths if needed
|
|
391
|
+
relative_path = (
|
|
392
|
+
os.path.relpath(target_path, "src").replace("\\", "/").replace("/", ".")
|
|
393
|
+
)
|
|
394
|
+
module_path = relative_path.replace(".agents", "")
|
|
395
|
+
|
|
396
|
+
if relative_path != "core.__templates__.agents":
|
|
397
|
+
agent_content = agent_content.replace(
|
|
398
|
+
"from naas_abi.core.__templates__.models.gpt_4_1 import model as cloud_model",
|
|
399
|
+
f"from naas_abi.{module_path}.models.gpt_4_1 import model as cloud_model",
|
|
400
|
+
)
|
|
401
|
+
agent_content = agent_content.replace(
|
|
402
|
+
"from naas_abi.core.__templates__.models.qwen3_8b import model as local_model",
|
|
403
|
+
f"from naas_abi.{module_path}.models.qwen3_8b import model as local_model",
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
with open(target_agent_file, "w", encoding="utf-8") as f:
|
|
407
|
+
f.write(agent_content)
|
|
408
|
+
|
|
409
|
+
console.print(f"š Creating {agent_name}Agent_test.py...", style="yellow")
|
|
410
|
+
|
|
411
|
+
# Copy and update test file
|
|
412
|
+
with open(template_test_file, "r", encoding="utf-8") as f:
|
|
413
|
+
test_content = f.read()
|
|
414
|
+
|
|
415
|
+
# Replace Template with agent name and update import path
|
|
416
|
+
test_content = test_content.replace("Template", agent_name)
|
|
417
|
+
test_content = test_content.replace("template", agent_name.lower())
|
|
418
|
+
|
|
419
|
+
# Update import path
|
|
420
|
+
import_path = f"src.{relative_path}.{agent_name}Agent"
|
|
421
|
+
test_content = test_content.replace(
|
|
422
|
+
"from naas_abi.core.__templates__.agents.TemplateAgent import create_agent",
|
|
423
|
+
f"from {import_path} import create_agent",
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
with open(target_test_file, "w", encoding="utf-8") as f:
|
|
427
|
+
f.write(test_content)
|
|
428
|
+
|
|
429
|
+
# Check if models folder exists in the module, if not, create it
|
|
430
|
+
module_root_path = os.path.dirname(
|
|
431
|
+
target_path
|
|
432
|
+
) # Get parent directory (module root)
|
|
433
|
+
models_target_path = os.path.join(module_root_path, "models")
|
|
434
|
+
|
|
435
|
+
if not os.path.exists(models_target_path):
|
|
436
|
+
console.print(
|
|
437
|
+
"š¦ Creating models folder (required for agents)...", style="yellow"
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
# Copy models from template
|
|
441
|
+
template_models_path = "src/core/__templates__/models"
|
|
442
|
+
if os.path.exists(template_models_path):
|
|
443
|
+
shutil.copytree(template_models_path, models_target_path)
|
|
444
|
+
|
|
445
|
+
# Update model files with module name
|
|
446
|
+
for root, dirs, files in os.walk(models_target_path):
|
|
447
|
+
for file in files:
|
|
448
|
+
if file.endswith(".py"):
|
|
449
|
+
file_path = os.path.join(root, file)
|
|
450
|
+
|
|
451
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
|
452
|
+
content = f.read()
|
|
453
|
+
|
|
454
|
+
# Replace template references in model files
|
|
455
|
+
content = content.replace(
|
|
456
|
+
"__templates__", module_path.split(".")[-1]
|
|
457
|
+
)
|
|
458
|
+
content = content.replace("Template", agent_name)
|
|
459
|
+
content = content.replace("template", agent_name.lower())
|
|
460
|
+
|
|
461
|
+
with open(file_path, "w", encoding="utf-8") as f:
|
|
462
|
+
f.write(content)
|
|
463
|
+
|
|
464
|
+
console.print(
|
|
465
|
+
f"ā
Models folder created at {models_target_path}", style="green"
|
|
466
|
+
)
|
|
467
|
+
else:
|
|
468
|
+
console.print(
|
|
469
|
+
f"ā ļø Template models folder not found at {template_models_path}",
|
|
470
|
+
style="yellow",
|
|
471
|
+
)
|
|
472
|
+
else:
|
|
473
|
+
console.print(
|
|
474
|
+
f"š¦ Models folder already exists at {models_target_path}", style="dim"
|
|
475
|
+
)
|
|
476
|
+
|
|
477
|
+
console.print(
|
|
478
|
+
f"ā
Agent '{agent_name}Agent' created successfully!", style="bright_green"
|
|
479
|
+
)
|
|
480
|
+
console.print("\nš Files created:", style="bright_blue")
|
|
481
|
+
console.print(f"⢠{target_agent_file}", style="dim")
|
|
482
|
+
console.print(f"⢠{target_test_file}", style="dim")
|
|
483
|
+
if os.path.exists(models_target_path):
|
|
484
|
+
console.print(f"⢠{models_target_path}/ (models folder)", style="dim")
|
|
485
|
+
console.print("\nš Next steps:", style="bright_blue")
|
|
486
|
+
console.print("1. Edit the agent's SYSTEM_PROMPT and capabilities", style="dim")
|
|
487
|
+
console.print("2. Configure the agent's tools and intents", style="dim")
|
|
488
|
+
console.print(
|
|
489
|
+
f"3. Test your agent: uv run python -m pytest {target_test_file}",
|
|
490
|
+
style="dim",
|
|
491
|
+
)
|
|
492
|
+
console.print(
|
|
493
|
+
f"4. Run your agent: make chat agent={agent_name}Agent", style="dim"
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
except Exception as e:
|
|
497
|
+
console.print(f"ā Error creating agent: {e}", style="red")
|
|
498
|
+
# Clean up partial creation
|
|
499
|
+
for file_path in [target_agent_file, target_test_file]:
|
|
500
|
+
if os.path.exists(file_path):
|
|
501
|
+
os.remove(file_path)
|
|
502
|
+
|
|
503
|
+
# Clean up models folder if it was created during this operation
|
|
504
|
+
module_root_path = os.path.dirname(target_path)
|
|
505
|
+
models_target_path = os.path.join(module_root_path, "models")
|
|
506
|
+
if os.path.exists(models_target_path):
|
|
507
|
+
# Only remove if it's empty or was just created
|
|
508
|
+
try:
|
|
509
|
+
if not os.listdir(models_target_path): # Empty directory
|
|
510
|
+
os.rmdir(models_target_path)
|
|
511
|
+
except Exception:
|
|
512
|
+
pass # Don't fail if we can't clean up models folder
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
def create_component(component_type: str, template_files: list[str], file_suffix: str):
|
|
516
|
+
"""Generic function to create components (integration, workflow, pipeline, ontology)."""
|
|
517
|
+
console.print(f"š§ Creating a new {component_type}...\n", style="bright_cyan")
|
|
518
|
+
|
|
519
|
+
# Get target path first
|
|
520
|
+
target_path = Prompt.ask(
|
|
521
|
+
f"Where should the {component_type} be created? (e.g., src/core/mymodule/{component_type}s)"
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
# Validate path exists
|
|
525
|
+
if not os.path.exists(target_path):
|
|
526
|
+
create_path = Prompt.ask(
|
|
527
|
+
f"Path {target_path} doesn't exist. Create it?",
|
|
528
|
+
choices=["y", "n"],
|
|
529
|
+
default="y",
|
|
530
|
+
)
|
|
531
|
+
if create_path == "y":
|
|
532
|
+
try:
|
|
533
|
+
os.makedirs(target_path, exist_ok=True)
|
|
534
|
+
console.print(f"š Created directory {target_path}", style="green")
|
|
535
|
+
except Exception as e:
|
|
536
|
+
console.print(f"ā Error creating directory: {e}", style="red")
|
|
537
|
+
return
|
|
538
|
+
else:
|
|
539
|
+
console.print(
|
|
540
|
+
f"ā {component_type.title()} creation cancelled", style="red"
|
|
541
|
+
)
|
|
542
|
+
return
|
|
543
|
+
|
|
544
|
+
# Get component name
|
|
545
|
+
while True:
|
|
546
|
+
component_name = Prompt.ask(f"What is the {component_type} name?")
|
|
547
|
+
if re.match(r"^[a-zA-Z][a-zA-Z0-9_]*$", component_name):
|
|
548
|
+
break
|
|
549
|
+
console.print(
|
|
550
|
+
"ā Invalid name. Use only letters, numbers, and underscores. Must start with a letter.",
|
|
551
|
+
style="red",
|
|
552
|
+
)
|
|
553
|
+
|
|
554
|
+
# Define source and target files
|
|
555
|
+
source_files = [
|
|
556
|
+
f"src/core/__templates__/{component_type}s/{template_file}"
|
|
557
|
+
for template_file in template_files
|
|
558
|
+
]
|
|
559
|
+
target_files = []
|
|
560
|
+
|
|
561
|
+
for template_file in template_files:
|
|
562
|
+
if template_file.endswith(".py"):
|
|
563
|
+
target_file = os.path.join(
|
|
564
|
+
target_path, template_file.replace("Template", component_name)
|
|
565
|
+
)
|
|
566
|
+
else: # For .ttl files
|
|
567
|
+
target_file = os.path.join(
|
|
568
|
+
target_path, template_file.replace("Template", component_name)
|
|
569
|
+
)
|
|
570
|
+
target_files.append(target_file)
|
|
571
|
+
|
|
572
|
+
# Check if files already exist
|
|
573
|
+
if any(os.path.exists(target_file) for target_file in target_files):
|
|
574
|
+
console.print(
|
|
575
|
+
f"ā {component_type.title()} files already exist at {target_path}",
|
|
576
|
+
style="red",
|
|
577
|
+
)
|
|
578
|
+
return
|
|
579
|
+
|
|
580
|
+
try:
|
|
581
|
+
for i, (source_file, target_file) in enumerate(zip(source_files, target_files)):
|
|
582
|
+
console.print(
|
|
583
|
+
f"š Creating {os.path.basename(target_file)}...", style="yellow"
|
|
584
|
+
)
|
|
585
|
+
|
|
586
|
+
# Copy and update file
|
|
587
|
+
with open(source_file, "r", encoding="utf-8") as f:
|
|
588
|
+
content = f.read()
|
|
589
|
+
|
|
590
|
+
# Replace Template with component name
|
|
591
|
+
content = content.replace("Template", component_name)
|
|
592
|
+
content = content.replace("template", component_name.lower())
|
|
593
|
+
|
|
594
|
+
# Update import paths if needed (for Python files)
|
|
595
|
+
if target_file.endswith(".py"):
|
|
596
|
+
relative_path = (
|
|
597
|
+
os.path.relpath(target_path, "src")
|
|
598
|
+
.replace("\\", "/")
|
|
599
|
+
.replace("/", ".")
|
|
600
|
+
)
|
|
601
|
+
if relative_path != f"core.__templates__.{component_type}s":
|
|
602
|
+
# Update integration imports
|
|
603
|
+
content = content.replace(
|
|
604
|
+
"from naas_abi.core.__templates__.integrations.TemplateIntegration import",
|
|
605
|
+
f"from naas_abi.{relative_path.replace(f'.{component_type}s', '')}.integrations.{component_name}Integration import",
|
|
606
|
+
)
|
|
607
|
+
# Update other template references in imports
|
|
608
|
+
content = re.sub(
|
|
609
|
+
r"from src\.core\.__templates__\.([^.]+)\.Template([^.]+) import",
|
|
610
|
+
rf"from naas_abi.{relative_path.replace(f'.{component_type}s', '')}.\1.{component_name}\2 import",
|
|
611
|
+
content,
|
|
612
|
+
)
|
|
613
|
+
|
|
614
|
+
with open(target_file, "w", encoding="utf-8") as f:
|
|
615
|
+
f.write(content)
|
|
616
|
+
|
|
617
|
+
console.print(
|
|
618
|
+
f"ā
{component_type.title()} '{component_name}{file_suffix}' created successfully!",
|
|
619
|
+
style="bright_green",
|
|
620
|
+
)
|
|
621
|
+
console.print("\nš Files created:", style="bright_blue")
|
|
622
|
+
for target_file in target_files:
|
|
623
|
+
console.print(f"⢠{target_file}", style="dim")
|
|
624
|
+
console.print("\nš Next steps:", style="bright_blue")
|
|
625
|
+
console.print(
|
|
626
|
+
f"1. Edit the {component_type}'s configuration and implementation",
|
|
627
|
+
style="dim",
|
|
628
|
+
)
|
|
629
|
+
console.print("2. Update the class names and methods as needed", style="dim")
|
|
630
|
+
console.print(
|
|
631
|
+
f"3. Test your {component_type}: uv run python -m pytest {target_files[1] if len(target_files) > 1 else target_files[0]}",
|
|
632
|
+
style="dim",
|
|
633
|
+
)
|
|
634
|
+
|
|
635
|
+
except Exception as e:
|
|
636
|
+
console.print(f"ā Error creating {component_type}: {e}", style="red")
|
|
637
|
+
# Clean up partial creation
|
|
638
|
+
for target_file in target_files:
|
|
639
|
+
if os.path.exists(target_file):
|
|
640
|
+
os.remove(target_file)
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
def create_integration():
|
|
644
|
+
"""Create a new integration by duplicating TemplateIntegration files."""
|
|
645
|
+
create_component(
|
|
646
|
+
component_type="integration",
|
|
647
|
+
template_files=["TemplateIntegration.py", "TemplateIntegration_test.py"],
|
|
648
|
+
file_suffix="Integration",
|
|
649
|
+
)
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
def create_workflow():
|
|
653
|
+
"""Create a new workflow by duplicating TemplateWorkflow files."""
|
|
654
|
+
create_component(
|
|
655
|
+
component_type="workflow",
|
|
656
|
+
template_files=["TemplateWorkflow.py", "TemplateWorkflow_test.py"],
|
|
657
|
+
file_suffix="Workflow",
|
|
658
|
+
)
|
|
659
|
+
|
|
660
|
+
|
|
661
|
+
def create_pipeline():
|
|
662
|
+
"""Create a new pipeline by duplicating TemplatePipeline files."""
|
|
663
|
+
create_component(
|
|
664
|
+
component_type="pipeline",
|
|
665
|
+
template_files=["TemplatePipeline.py", "TemplatePipeline_test.py"],
|
|
666
|
+
file_suffix="Pipeline",
|
|
667
|
+
)
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
def create_ontology():
|
|
671
|
+
"""Create a new ontology by duplicating Template ontology files."""
|
|
672
|
+
create_component(
|
|
673
|
+
component_type="ontology",
|
|
674
|
+
template_files=["TemplateOntology.ttl", "TemplateSparqlQueries.ttl"],
|
|
675
|
+
file_suffix="Ontology",
|
|
676
|
+
)
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
def main():
|
|
680
|
+
"""Main CLI entry point for ABI commands."""
|
|
681
|
+
import sys
|
|
682
|
+
|
|
683
|
+
if len(sys.argv) < 2:
|
|
684
|
+
console.print("ā Please specify a command:", style="red")
|
|
685
|
+
console.print(
|
|
686
|
+
"Available commands: create-module, create-agent, create-integration, create-workflow, create-pipeline, create-ontology",
|
|
687
|
+
style="dim",
|
|
688
|
+
)
|
|
689
|
+
return
|
|
690
|
+
|
|
691
|
+
command = sys.argv[1]
|
|
692
|
+
|
|
693
|
+
if command == "create-module":
|
|
694
|
+
create_new_module()
|
|
695
|
+
elif command == "create-agent":
|
|
696
|
+
create_agent()
|
|
697
|
+
elif command == "create-integration":
|
|
698
|
+
create_integration()
|
|
699
|
+
elif command == "create-workflow":
|
|
700
|
+
create_workflow()
|
|
701
|
+
elif command == "create-pipeline":
|
|
702
|
+
create_pipeline()
|
|
703
|
+
elif command == "create-ontology":
|
|
704
|
+
create_ontology()
|
|
705
|
+
else:
|
|
706
|
+
console.print(f"ā Unknown command: {command}", style="red")
|
|
707
|
+
console.print(
|
|
708
|
+
"Available commands: create-module, create-agent, create-integration, create-workflow, create-pipeline, create-ontology",
|
|
709
|
+
style="dim",
|
|
710
|
+
)
|
|
711
|
+
|
|
712
|
+
|
|
713
|
+
if __name__ == "__main__":
|
|
714
|
+
main()
|