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.
Files changed (62) hide show
  1. naas_abi/__init__.py +35 -0
  2. naas_abi/agents/AbiAgent.py +442 -0
  3. naas_abi/agents/AbiAgent_test.py +157 -0
  4. naas_abi/agents/EntitytoSPARQLAgent.py +952 -0
  5. naas_abi/agents/EntitytoSPARQLAgent_test.py +66 -0
  6. naas_abi/agents/KnowledgeGraphBuilderAgent.py +321 -0
  7. naas_abi/agents/KnowledgeGraphBuilderAgent_test.py +86 -0
  8. naas_abi/agents/OntologyEngineerAgent.py +115 -0
  9. naas_abi/agents/OntologyEngineerAgent_test.py +42 -0
  10. naas_abi/apps/oxigraph_admin/main.py +392 -0
  11. naas_abi/apps/oxigraph_admin/terminal_style.py +151 -0
  12. naas_abi/apps/sparql_terminal/main.py +68 -0
  13. naas_abi/apps/sparql_terminal/terminal_style.py +236 -0
  14. naas_abi/apps/terminal_agent/main.py +553 -0
  15. naas_abi/apps/terminal_agent/terminal_style.py +175 -0
  16. naas_abi/cli.py +714 -0
  17. naas_abi/mappings.py +83 -0
  18. naas_abi/models/airgap_gemma.py +220 -0
  19. naas_abi/models/airgap_qwen.py +24 -0
  20. naas_abi/models/default.py +23 -0
  21. naas_abi/models/gpt_4_1.py +25 -0
  22. naas_abi/pipelines/AIAgentOntologyGenerationPipeline.py +635 -0
  23. naas_abi/pipelines/AIAgentOntologyGenerationPipeline_test.py +133 -0
  24. naas_abi/pipelines/AddIndividualPipeline.py +215 -0
  25. naas_abi/pipelines/AddIndividualPipeline_test.py +66 -0
  26. naas_abi/pipelines/InsertDataSPARQLPipeline.py +197 -0
  27. naas_abi/pipelines/InsertDataSPARQLPipeline_test.py +96 -0
  28. naas_abi/pipelines/MergeIndividualsPipeline.py +245 -0
  29. naas_abi/pipelines/MergeIndividualsPipeline_test.py +98 -0
  30. naas_abi/pipelines/RemoveIndividualPipeline.py +166 -0
  31. naas_abi/pipelines/RemoveIndividualPipeline_test.py +58 -0
  32. naas_abi/pipelines/UpdateCommercialOrganizationPipeline.py +198 -0
  33. naas_abi/pipelines/UpdateDataPropertyPipeline.py +175 -0
  34. naas_abi/pipelines/UpdateLegalNamePipeline.py +107 -0
  35. naas_abi/pipelines/UpdateLinkedInPagePipeline.py +179 -0
  36. naas_abi/pipelines/UpdatePersonPipeline.py +184 -0
  37. naas_abi/pipelines/UpdateSkillPipeline.py +118 -0
  38. naas_abi/pipelines/UpdateTickerPipeline.py +104 -0
  39. naas_abi/pipelines/UpdateWebsitePipeline.py +106 -0
  40. naas_abi/triggers.py +131 -0
  41. naas_abi/workflows/AgentRecommendationWorkflow.py +321 -0
  42. naas_abi/workflows/AgentRecommendationWorkflow_test.py +160 -0
  43. naas_abi/workflows/ArtificialAnalysisWorkflow.py +337 -0
  44. naas_abi/workflows/ArtificialAnalysisWorkflow_test.py +57 -0
  45. naas_abi/workflows/ConvertOntologyGraphToYamlWorkflow.py +210 -0
  46. naas_abi/workflows/ConvertOntologyGraphToYamlWorkflow_test.py +78 -0
  47. naas_abi/workflows/CreateClassOntologyYamlWorkflow.py +208 -0
  48. naas_abi/workflows/CreateClassOntologyYamlWorkflow_test.py +65 -0
  49. naas_abi/workflows/CreateIndividualOntologyYamlWorkflow.py +183 -0
  50. naas_abi/workflows/CreateIndividualOntologyYamlWorkflow_test.py +86 -0
  51. naas_abi/workflows/ExportGraphInstancesToExcelWorkflow.py +450 -0
  52. naas_abi/workflows/ExportGraphInstancesToExcelWorkflow_test.py +33 -0
  53. naas_abi/workflows/GetObjectPropertiesFromClassWorkflow.py +385 -0
  54. naas_abi/workflows/GetObjectPropertiesFromClassWorkflow_test.py +57 -0
  55. naas_abi/workflows/GetSubjectGraphWorkflow.py +84 -0
  56. naas_abi/workflows/GetSubjectGraphWorkflow_test.py +71 -0
  57. naas_abi/workflows/SearchIndividualWorkflow.py +190 -0
  58. naas_abi/workflows/SearchIndividualWorkflow_test.py +98 -0
  59. naas_abi-1.0.0.dist-info/METADATA +9 -0
  60. naas_abi-1.0.0.dist-info/RECORD +62 -0
  61. naas_abi-1.0.0.dist-info/WHEEL +5 -0
  62. 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()