Amsha 3.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 (114) hide show
  1. amsha/__init__.py +0 -0
  2. amsha/common/__init__.py +4 -0
  3. amsha/common/logger.py +273 -0
  4. amsha/configuration/application/configuration_manager.py +57 -0
  5. amsha/configuration/domain/models/amsha_app_config.py +9 -0
  6. amsha/configuration/domain/models/amsha_job_config.py +35 -0
  7. amsha/configuration/domain/models/amsha_llm_config.py +38 -0
  8. amsha/configuration/exceptions/amsha_configuration_exception.py +16 -0
  9. amsha/configuration/infrastructure/strict_validator.py +50 -0
  10. amsha/crew_forge/__init__.py +4 -0
  11. amsha/crew_forge/dependency/__init__.py +0 -0
  12. amsha/crew_forge/domain/__init__.py +0 -0
  13. amsha/crew_forge/domain/enum/__init__.py +0 -0
  14. amsha/crew_forge/domain/models/__init__.py +0 -0
  15. amsha/crew_forge/domain/models/agent_data.py +34 -0
  16. amsha/crew_forge/domain/models/crew_data.py +30 -0
  17. amsha/crew_forge/domain/models/mcp_data.py +49 -0
  18. amsha/crew_forge/domain/models/task_data.py +22 -0
  19. amsha/crew_forge/exceptions/__init__.py +24 -0
  20. amsha/crew_forge/exceptions/crew_configuration_exception.py +39 -0
  21. amsha/crew_forge/exceptions/crew_execution_exception.py +38 -0
  22. amsha/crew_forge/exceptions/crew_forge_exception.py +37 -0
  23. amsha/crew_forge/exceptions/crew_manager_exception.py +38 -0
  24. amsha/crew_forge/exceptions/error_context.py +206 -0
  25. amsha/crew_forge/exceptions/input_preparation_exception.py +38 -0
  26. amsha/crew_forge/knowledge/__init__.py +0 -0
  27. amsha/crew_forge/knowledge/amsha_crew_docling_source.py +136 -0
  28. amsha/crew_forge/knowledge/amsha_json_knowledge_source.py +56 -0
  29. amsha/crew_forge/orchestrator/__init__.py +0 -0
  30. amsha/crew_forge/orchestrator/file/__init__.py +0 -0
  31. amsha/crew_forge/orchestrator/file/amsha_crew_file_application.py +223 -0
  32. amsha/crew_forge/orchestrator/file/atomic_crew_file_manager.py +128 -0
  33. amsha/crew_forge/orchestrator/file/file_crew_orchestrator.py +153 -0
  34. amsha/crew_forge/orchestrator/flow/__init__.py +3 -0
  35. amsha/crew_forge/orchestrator/flow/flow_crew_pipeline.py +134 -0
  36. amsha/crew_forge/protocols/__init__.py +0 -0
  37. amsha/crew_forge/protocols/crew_application.py +99 -0
  38. amsha/crew_forge/protocols/crew_manager.py +69 -0
  39. amsha/crew_forge/protocols/crew_orchestrator.py +72 -0
  40. amsha/crew_forge/protocols/input_preparation_service.py +61 -0
  41. amsha/crew_forge/seeding/__init__.py +0 -0
  42. amsha/crew_forge/seeding/parser/__init__.py +0 -0
  43. amsha/crew_forge/seeding/parser/crew_parser.py +48 -0
  44. amsha/crew_forge/service/__init__.py +0 -0
  45. amsha/crew_forge/service/atomic_yaml_builder.py +77 -0
  46. amsha/crew_forge/service/base_crew_orchestrator.py +322 -0
  47. amsha/crew_forge/service/crew_builder_service.py +247 -0
  48. amsha/crew_forge/service/shared_input_preparation_service.py +203 -0
  49. amsha/crew_forge/service/shared_json_file_service.py +179 -0
  50. amsha/crew_forge/service/shared_llm_initialization_service.py +159 -0
  51. amsha/crew_forge/service/tool_registry.py +58 -0
  52. amsha/crew_monitor/__init__.py +13 -0
  53. amsha/crew_monitor/service/__init__.py +0 -0
  54. amsha/crew_monitor/service/amsha_event_listener.py +238 -0
  55. amsha/crew_monitor/service/contribution_analyzer.py +109 -0
  56. amsha/crew_monitor/service/crew_performance_monitor.py +165 -0
  57. amsha/crew_monitor/service/reporting_tool.py +164 -0
  58. amsha/execution_runtime/__init__.py +0 -0
  59. amsha/execution_runtime/domain/__init__.py +2 -0
  60. amsha/execution_runtime/domain/execution_handle.py +29 -0
  61. amsha/execution_runtime/domain/execution_mode.py +9 -0
  62. amsha/execution_runtime/service/__init__.py +1 -0
  63. amsha/execution_runtime/service/runtime_engine.py +89 -0
  64. amsha/execution_state/__init__.py +0 -0
  65. amsha/execution_state/domain/__init__.py +2 -0
  66. amsha/execution_state/domain/enums.py +12 -0
  67. amsha/execution_state/domain/execution_state.py +60 -0
  68. amsha/execution_state/service/__init__.py +1 -0
  69. amsha/execution_state/service/state_manager.py +67 -0
  70. amsha/llm_factory/__init__.py +0 -0
  71. amsha/llm_factory/adapters/__init__.py +0 -0
  72. amsha/llm_factory/adapters/crewai_adapter.py +22 -0
  73. amsha/llm_factory/adapters/lmstudio_lifecycle_client.py +125 -0
  74. amsha/llm_factory/dependency/__init__.py +0 -0
  75. amsha/llm_factory/dependency/llm_container.py +33 -0
  76. amsha/llm_factory/domain/__init__.py +0 -0
  77. amsha/llm_factory/domain/llm_type.py +5 -0
  78. amsha/llm_factory/domain/model/__init__.py +0 -0
  79. amsha/llm_factory/domain/model/llm_build_result.py +17 -0
  80. amsha/llm_factory/domain/model/llm_model_capabilities.py +15 -0
  81. amsha/llm_factory/domain/model/llm_model_config.py +17 -0
  82. amsha/llm_factory/domain/model/llm_output_config.py +19 -0
  83. amsha/llm_factory/domain/model/llm_parameters.py +12 -0
  84. amsha/llm_factory/domain/model/llm_type.py +7 -0
  85. amsha/llm_factory/domain/model/llm_use_case_config.py +9 -0
  86. amsha/llm_factory/domain/model/lmstudio_lifecycle_config.py +19 -0
  87. amsha/llm_factory/domain/provider_protocol.py +21 -0
  88. amsha/llm_factory/exceptions.py +2 -0
  89. amsha/llm_factory/service/__init__.py +0 -0
  90. amsha/llm_factory/service/llm_builder.py +75 -0
  91. amsha/llm_factory/settings/__init__.py +0 -0
  92. amsha/llm_factory/settings/llm_settings.py +62 -0
  93. amsha/llm_factory/utils/__init__.py +0 -0
  94. amsha/llm_factory/utils/deprecated_compat.py +60 -0
  95. amsha/llm_factory/utils/llm_utils.py +35 -0
  96. amsha/output_process/__init__.py +0 -0
  97. amsha/output_process/evaluation/__init__.py +0 -0
  98. amsha/output_process/evaluation/evaluation_aggregate_tool.py +0 -0
  99. amsha/output_process/evaluation/evaluation_processing_tool.py +0 -0
  100. amsha/output_process/evaluation/evaluation_report_tool.py +0 -0
  101. amsha/output_process/optimization/__init__.py +0 -0
  102. amsha/output_process/optimization/json_cleaner_utils.py +182 -0
  103. amsha/output_process/validation/__init__.py +0 -0
  104. amsha/output_process/validation/crew_validator.py +0 -0
  105. amsha/output_process/validation/json_output_validator.py +0 -0
  106. amsha/utils/__init__.py +0 -0
  107. amsha/utils/json_utils.py +50 -0
  108. amsha/utils/utf8_utils.py +51 -0
  109. amsha/utils/yaml_utils.py +19 -0
  110. amsha-3.0.0.dist-info/METADATA +173 -0
  111. amsha-3.0.0.dist-info/RECORD +114 -0
  112. amsha-3.0.0.dist-info/WHEEL +5 -0
  113. amsha-3.0.0.dist-info/licenses/LICENSE +21 -0
  114. amsha-3.0.0.dist-info/top_level.txt +1 -0
amsha/__init__.py ADDED
File without changes
@@ -0,0 +1,4 @@
1
+ """Common utilities for Amsha."""
2
+ from amsha.common.logger import get_logger, reset_logger, log_execution, MetricsLogger
3
+
4
+ __all__ = ["get_logger", "reset_logger", "log_execution", "MetricsLogger"]
amsha/common/logger.py ADDED
@@ -0,0 +1,273 @@
1
+ """
2
+ Comprehensive logging for Amsha (stdlib-only).
3
+
4
+ This module provides structured logging with metrics tracking, execution tracing,
5
+ and performance monitoring - going beyond simple print replacement.
6
+
7
+ Basic Usage:
8
+ from amsha.common.logger import get_logger, log_execution, MetricsLogger
9
+
10
+ logger = get_logger("crew_forge")
11
+ logger.info("Operation successful", extra={"crew_name": "test", "tokens": 1500})
12
+
13
+ @log_execution(logger, "build_crew")
14
+ def build_crew(name):
15
+ # Automatically logs start, duration, and completion
16
+ pass
17
+ """
18
+ from typing import Optional, Dict, Any, Callable
19
+ import logging
20
+ import os
21
+ import time
22
+ import functools
23
+ from logging.handlers import RotatingFileHandler
24
+
25
+ _configured = False
26
+ _module_loggers: dict = {}
27
+
28
+
29
+ # ============================================================================
30
+ # Structured Logging Formatter
31
+ # ============================================================================
32
+
33
+ class StructuredFormatter(logging.Formatter):
34
+ """
35
+ Custom formatter that includes extra fields in log output.
36
+
37
+ Formats log records to show structured metadata in a readable way:
38
+ timestamp | logger_name | level | message | key1=value1 key2=value2
39
+ """
40
+
41
+ # Standard logging record attributes to exclude from extra fields
42
+ EXCLUDED_ATTRS = {
43
+ 'name', 'msg', 'args', 'created', 'filename', 'funcName', 'levelname',
44
+ 'levelno', 'lineno', 'module', 'msecs', 'message', 'pathname', 'process',
45
+ 'processName', 'relativeCreated', 'thread', 'threadName', 'exc_info',
46
+ 'exc_text', 'stack_info', 'asctime', 'taskName'
47
+ }
48
+
49
+ def format(self, record: logging.LogRecord) -> str:
50
+ # Format base message with standard format
51
+ base_message = super().format(record)
52
+
53
+ # Extract extra fields
54
+ extra_fields = {}
55
+ for key, value in record.__dict__.items():
56
+ if key not in self.EXCLUDED_ATTRS:
57
+ extra_fields[key] = value
58
+
59
+ # If there are extra fields, append them to the message
60
+ if extra_fields:
61
+ # Format extra fields as key=value pairs
62
+ extra_str = " | ".join(f"{k}={v}" for k, v in sorted(extra_fields.items()))
63
+ return f"{base_message} | {extra_str}"
64
+
65
+ return base_message
66
+
67
+
68
+ def _configure() -> None:
69
+ """
70
+ Configure the root Amsha logger with a rotating file handler and console handler.
71
+ Idempotent - only runs once per process.
72
+ """
73
+ global _configured
74
+ if _configured:
75
+ return
76
+
77
+ level = os.getenv("AMSHA_LOG_LEVEL", "INFO")
78
+ root = logging.getLogger("Amsha")
79
+ root.setLevel(level.upper())
80
+ root.propagate = False
81
+
82
+ formatter = StructuredFormatter(
83
+ fmt='%(asctime)s | %(name)s | %(levelname)s | %(message)s',
84
+ datefmt='%Y-%m-%d %H:%M:%S'
85
+ )
86
+
87
+ logs_dir = os.getenv("AMSHA_LOG_DIR", "logs")
88
+ os.makedirs(logs_dir, exist_ok=True)
89
+
90
+ file_handler = RotatingFileHandler(
91
+ os.path.join(logs_dir, "amsha.log"),
92
+ maxBytes=50 * 1024 * 1024,
93
+ backupCount=30,
94
+ encoding="utf-8",
95
+ )
96
+ file_handler.setFormatter(formatter)
97
+ root.addHandler(file_handler)
98
+
99
+ # Only add a console handler if one isn't already attached (avoids duplicate
100
+ # console output when the root handler is configured elsewhere).
101
+ if not any(isinstance(h, logging.StreamHandler) for h in root.handlers):
102
+ console = logging.StreamHandler()
103
+ console.setFormatter(formatter)
104
+ root.addHandler(console)
105
+
106
+ _configured = True
107
+
108
+
109
+ def get_logger(module_name: Optional[str] = None, log_level: Optional[str] = None) -> logging.Logger:
110
+ """
111
+ Get or create a logger instance for Amsha.
112
+
113
+ All logs are written to logs/amsha.log and console (path overridable via
114
+ the AMSHA_LOG_DIR environment variable).
115
+
116
+ Args:
117
+ module_name: Optional module name for hierarchical logging (e.g., "crew_forge")
118
+ log_level: Optional log level override (DEBUG, INFO, WARNING, ERROR, CRITICAL)
119
+ Defaults to environment variable AMSHA_LOG_LEVEL or INFO
120
+
121
+ Returns:
122
+ Configured logger instance
123
+
124
+ Examples:
125
+ >>> logger = get_logger() # Root Amsha logger
126
+ >>> logger.info("Application started")
127
+
128
+ >>> crew_logger = get_logger("crew_forge")
129
+ >>> crew_logger.debug("Building crew", extra={"crew_name": "test", "tokens": 1500})
130
+ """
131
+ global _module_loggers
132
+
133
+ _configure()
134
+
135
+ logger = logging.getLogger(f"Amsha.{module_name}" if module_name else "Amsha")
136
+
137
+ if log_level:
138
+ logger.setLevel(log_level.upper())
139
+
140
+ # Cache module loggers to avoid recreation
141
+ if module_name and module_name not in _module_loggers:
142
+ _module_loggers[module_name] = logger
143
+
144
+ return logger
145
+
146
+
147
+ def log_execution(logger: logging.Logger, operation_name: str) -> Callable:
148
+ """
149
+ Decorator to automatically log function execution with timing and error handling.
150
+
151
+ Args:
152
+ logger: Logger instance to use
153
+ operation_name: Human-readable operation name
154
+
155
+ Returns:
156
+ Decorated function
157
+
158
+ Usage:
159
+ @log_execution(logger, "build_crew")
160
+ def build_atomic_crew(self, crew_name):
161
+ # Automatically logs start, duration, success/failure
162
+ return crew
163
+ """
164
+ def decorator(func: Callable) -> Callable:
165
+ @functools.wraps(func)
166
+ def wrapper(*args, **kwargs):
167
+ start_time = time.time()
168
+ logger.debug(f"{operation_name} started", extra={
169
+ "operation": operation_name,
170
+ "function": func.__name__
171
+ })
172
+
173
+ try:
174
+ result = func(*args, **kwargs)
175
+ duration = time.time() - start_time
176
+
177
+ logger.info(f"{operation_name} completed", extra={
178
+ "operation": operation_name,
179
+ "duration_seconds": round(duration, 4),
180
+ "status": "success"
181
+ })
182
+ return result
183
+ except Exception as e:
184
+ duration = time.time() - start_time
185
+ logger.error(f"{operation_name} failed", extra={
186
+ "operation": operation_name,
187
+ "duration_seconds": round(duration, 4),
188
+ "error_type": type(e).__name__,
189
+ "error_message": str(e),
190
+ "status": "failed"
191
+ }, exc_info=True)
192
+ raise
193
+ return wrapper
194
+ return decorator
195
+
196
+
197
+ class MetricsLogger:
198
+ """Helper class for logging structured metrics and performance data."""
199
+
200
+ def __init__(self, logger: logging.Logger):
201
+ self.logger = logger
202
+
203
+ def log_execution_metrics(self, crew_name: str, execution_id: str,
204
+ metrics: Dict[str, Any], duration: float):
205
+ """
206
+ Log comprehensive execution metrics.
207
+
208
+ Args:
209
+ crew_name: Name of the crew
210
+ execution_id: Unique execution identifier
211
+ metrics: Dict containing general and gpu metrics
212
+ duration: Execution duration in seconds
213
+ """
214
+ general = metrics.get("general", {})
215
+ self.logger.info("Execution metrics captured", extra={
216
+ "crew_name": crew_name,
217
+ "execution_id": execution_id,
218
+ "duration_seconds": round(duration, 4),
219
+ "total_tokens": general.get("total_tokens", 0),
220
+ "prompt_tokens": general.get("prompt_tokens", 0),
221
+ "completion_tokens": general.get("completion_tokens", 0),
222
+ "cpu_usage_percent": general.get("cpu_usage_end_percent", 0),
223
+ "memory_change_mb": general.get("memory_usage_change_mb", 0),
224
+ "has_gpu_metrics": bool(metrics.get("gpu", {}))
225
+ })
226
+
227
+ def log_llm_config(self, model_name: str, config: Dict[str, Any]):
228
+ """
229
+ Log LLM configuration details.
230
+
231
+ Args:
232
+ model_name: Clean model name
233
+ config: Dict containing LLM configuration
234
+ """
235
+ self.logger.info("LLM configuration applied", extra={
236
+ "model_name": model_name,
237
+ "model": config.get("model"),
238
+ "temperature": config.get("temperature"),
239
+ "top_p": config.get("top_p"),
240
+ "max_tokens": config.get("max_completion_tokens"),
241
+ "stream": config.get("stream", False),
242
+ "has_base_url": bool(config.get("base_url") or config.get("endpoint"))
243
+ })
244
+
245
+ def log_file_operation(self, operation: str, file_path: str,
246
+ status: str, **extra_data):
247
+ """
248
+ Log file I/O operations with context.
249
+
250
+ Args:
251
+ operation: Operation type (e.g., "validation", "save", "clean")
252
+ file_path: Path to the file
253
+ status: Operation status (success, failed)
254
+ **extra_data: Additional context
255
+ """
256
+ self.logger.info(f"File operation: {operation}", extra={
257
+ "operation": operation,
258
+ "file_path": file_path,
259
+ "status": status,
260
+ **extra_data
261
+ })
262
+
263
+
264
+ def reset_logger():
265
+ """
266
+ Reset the logger configuration. Primarily for testing purposes.
267
+ """
268
+ global _configured, _module_loggers
269
+ root = logging.getLogger("Amsha")
270
+ for handler in list(root.handlers):
271
+ root.removeHandler(handler)
272
+ _configured = False
273
+ _module_loggers = {}
@@ -0,0 +1,57 @@
1
+ from pathlib import Path
2
+ from typing import Union, Dict, Any, Type, TypeVar
3
+ import json
4
+ import yaml
5
+
6
+ from pydantic import BaseModel
7
+ from amsha.configuration.infrastructure.strict_validator import StrictConfigValidator
8
+ from amsha.configuration.exceptions.amsha_configuration_exception import AmshaConfigurationException
9
+
10
+ T = TypeVar('T', bound=BaseModel)
11
+
12
+ class ConfigurationManager:
13
+ """
14
+ Manages loading and strict validation of Amsha Configurations.
15
+ Requires configurations to be perfectly valid to proceed.
16
+ """
17
+
18
+ @staticmethod
19
+ def load_from_dict(model_class: Type[T], data: Dict[str, Any], config_name: str = "Configuration") -> T:
20
+ """
21
+ Load configuration from a dictionary strictly.
22
+ """
23
+ validator = StrictConfigValidator()
24
+ # This will raise an AmshaConfigurationException if it fails
25
+ validated_model = validator.validate_strictly(model_class, data, config_name)
26
+
27
+ # Optionally log auditor traces
28
+ import logging
29
+ logger = logging.getLogger(__name__)
30
+ for log_entry in validator.audit_log:
31
+ logger.debug(log_entry)
32
+
33
+ return validated_model
34
+
35
+ @staticmethod
36
+ def load_from_json(model_class: Type[T], path: Union[str, Path], config_name: str = "Configuration") -> T:
37
+ """Load configuration from a JSON file strictly."""
38
+ path = Path(path)
39
+ if not path.exists():
40
+ raise AmshaConfigurationException(f"Configuration file not found: {path}")
41
+
42
+ with open(path, 'r', encoding='utf-8') as f:
43
+ data = json.load(f)
44
+
45
+ return ConfigurationManager.load_from_dict(model_class, data, config_name)
46
+
47
+ @staticmethod
48
+ def load_from_yaml(model_class: Type[T], path: Union[str, Path], config_name: str = "Configuration") -> T:
49
+ """Load configuration from a YAML file strictly."""
50
+ path = Path(path)
51
+ if not path.exists():
52
+ raise AmshaConfigurationException(f"Configuration file not found: {path}")
53
+
54
+ with open(path, 'r', encoding='utf-8') as f:
55
+ data = yaml.safe_load(f)
56
+
57
+ return ConfigurationManager.load_from_dict(model_class, data, config_name)
@@ -0,0 +1,9 @@
1
+ from pydantic import BaseModel, Field
2
+
3
+ class AmshaAppConfig(BaseModel):
4
+ """
5
+ Main Application Configuration for Amsha.
6
+ Requires strict validation with no silent default fallbacks for critical paths.
7
+ """
8
+ domain_root_path: str = Field(..., description="Root path for domain configurations (e.g., crew_configs)")
9
+ output_dir_path: str = Field(..., description="Path to output directory")
@@ -0,0 +1,35 @@
1
+ from typing import List, Dict, Union, Any, Optional
2
+ from pydantic import BaseModel, Field
3
+
4
+ class CrewStep(BaseModel):
5
+ task_key: str = Field(..., description="Task reference key")
6
+ agent_key: str = Field(..., description="Agent reference key")
7
+
8
+ class CrewDefinition(BaseModel):
9
+ steps: List[CrewStep] = Field(..., description="List of steps executed by the crew")
10
+ knowledge_sources: Optional[List[str]] = Field(None, description="List of knowledge source paths")
11
+ input: Optional[Union[Dict[str, Any], List[Dict[str, Any]]]] = Field(None, description="Input definitions for the crew")
12
+ memory: bool = Field(
13
+ False,
14
+ description="Whether the crew uses CrewAI's unified memory",
15
+ )
16
+ checkpoint: Optional[Union[bool, Dict[str, Any]]] = Field(
17
+ None,
18
+ description=(
19
+ "Checkpoint configuration: a bool to enable/disable CrewAI checkpointing, "
20
+ "or a dict with keys enabled/on_events/provider/location/max_checkpoints."
21
+ ),
22
+ )
23
+
24
+ class AmshaJobConfig(BaseModel):
25
+ """
26
+ Job Orchestration Configuration.
27
+ Requires explicit validation of all structured job definitions.
28
+ """
29
+ crew_name: str = Field(..., description="Name of the overall crew execution job")
30
+ usecase: str = Field(..., description="Usecase context for execution")
31
+ module_name: str = Field(..., description="Module name for categorization")
32
+ output_filepath: str = Field(..., description="File path indicating where final output is saved")
33
+
34
+ crews: Dict[str, CrewDefinition] = Field(..., description="Dictionary mapping crew identifiers to their definitions")
35
+ pipeline: List[str] = Field(..., description="Ordered list of crews defining execution sequence")
@@ -0,0 +1,38 @@
1
+ from typing import Dict, List, Any, Optional
2
+ from pydantic import BaseModel, Field
3
+
4
+ class LLMModelDefinition(BaseModel):
5
+ base_url: Optional[str] = Field(default=None, description="Base URL of the LLM API endpoint")
6
+ model: str = Field(..., description="Model identifier string")
7
+ api_key: Optional[str] = Field(default=None, description="API key")
8
+ api_key_env: Optional[str] = Field(default=None, description="Environment variable name for the API key")
9
+ api_version: Optional[str] = Field(default=None, description="API version string")
10
+ output_config: Optional[Dict[str, Any]] = Field(default=None, description="Output file naming and organization configuration")
11
+
12
+ class LLMTypeSection(BaseModel):
13
+ default: str = Field(..., description="The default model key to use for this type")
14
+ models: Dict[str, LLMModelDefinition] = Field(..., description="Dictionary of available models by key")
15
+
16
+ class LLMConfigSection(BaseModel):
17
+ creative: LLMTypeSection = Field(..., description="Creative LLM configurations")
18
+ evaluation: LLMTypeSection = Field(..., description="Evaluation LLM configurations")
19
+
20
+ class LLMParametersTypeSection(BaseModel):
21
+ temperature: float = Field(..., description="Model temperature")
22
+ top_p: float = Field(..., description="Top P sampling parameter")
23
+ max_completion_tokens: Optional[int] = Field(default=None, description="Maximum completion tokens")
24
+ presence_penalty: Optional[float] = Field(default=None, description="Presence penalty parameter")
25
+ frequency_penalty: Optional[float] = Field(default=None, description="Frequency penalty parameter")
26
+ stop: Optional[List[str]] = Field(default=None, description="Stop sequences")
27
+
28
+ class LLMParametersSection(BaseModel):
29
+ creative: LLMParametersTypeSection = Field(..., description="Parameters for creative models")
30
+ evaluation: LLMParametersTypeSection = Field(..., description="Parameters for evaluation models")
31
+
32
+ class AmshaLLMConfig(BaseModel):
33
+ """
34
+ LLM API and Parameters Configuration.
35
+ Strictly validates connections and parameters without silent defaults.
36
+ """
37
+ llm: LLMConfigSection = Field(..., description="Configured LLM connections by type")
38
+ llm_parameters: LLMParametersSection = Field(..., description="Configured LLM hyper-parameters by type")
@@ -0,0 +1,16 @@
1
+ class AmshaException(Exception):
2
+ """Base exception for all Amsha errors"""
3
+ pass
4
+
5
+ class AmshaConfigurationException(AmshaException):
6
+ """Specific strict configuration validation error"""
7
+ def __init__(self, message: str, errors: list[str] = None):
8
+ super().__init__(message)
9
+ self.errors = errors or []
10
+
11
+ def __str__(self):
12
+ base_msg = super().__str__()
13
+ if self.errors:
14
+ error_details = "\n".join(f" - {err}" for err in self.errors)
15
+ return f"{base_msg}\nValidation Errors:\n{error_details}"
16
+ return base_msg
@@ -0,0 +1,50 @@
1
+ import logging
2
+ from typing import Type, Any, Dict, List
3
+ from pydantic import BaseModel, ValidationError
4
+
5
+ from amsha.configuration.exceptions.amsha_configuration_exception import AmshaConfigurationException
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+ class StrictConfigValidator:
10
+ """
11
+ Validator that checks input data against a Pydantic model.
12
+ Unlike RobustConfigValidator, this validator strictly enforces
13
+ that all required fields are present and valid. Any failure
14
+ will accumulate errors and raise an AmshaConfigurationException
15
+ to terminate execution early and prevent silent crashes.
16
+ """
17
+ def __init__(self):
18
+ self.audit_log: List[str] = []
19
+
20
+ def validate_strictly(self, model_class: Type[BaseModel], input_data: Dict[str, Any], config_name: str = "Configuration") -> BaseModel:
21
+ """
22
+ Validates input_data against model_class.
23
+ Raises AmshaConfigurationException if any validation fails.
24
+ """
25
+ if input_data is None:
26
+ raise AmshaConfigurationException(f"{config_name} cannot be empty or None.")
27
+
28
+ if not isinstance(input_data, dict):
29
+ raise AmshaConfigurationException(f"Expected dictionary for {config_name}, got {type(input_data).__name__}.")
30
+
31
+ try:
32
+ # Pydantic will perform strict validation based on the model.
33
+ # Missing fields configured with `Field(...)` will throw ValidationErrors.
34
+ validated_model = model_class(**input_data)
35
+ self.audit_log.append(f"[VALID] {config_name} passed strict validation.")
36
+ return validated_model
37
+
38
+ except ValidationError as e:
39
+ # Extract detailed errors from Pydantic
40
+ detailed_errors = []
41
+ for err in e.errors():
42
+ loc = ".".join([str(x) for x in err.get("loc", [])])
43
+ msg = err.get("msg", "")
44
+ detailed_errors.append(f"Field '{loc}': {msg}")
45
+
46
+ error_msg = f"❌ Strict Validation Failed for {config_name}."
47
+ logger.error(error_msg)
48
+
49
+ # Immediately raise our custom exception with the accumulated details
50
+ raise AmshaConfigurationException(error_msg, errors=detailed_errors) from e
@@ -0,0 +1,4 @@
1
+ from .orchestrator.file.amsha_crew_file_application import AmshaCrewFileApplication
2
+ from .orchestrator.flow.flow_crew_pipeline import FlowCrewOrchestrator, PipelineState
3
+
4
+ __all__ = ["AmshaCrewFileApplication", "FlowCrewOrchestrator", "PipelineState"]
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,34 @@
1
+ # src/nikhil/amsha/toolkit/crew_forge/domain/agent_data.py
2
+ from typing import List, Optional
3
+
4
+ from pydantic import BaseModel, Field
5
+
6
+ from amsha.crew_forge.domain.models.mcp_data import McpServerConfig
7
+
8
+
9
+ class AgentRequest(BaseModel):
10
+ role: str = Field(..., description="The role or persona of the agent.")
11
+ goal: str = Field(..., description="The primary objective or goal of the agent.")
12
+ backstory: str = Field(..., description="The backstory or context for the agent.")
13
+ usecase: Optional[str] = Field(None, description="The Usecase or context for the agent for its use")
14
+
15
+ # Execution tuning
16
+ max_iter: Optional[int] = Field(None, description="Maximum number of iterations for the agent.")
17
+ max_rpm: Optional[int] = Field(None, description="Maximum requests per minute.")
18
+ max_execution_time: Optional[int] = Field(None, description="Maximum execution time (seconds).")
19
+ max_retry_limit: Optional[int] = Field(None, description="Maximum number of retries on tool failure.")
20
+ respect_context_window: Optional[bool] = Field(None, description="Respect the context window when sending messages.")
21
+ allow_delegation: Optional[bool] = Field(None, description="Allow the agent to delegate tasks to other agents.")
22
+
23
+ # Capabilities
24
+ reasoning: Optional[bool] = Field(None, description="Enable reasoning model.")
25
+ max_reasoning_attempts: Optional[int] = Field(None, description="Maximum reasoning attempts when reasoning is enabled.")
26
+ multimodal: Optional[bool] = Field(None, description="Enable multimodal (image/audio) inputs.")
27
+ skills: Optional[List[str]] = Field(None, description="Skill names or paths to attach to the agent.")
28
+ tools: Optional[List[str]] = Field(None, description="Tool names resolved against the tool registry.")
29
+ mcp_servers: Optional[List[McpServerConfig]] = Field(None, description="MCP server configurations for this agent.")
30
+
31
+ # Prompt customization
32
+ system_template: Optional[str] = Field(None, description="System template override.")
33
+ prompt_template: Optional[str] = Field(None, description="Prompt template override.")
34
+ response_template: Optional[str] = Field(None, description="Response template override.")
@@ -0,0 +1,30 @@
1
+ # src/nikhil/amsha/toolkit/crew_forge/domain/crew_data.py
2
+ from typing import Any, Dict, Optional, Union
3
+
4
+ from crewai.llms.base_llm import BaseLLM
5
+ from pydantic import BaseModel, Field, ConfigDict
6
+
7
+
8
+ class CrewData(BaseModel):
9
+ model_config = ConfigDict(arbitrary_types_allowed=True)
10
+ llm: BaseLLM = Field(..., description="Model name")
11
+ module_name: str = Field(..., description="The name of module")
12
+ output_dir_path: Optional[str] = Field(..., description="output dir path")
13
+ memory: bool = Field(
14
+ default=False,
15
+ description="Whether the crew uses CrewAI's unified memory",
16
+ )
17
+ checkpoint: Optional[Union[bool, Dict[str, Any]]] = Field(
18
+ default=None,
19
+ description=(
20
+ "Checkpoint configuration: a bool to enable/disable CrewAI checkpointing, "
21
+ "or a dict with keys enabled/on_events/provider/location/max_checkpoints."
22
+ ),
23
+ )
24
+ tracing: Optional[bool] = Field(
25
+ default=None,
26
+ description=(
27
+ "Enable CrewAI native tracing (sends full prompt/response content "
28
+ "to CrewAI's hosted dashboard). Off by default — requires crewai login."
29
+ ),
30
+ )
@@ -0,0 +1,49 @@
1
+ # src/nikhil/amsha/crew_forge/domain/models/mcp_data.py
2
+ """MCP server configuration models for YAML config-as-code.
3
+
4
+ These models constrain what's expressible in YAML — no raw dict passthrough.
5
+ """
6
+ from typing import Any, Dict, List, Literal, Optional
7
+
8
+ from pydantic import BaseModel, Field
9
+
10
+
11
+ class McpServerConfig(BaseModel):
12
+ """Structured MCP server configuration — Amsha's YAML-facing schema.
13
+
14
+ ``transport`` discriminates which fields are relevant:
15
+ - ``stdio``: ``command`` + ``args`` + ``env``
16
+ - ``http`` / ``sse``: ``url`` + ``headers``
17
+ """
18
+
19
+ transport: Literal["stdio", "http", "sse"] = Field(
20
+ ...,
21
+ description="MCP transport type.",
22
+ )
23
+ # stdio fields
24
+ command: Optional[str] = Field(
25
+ None,
26
+ description="Command to execute for stdio transport (e.g. 'python', 'node').",
27
+ )
28
+ args: Optional[List[str]] = Field(
29
+ None,
30
+ description="Command arguments for stdio transport.",
31
+ )
32
+ env: Optional[Dict[str, str]] = Field(
33
+ None,
34
+ description="Environment variables for stdio transport (replaces parent env).",
35
+ )
36
+ # http / sse fields
37
+ url: Optional[str] = Field(
38
+ None,
39
+ description="Server URL for http/sse transport.",
40
+ )
41
+ headers: Optional[Dict[str, str]] = Field(
42
+ None,
43
+ description="HTTP headers for http/sse transport (e.g. auth tokens).",
44
+ )
45
+ # common
46
+ connect_timeout: int = Field(
47
+ 30,
48
+ description="Connection timeout in seconds.",
49
+ )
@@ -0,0 +1,22 @@
1
+ # src/nikhil/amsha/toolkit/crew_forge/domain/task_data.py
2
+ from typing import List, Optional
3
+
4
+ from pydantic import BaseModel, Field
5
+
6
+
7
+ class TaskRequest(BaseModel):
8
+ name: str = Field(..., description="The name of the task.")
9
+ description: str = Field(..., description="Description of the task to be performed.")
10
+ expected_output: str = Field(..., description="The expected output or result of the task.")
11
+ usecase: Optional[str] = Field(None, description="The Usecase or context for the task for its use")
12
+
13
+ # Execution
14
+ context: Optional[List[str]] = Field(None, description="Names of prerequisite TaskRequests whose output feeds this task.")
15
+ tools: Optional[List[str]] = Field(None, description="Tool names resolved against the tool registry. Task-level tools override agent-level tools.")
16
+ async_execution: Optional[bool] = Field(None, description="Execute this task asynchronously.")
17
+ human_input: Optional[bool] = Field(None, description="Require human input before final output.")
18
+ markdown: Optional[bool] = Field(None, description="Render the task output as markdown.")
19
+
20
+ # Guardrails
21
+ guardrail: Optional[str] = Field(None, description="LLM-based guardrail description for validating task output.")
22
+ guardrail_max_retries: Optional[int] = Field(None, description="Maximum retries when the guardrail fails.")