claude-mpm 4.13.2__py3-none-any.whl → 4.18.2__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 (250) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_ENGINEER.md +286 -0
  3. claude_mpm/agents/BASE_PM.md +48 -17
  4. claude_mpm/agents/OUTPUT_STYLE.md +329 -11
  5. claude_mpm/agents/PM_INSTRUCTIONS.md +227 -8
  6. claude_mpm/agents/agent_loader.py +17 -5
  7. claude_mpm/agents/frontmatter_validator.py +284 -253
  8. claude_mpm/agents/templates/agentic-coder-optimizer.json +9 -2
  9. claude_mpm/agents/templates/api_qa.json +7 -1
  10. claude_mpm/agents/templates/clerk-ops.json +8 -1
  11. claude_mpm/agents/templates/code_analyzer.json +4 -1
  12. claude_mpm/agents/templates/dart_engineer.json +11 -1
  13. claude_mpm/agents/templates/data_engineer.json +11 -1
  14. claude_mpm/agents/templates/documentation.json +6 -1
  15. claude_mpm/agents/templates/engineer.json +18 -1
  16. claude_mpm/agents/templates/gcp_ops_agent.json +8 -1
  17. claude_mpm/agents/templates/golang_engineer.json +11 -1
  18. claude_mpm/agents/templates/java_engineer.json +12 -2
  19. claude_mpm/agents/templates/local_ops_agent.json +1217 -6
  20. claude_mpm/agents/templates/nextjs_engineer.json +11 -1
  21. claude_mpm/agents/templates/ops.json +8 -1
  22. claude_mpm/agents/templates/php-engineer.json +11 -1
  23. claude_mpm/agents/templates/project_organizer.json +10 -3
  24. claude_mpm/agents/templates/prompt-engineer.json +5 -1
  25. claude_mpm/agents/templates/python_engineer.json +11 -1
  26. claude_mpm/agents/templates/qa.json +7 -1
  27. claude_mpm/agents/templates/react_engineer.json +11 -1
  28. claude_mpm/agents/templates/refactoring_engineer.json +8 -1
  29. claude_mpm/agents/templates/research.json +4 -1
  30. claude_mpm/agents/templates/ruby-engineer.json +11 -1
  31. claude_mpm/agents/templates/rust_engineer.json +11 -1
  32. claude_mpm/agents/templates/security.json +6 -1
  33. claude_mpm/agents/templates/svelte-engineer.json +225 -0
  34. claude_mpm/agents/templates/ticketing.json +6 -1
  35. claude_mpm/agents/templates/typescript_engineer.json +11 -1
  36. claude_mpm/agents/templates/vercel_ops_agent.json +8 -1
  37. claude_mpm/agents/templates/version_control.json +8 -1
  38. claude_mpm/agents/templates/web_qa.json +7 -1
  39. claude_mpm/agents/templates/web_ui.json +11 -1
  40. claude_mpm/cli/__init__.py +34 -706
  41. claude_mpm/cli/commands/agent_manager.py +25 -12
  42. claude_mpm/cli/commands/agent_state_manager.py +186 -0
  43. claude_mpm/cli/commands/agents.py +204 -148
  44. claude_mpm/cli/commands/aggregate.py +7 -3
  45. claude_mpm/cli/commands/analyze.py +9 -4
  46. claude_mpm/cli/commands/analyze_code.py +7 -2
  47. claude_mpm/cli/commands/auto_configure.py +7 -9
  48. claude_mpm/cli/commands/config.py +47 -13
  49. claude_mpm/cli/commands/configure.py +294 -1788
  50. claude_mpm/cli/commands/configure_agent_display.py +261 -0
  51. claude_mpm/cli/commands/configure_behavior_manager.py +204 -0
  52. claude_mpm/cli/commands/configure_hook_manager.py +225 -0
  53. claude_mpm/cli/commands/configure_models.py +18 -0
  54. claude_mpm/cli/commands/configure_navigation.py +167 -0
  55. claude_mpm/cli/commands/configure_paths.py +104 -0
  56. claude_mpm/cli/commands/configure_persistence.py +254 -0
  57. claude_mpm/cli/commands/configure_startup_manager.py +646 -0
  58. claude_mpm/cli/commands/configure_template_editor.py +497 -0
  59. claude_mpm/cli/commands/configure_validators.py +73 -0
  60. claude_mpm/cli/commands/local_deploy.py +537 -0
  61. claude_mpm/cli/commands/memory.py +54 -20
  62. claude_mpm/cli/commands/mpm_init.py +39 -25
  63. claude_mpm/cli/commands/mpm_init_handler.py +8 -3
  64. claude_mpm/cli/executor.py +202 -0
  65. claude_mpm/cli/helpers.py +105 -0
  66. claude_mpm/cli/interactive/__init__.py +3 -0
  67. claude_mpm/cli/interactive/skills_wizard.py +491 -0
  68. claude_mpm/cli/parsers/__init__.py +7 -1
  69. claude_mpm/cli/parsers/base_parser.py +98 -3
  70. claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
  71. claude_mpm/cli/shared/output_formatters.py +28 -19
  72. claude_mpm/cli/startup.py +481 -0
  73. claude_mpm/cli/utils.py +52 -1
  74. claude_mpm/commands/mpm-help.md +3 -0
  75. claude_mpm/commands/mpm-version.md +113 -0
  76. claude_mpm/commands/mpm.md +1 -0
  77. claude_mpm/config/agent_config.py +2 -2
  78. claude_mpm/config/model_config.py +428 -0
  79. claude_mpm/core/base_service.py +13 -12
  80. claude_mpm/core/enums.py +452 -0
  81. claude_mpm/core/factories.py +1 -1
  82. claude_mpm/core/instruction_reinforcement_hook.py +2 -1
  83. claude_mpm/core/interactive_session.py +9 -3
  84. claude_mpm/core/logging_config.py +6 -2
  85. claude_mpm/core/oneshot_session.py +8 -4
  86. claude_mpm/core/optimized_agent_loader.py +3 -3
  87. claude_mpm/core/output_style_manager.py +12 -192
  88. claude_mpm/core/service_registry.py +5 -1
  89. claude_mpm/core/types.py +2 -9
  90. claude_mpm/core/typing_utils.py +7 -6
  91. claude_mpm/dashboard/static/js/dashboard.js +0 -14
  92. claude_mpm/dashboard/templates/index.html +3 -41
  93. claude_mpm/hooks/claude_hooks/response_tracking.py +35 -1
  94. claude_mpm/hooks/instruction_reinforcement.py +7 -2
  95. claude_mpm/models/resume_log.py +340 -0
  96. claude_mpm/services/agents/auto_config_manager.py +10 -11
  97. claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
  98. claude_mpm/services/agents/deployment/agent_record_service.py +1 -1
  99. claude_mpm/services/agents/deployment/agent_validator.py +17 -1
  100. claude_mpm/services/agents/deployment/async_agent_deployment.py +1 -1
  101. claude_mpm/services/agents/deployment/interface_adapter.py +3 -2
  102. claude_mpm/services/agents/deployment/local_template_deployment.py +1 -1
  103. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +7 -6
  104. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +7 -16
  105. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +4 -3
  106. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +5 -3
  107. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +6 -5
  108. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +9 -6
  109. claude_mpm/services/agents/deployment/validation/__init__.py +3 -1
  110. claude_mpm/services/agents/deployment/validation/validation_result.py +1 -9
  111. claude_mpm/services/agents/local_template_manager.py +1 -1
  112. claude_mpm/services/agents/memory/agent_memory_manager.py +5 -2
  113. claude_mpm/services/agents/registry/modification_tracker.py +5 -2
  114. claude_mpm/services/command_handler_service.py +11 -5
  115. claude_mpm/services/core/interfaces/__init__.py +74 -2
  116. claude_mpm/services/core/interfaces/health.py +172 -0
  117. claude_mpm/services/core/interfaces/model.py +281 -0
  118. claude_mpm/services/core/interfaces/process.py +372 -0
  119. claude_mpm/services/core/interfaces/restart.py +307 -0
  120. claude_mpm/services/core/interfaces/stability.py +260 -0
  121. claude_mpm/services/core/models/__init__.py +33 -0
  122. claude_mpm/services/core/models/agent_config.py +12 -28
  123. claude_mpm/services/core/models/health.py +162 -0
  124. claude_mpm/services/core/models/process.py +235 -0
  125. claude_mpm/services/core/models/restart.py +302 -0
  126. claude_mpm/services/core/models/stability.py +264 -0
  127. claude_mpm/services/core/path_resolver.py +23 -7
  128. claude_mpm/services/diagnostics/__init__.py +2 -2
  129. claude_mpm/services/diagnostics/checks/agent_check.py +25 -24
  130. claude_mpm/services/diagnostics/checks/claude_code_check.py +24 -23
  131. claude_mpm/services/diagnostics/checks/common_issues_check.py +25 -24
  132. claude_mpm/services/diagnostics/checks/configuration_check.py +24 -23
  133. claude_mpm/services/diagnostics/checks/filesystem_check.py +18 -17
  134. claude_mpm/services/diagnostics/checks/installation_check.py +30 -29
  135. claude_mpm/services/diagnostics/checks/instructions_check.py +20 -19
  136. claude_mpm/services/diagnostics/checks/mcp_check.py +50 -36
  137. claude_mpm/services/diagnostics/checks/mcp_services_check.py +36 -31
  138. claude_mpm/services/diagnostics/checks/monitor_check.py +23 -22
  139. claude_mpm/services/diagnostics/checks/startup_log_check.py +9 -8
  140. claude_mpm/services/diagnostics/diagnostic_runner.py +6 -5
  141. claude_mpm/services/diagnostics/doctor_reporter.py +28 -25
  142. claude_mpm/services/diagnostics/models.py +19 -24
  143. claude_mpm/services/infrastructure/monitoring/__init__.py +1 -1
  144. claude_mpm/services/infrastructure/monitoring/aggregator.py +12 -12
  145. claude_mpm/services/infrastructure/monitoring/base.py +5 -13
  146. claude_mpm/services/infrastructure/monitoring/network.py +7 -6
  147. claude_mpm/services/infrastructure/monitoring/process.py +13 -12
  148. claude_mpm/services/infrastructure/monitoring/resources.py +7 -6
  149. claude_mpm/services/infrastructure/monitoring/service.py +16 -15
  150. claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
  151. claude_mpm/services/local_ops/__init__.py +163 -0
  152. claude_mpm/services/local_ops/crash_detector.py +257 -0
  153. claude_mpm/services/local_ops/health_checks/__init__.py +28 -0
  154. claude_mpm/services/local_ops/health_checks/http_check.py +224 -0
  155. claude_mpm/services/local_ops/health_checks/process_check.py +236 -0
  156. claude_mpm/services/local_ops/health_checks/resource_check.py +255 -0
  157. claude_mpm/services/local_ops/health_manager.py +430 -0
  158. claude_mpm/services/local_ops/log_monitor.py +396 -0
  159. claude_mpm/services/local_ops/memory_leak_detector.py +294 -0
  160. claude_mpm/services/local_ops/process_manager.py +595 -0
  161. claude_mpm/services/local_ops/resource_monitor.py +331 -0
  162. claude_mpm/services/local_ops/restart_manager.py +401 -0
  163. claude_mpm/services/local_ops/restart_policy.py +387 -0
  164. claude_mpm/services/local_ops/state_manager.py +372 -0
  165. claude_mpm/services/local_ops/unified_manager.py +600 -0
  166. claude_mpm/services/mcp_config_manager.py +9 -4
  167. claude_mpm/services/mcp_gateway/core/__init__.py +1 -2
  168. claude_mpm/services/mcp_gateway/core/base.py +18 -31
  169. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +71 -24
  170. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +30 -28
  171. claude_mpm/services/memory_hook_service.py +4 -1
  172. claude_mpm/services/model/__init__.py +147 -0
  173. claude_mpm/services/model/base_provider.py +365 -0
  174. claude_mpm/services/model/claude_provider.py +412 -0
  175. claude_mpm/services/model/model_router.py +453 -0
  176. claude_mpm/services/model/ollama_provider.py +415 -0
  177. claude_mpm/services/monitor/daemon_manager.py +3 -2
  178. claude_mpm/services/monitor/handlers/dashboard.py +2 -1
  179. claude_mpm/services/monitor/handlers/hooks.py +2 -1
  180. claude_mpm/services/monitor/management/lifecycle.py +3 -2
  181. claude_mpm/services/monitor/server.py +2 -1
  182. claude_mpm/services/session_management_service.py +3 -2
  183. claude_mpm/services/session_manager.py +205 -1
  184. claude_mpm/services/shared/async_service_base.py +16 -27
  185. claude_mpm/services/shared/lifecycle_service_base.py +1 -14
  186. claude_mpm/services/socketio/handlers/__init__.py +5 -2
  187. claude_mpm/services/socketio/handlers/hook.py +13 -2
  188. claude_mpm/services/socketio/handlers/registry.py +4 -2
  189. claude_mpm/services/socketio/server/main.py +10 -8
  190. claude_mpm/services/subprocess_launcher_service.py +14 -5
  191. claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +8 -7
  192. claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +6 -5
  193. claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +8 -7
  194. claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +7 -6
  195. claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +5 -4
  196. claude_mpm/services/unified/config_strategies/validation_strategy.py +13 -9
  197. claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +10 -3
  198. claude_mpm/services/unified/deployment_strategies/local.py +6 -5
  199. claude_mpm/services/unified/deployment_strategies/utils.py +6 -5
  200. claude_mpm/services/unified/deployment_strategies/vercel.py +7 -6
  201. claude_mpm/services/unified/interfaces.py +3 -1
  202. claude_mpm/services/unified/unified_analyzer.py +14 -10
  203. claude_mpm/services/unified/unified_config.py +2 -1
  204. claude_mpm/services/unified/unified_deployment.py +9 -4
  205. claude_mpm/services/version_service.py +104 -1
  206. claude_mpm/skills/__init__.py +21 -0
  207. claude_mpm/skills/bundled/__init__.py +6 -0
  208. claude_mpm/skills/bundled/api-documentation.md +393 -0
  209. claude_mpm/skills/bundled/async-testing.md +571 -0
  210. claude_mpm/skills/bundled/code-review.md +143 -0
  211. claude_mpm/skills/bundled/database-migration.md +199 -0
  212. claude_mpm/skills/bundled/docker-containerization.md +194 -0
  213. claude_mpm/skills/bundled/express-local-dev.md +1429 -0
  214. claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
  215. claude_mpm/skills/bundled/git-workflow.md +414 -0
  216. claude_mpm/skills/bundled/imagemagick.md +204 -0
  217. claude_mpm/skills/bundled/json-data-handling.md +223 -0
  218. claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
  219. claude_mpm/skills/bundled/pdf.md +141 -0
  220. claude_mpm/skills/bundled/performance-profiling.md +567 -0
  221. claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
  222. claude_mpm/skills/bundled/security-scanning.md +327 -0
  223. claude_mpm/skills/bundled/systematic-debugging.md +473 -0
  224. claude_mpm/skills/bundled/test-driven-development.md +378 -0
  225. claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
  226. claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
  227. claude_mpm/skills/bundled/xlsx.md +157 -0
  228. claude_mpm/skills/registry.py +286 -0
  229. claude_mpm/skills/skill_manager.py +310 -0
  230. claude_mpm/tools/code_tree_analyzer.py +177 -141
  231. claude_mpm/tools/code_tree_events.py +4 -2
  232. claude_mpm/utils/agent_dependency_loader.py +2 -2
  233. {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/METADATA +117 -8
  234. {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/RECORD +238 -174
  235. claude_mpm/dashboard/static/css/code-tree.css +0 -1639
  236. claude_mpm/dashboard/static/js/components/code-tree/tree-breadcrumb.js +0 -353
  237. claude_mpm/dashboard/static/js/components/code-tree/tree-constants.js +0 -235
  238. claude_mpm/dashboard/static/js/components/code-tree/tree-search.js +0 -409
  239. claude_mpm/dashboard/static/js/components/code-tree/tree-utils.js +0 -435
  240. claude_mpm/dashboard/static/js/components/code-tree.js +0 -5869
  241. claude_mpm/dashboard/static/js/components/code-viewer.js +0 -1386
  242. claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +0 -425
  243. claude_mpm/hooks/claude_hooks/hook_handler_original.py +0 -1041
  244. claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +0 -347
  245. claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +0 -575
  246. claude_mpm/services/project/analyzer_refactored.py +0 -450
  247. {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/WHEEL +0 -0
  248. {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/entry_points.txt +0 -0
  249. {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/licenses/LICENSE +0 -0
  250. {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,428 @@
1
+ """
2
+ Model Configuration Management for Claude MPM Framework
3
+ =======================================================
4
+
5
+ WHY: Centralizes configuration for model providers, routing strategy, and
6
+ model selection. Supports configuration files, environment variables, and
7
+ programmatic configuration.
8
+
9
+ DESIGN DECISION: Uses Pydantic for configuration validation and type safety.
10
+ Supports loading from YAML files and environment variables with sensible defaults.
11
+
12
+ CONFIGURATION STRUCTURE:
13
+ ```yaml
14
+ content_agent:
15
+ model_provider: auto # auto|ollama|claude|privacy
16
+
17
+ ollama:
18
+ enabled: true
19
+ host: http://localhost:11434
20
+ fallback_to_cloud: true
21
+ timeout: 30
22
+ models:
23
+ seo_analysis: llama3.3:70b
24
+ readability: gemma2:9b
25
+ grammar: qwen3:14b
26
+ summarization: mistral:7b
27
+ keyword_extraction: seoassistant
28
+
29
+ claude:
30
+ enabled: true
31
+ model: claude-3-5-sonnet-20241022
32
+ max_tokens: 4096
33
+ temperature: 0.7
34
+ ```
35
+
36
+ ENVIRONMENT VARIABLES:
37
+ - MODEL_PROVIDER: Override provider strategy
38
+ - OLLAMA_HOST: Override Ollama endpoint
39
+ - OLLAMA_ENABLED: Enable/disable Ollama
40
+ - CLAUDE_ENABLED: Enable/disable Claude
41
+ - ANTHROPIC_API_KEY: Claude API key
42
+ """
43
+
44
+ import os
45
+ from pathlib import Path
46
+ from typing import Any, Dict, Optional
47
+
48
+ from pydantic import BaseModel, Field
49
+
50
+ try:
51
+ import yaml
52
+ except ImportError:
53
+ yaml = None # type: ignore
54
+
55
+
56
+ class OllamaConfig(BaseModel):
57
+ """
58
+ Configuration for Ollama provider.
59
+
60
+ WHY: Separates Ollama-specific settings for better organization
61
+ and validation.
62
+ """
63
+
64
+ enabled: bool = Field(default=True, description="Enable Ollama provider")
65
+ host: str = Field(
66
+ default="http://localhost:11434",
67
+ description="Ollama API endpoint",
68
+ )
69
+ fallback_to_cloud: bool = Field(
70
+ default=True,
71
+ description="Allow fallback to cloud on Ollama failure",
72
+ )
73
+ timeout: int = Field(default=30, description="Request timeout in seconds")
74
+ models: Dict[str, str] = Field(
75
+ default_factory=dict,
76
+ description="Task-specific model mappings",
77
+ )
78
+
79
+ class Config:
80
+ """Pydantic config."""
81
+
82
+ extra = "allow"
83
+
84
+
85
+ class ClaudeConfig(BaseModel):
86
+ """
87
+ Configuration for Claude provider.
88
+
89
+ WHY: Separates Claude-specific settings for better organization
90
+ and validation.
91
+ """
92
+
93
+ enabled: bool = Field(default=True, description="Enable Claude provider")
94
+ model: str = Field(
95
+ default="claude-3-5-sonnet-20241022",
96
+ description="Default Claude model",
97
+ )
98
+ max_tokens: int = Field(
99
+ default=4096,
100
+ description="Maximum response tokens",
101
+ )
102
+ temperature: float = Field(
103
+ default=0.7,
104
+ description="Sampling temperature (0.0-1.0)",
105
+ )
106
+ api_key: Optional[str] = Field(
107
+ default=None,
108
+ description="Anthropic API key (can use env var)",
109
+ )
110
+
111
+ class Config:
112
+ """Pydantic config."""
113
+
114
+ extra = "allow"
115
+
116
+
117
+ class ModelProviderConfig(BaseModel):
118
+ """
119
+ Main model provider configuration.
120
+
121
+ WHY: Top-level configuration containing all model-related settings.
122
+ Validates configuration at load time to catch errors early.
123
+ """
124
+
125
+ provider: str = Field(
126
+ default="auto",
127
+ description="Provider strategy: auto|ollama|claude|privacy",
128
+ )
129
+ ollama: OllamaConfig = Field(
130
+ default_factory=OllamaConfig,
131
+ description="Ollama provider configuration",
132
+ )
133
+ claude: ClaudeConfig = Field(
134
+ default_factory=ClaudeConfig,
135
+ description="Claude provider configuration",
136
+ )
137
+
138
+ class Config:
139
+ """Pydantic config."""
140
+
141
+ extra = "allow"
142
+
143
+
144
+ class ModelConfigManager:
145
+ """
146
+ Manager for model configuration.
147
+
148
+ WHY: Provides centralized configuration loading with support for
149
+ multiple sources (files, env vars, defaults) and validation.
150
+
151
+ Usage:
152
+ manager = ModelConfigManager()
153
+ config = manager.load_config()
154
+
155
+ # Get router config
156
+ router_config = manager.get_router_config(config)
157
+
158
+ # Get provider configs
159
+ ollama_config = manager.get_ollama_config(config)
160
+ claude_config = manager.get_claude_config(config)
161
+ """
162
+
163
+ DEFAULT_CONFIG_PATHS = [
164
+ ".claude/configuration.yaml",
165
+ "configuration.yaml",
166
+ ".claude-mpm/configuration.yaml",
167
+ str(Path("~/.claude-mpm/configuration.yaml").expanduser()),
168
+ ]
169
+
170
+ @staticmethod
171
+ def load_config(
172
+ config_path: Optional[str] = None,
173
+ ) -> ModelProviderConfig:
174
+ """
175
+ Load model configuration from file and environment.
176
+
177
+ WHY: Supports multiple configuration sources with priority:
178
+ 1. Explicit config_path parameter
179
+ 2. Environment variables
180
+ 3. Configuration file
181
+ 4. Default values
182
+
183
+ Args:
184
+ config_path: Optional path to configuration file
185
+
186
+ Returns:
187
+ ModelProviderConfig with merged settings
188
+ """
189
+ config_data: Dict[str, Any] = {}
190
+
191
+ # Try to load from file
192
+ if config_path and Path(config_path).exists():
193
+ config_data = ModelConfigManager._load_yaml_file(config_path)
194
+ else:
195
+ # Try default paths
196
+ for default_path in ModelConfigManager.DEFAULT_CONFIG_PATHS:
197
+ if Path(default_path).exists():
198
+ config_data = ModelConfigManager._load_yaml_file(default_path)
199
+ break
200
+
201
+ # Extract content_agent section if present
202
+ if "content_agent" in config_data:
203
+ config_data = config_data["content_agent"]
204
+
205
+ # Override with environment variables
206
+ config_data = ModelConfigManager._apply_env_overrides(config_data)
207
+
208
+ # Create and validate config
209
+ try:
210
+ return ModelProviderConfig(**config_data)
211
+ except Exception as e:
212
+ # If validation fails, return default config
213
+ print(f"Warning: Failed to load config: {e}. Using defaults.")
214
+ return ModelProviderConfig()
215
+
216
+ @staticmethod
217
+ def _load_yaml_file(path: str) -> Dict[str, Any]:
218
+ """
219
+ Load YAML configuration file.
220
+
221
+ Args:
222
+ path: Path to YAML file
223
+
224
+ Returns:
225
+ Dictionary of configuration
226
+ """
227
+ if yaml is None:
228
+ return {}
229
+
230
+ try:
231
+ path_obj = Path(path)
232
+ with path_obj.open() as f:
233
+ data = yaml.safe_load(f)
234
+ return data or {}
235
+ except Exception as e:
236
+ print(f"Warning: Failed to load {path}: {e}")
237
+ return {}
238
+
239
+ @staticmethod
240
+ def _apply_env_overrides(config: Dict[str, Any]) -> Dict[str, Any]:
241
+ """
242
+ Apply environment variable overrides.
243
+
244
+ WHY: Allows runtime configuration without modifying files.
245
+ Useful for containerized deployments and CI/CD.
246
+
247
+ Args:
248
+ config: Base configuration
249
+
250
+ Returns:
251
+ Configuration with env overrides applied
252
+ """
253
+ # Provider strategy
254
+ if "MODEL_PROVIDER" in os.environ:
255
+ config["provider"] = os.environ["MODEL_PROVIDER"]
256
+
257
+ # Ollama settings
258
+ if "ollama" not in config:
259
+ config["ollama"] = {}
260
+
261
+ if "OLLAMA_ENABLED" in os.environ:
262
+ config["ollama"]["enabled"] = os.environ["OLLAMA_ENABLED"].lower() == "true"
263
+
264
+ if "OLLAMA_HOST" in os.environ:
265
+ config["ollama"]["host"] = os.environ["OLLAMA_HOST"]
266
+
267
+ if "OLLAMA_TIMEOUT" in os.environ:
268
+ try:
269
+ config["ollama"]["timeout"] = int(os.environ["OLLAMA_TIMEOUT"])
270
+ except ValueError:
271
+ pass
272
+
273
+ if "OLLAMA_FALLBACK_TO_CLOUD" in os.environ:
274
+ config["ollama"]["fallback_to_cloud"] = (
275
+ os.environ["OLLAMA_FALLBACK_TO_CLOUD"].lower() == "true"
276
+ )
277
+
278
+ # Claude settings
279
+ if "claude" not in config:
280
+ config["claude"] = {}
281
+
282
+ if "CLAUDE_ENABLED" in os.environ:
283
+ config["claude"]["enabled"] = os.environ["CLAUDE_ENABLED"].lower() == "true"
284
+
285
+ if "ANTHROPIC_API_KEY" in os.environ:
286
+ config["claude"]["api_key"] = os.environ["ANTHROPIC_API_KEY"]
287
+
288
+ if "CLAUDE_MODEL" in os.environ:
289
+ config["claude"]["model"] = os.environ["CLAUDE_MODEL"]
290
+
291
+ if "CLAUDE_MAX_TOKENS" in os.environ:
292
+ try:
293
+ config["claude"]["max_tokens"] = int(os.environ["CLAUDE_MAX_TOKENS"])
294
+ except ValueError:
295
+ pass
296
+
297
+ if "CLAUDE_TEMPERATURE" in os.environ:
298
+ try:
299
+ config["claude"]["temperature"] = float(
300
+ os.environ["CLAUDE_TEMPERATURE"]
301
+ )
302
+ except ValueError:
303
+ pass
304
+
305
+ return config
306
+
307
+ @staticmethod
308
+ def get_router_config(config: ModelProviderConfig) -> Dict[str, Any]:
309
+ """
310
+ Get router configuration from model config.
311
+
312
+ Args:
313
+ config: Model provider configuration
314
+
315
+ Returns:
316
+ Dictionary suitable for ModelRouter initialization
317
+ """
318
+ return {
319
+ "strategy": config.provider,
320
+ "fallback_enabled": config.ollama.fallback_to_cloud,
321
+ "ollama_config": ModelConfigManager.get_ollama_config(config),
322
+ "claude_config": ModelConfigManager.get_claude_config(config),
323
+ }
324
+
325
+ @staticmethod
326
+ def get_ollama_config(config: ModelProviderConfig) -> Dict[str, Any]:
327
+ """
328
+ Get Ollama provider configuration.
329
+
330
+ Args:
331
+ config: Model provider configuration
332
+
333
+ Returns:
334
+ Dictionary suitable for OllamaProvider initialization
335
+ """
336
+ return {
337
+ "host": config.ollama.host,
338
+ "timeout": config.ollama.timeout,
339
+ "models": config.ollama.models,
340
+ }
341
+
342
+ @staticmethod
343
+ def get_claude_config(config: ModelProviderConfig) -> Dict[str, Any]:
344
+ """
345
+ Get Claude provider configuration.
346
+
347
+ Args:
348
+ config: Model provider configuration
349
+
350
+ Returns:
351
+ Dictionary suitable for ClaudeProvider initialization
352
+ """
353
+ return {
354
+ "api_key": config.claude.api_key,
355
+ "model": config.claude.model,
356
+ "max_tokens": config.claude.max_tokens,
357
+ "temperature": config.claude.temperature,
358
+ }
359
+
360
+ @staticmethod
361
+ def create_sample_config(output_path: str) -> None:
362
+ """
363
+ Create sample configuration file.
364
+
365
+ WHY: Helps users get started with proper configuration.
366
+
367
+ Args:
368
+ output_path: Path where to write sample config
369
+ """
370
+ sample_config = """# Claude MPM Model Provider Configuration
371
+ # ==========================================
372
+
373
+ content_agent:
374
+ # Provider strategy: auto|ollama|claude|privacy
375
+ # - auto: Try Ollama first, fallback to Claude
376
+ # - ollama: Local-only, fail if unavailable
377
+ # - claude: Cloud-only, always use Claude
378
+ # - privacy: Like ollama but with privacy-focused error messages
379
+ model_provider: auto
380
+
381
+ # Ollama Configuration (local models)
382
+ ollama:
383
+ enabled: true
384
+ host: http://localhost:11434
385
+ fallback_to_cloud: true # Allow fallback to Claude on error
386
+ timeout: 30 # Request timeout in seconds
387
+
388
+ # Task-specific model mappings (optional)
389
+ # Defaults are provided if not specified
390
+ models:
391
+ seo_analysis: llama3.3:70b
392
+ readability: gemma2:9b
393
+ grammar: qwen3:14b
394
+ summarization: mistral:7b
395
+ keyword_extraction: seoassistant
396
+ accessibility: gemma2:9b
397
+ sentiment: gemma2:9b
398
+ general: gemma2:9b
399
+
400
+ # Claude Configuration (cloud models)
401
+ claude:
402
+ enabled: true
403
+ model: claude-3-5-sonnet-20241022
404
+ max_tokens: 4096
405
+ temperature: 0.7
406
+ # api_key: sk-ant-... # Or use ANTHROPIC_API_KEY env var
407
+
408
+ # Environment Variable Overrides:
409
+ # - MODEL_PROVIDER: Override provider strategy
410
+ # - OLLAMA_HOST: Override Ollama endpoint
411
+ # - OLLAMA_ENABLED: Enable/disable Ollama (true/false)
412
+ # - CLAUDE_ENABLED: Enable/disable Claude (true/false)
413
+ # - ANTHROPIC_API_KEY: Claude API key
414
+ # - CLAUDE_MODEL: Override Claude model
415
+ """
416
+
417
+ output_path_obj = Path(output_path)
418
+ output_path_obj.parent.mkdir(parents=True, exist_ok=True)
419
+ with output_path_obj.open("w") as f:
420
+ f.write(sample_config)
421
+
422
+
423
+ __all__ = [
424
+ "ClaudeConfig",
425
+ "ModelConfigManager",
426
+ "ModelProviderConfig",
427
+ "OllamaConfig",
428
+ ]
@@ -31,6 +31,7 @@ from pathlib import Path
31
31
  from typing import Any, Dict, List, Optional
32
32
 
33
33
  from .config import Config
34
+ from .enums import HealthStatus
34
35
  from .mixins import LoggerMixin
35
36
 
36
37
 
@@ -38,7 +39,7 @@ from .mixins import LoggerMixin
38
39
  class ServiceHealth:
39
40
  """Service health status information."""
40
41
 
41
- status: str # healthy, degraded, unhealthy, unknown
42
+ status: HealthStatus # Type-safe health status using enum
42
43
  message: str
43
44
  timestamp: str
44
45
  metrics: Dict[str, Any] = field(default_factory=dict)
@@ -142,7 +143,7 @@ class BaseService(LoggerMixin, ABC):
142
143
 
143
144
  # Health and metrics
144
145
  self._health = ServiceHealth(
145
- status="unknown",
146
+ status=HealthStatus.UNKNOWN,
146
147
  message="Service not started",
147
148
  timestamp=datetime.now(timezone.utc).isoformat(),
148
149
  )
@@ -235,7 +236,7 @@ class BaseService(LoggerMixin, ABC):
235
236
  except Exception as e:
236
237
  self.logger.error(f"Failed to start service {self.name}: {e}")
237
238
  self._health = ServiceHealth(
238
- status="unhealthy",
239
+ status=HealthStatus.UNHEALTHY,
239
240
  message=f"Startup failed: {e!s}",
240
241
  timestamp=datetime.now(timezone.utc).isoformat(),
241
242
  checks={"startup": False},
@@ -272,7 +273,7 @@ class BaseService(LoggerMixin, ABC):
272
273
 
273
274
  # Update health status
274
275
  self._health = ServiceHealth(
275
- status="healthy",
276
+ status=HealthStatus.HEALTHY,
276
277
  message="Service started successfully",
277
278
  timestamp=datetime.now(timezone.utc).isoformat(),
278
279
  checks={"startup": True},
@@ -336,7 +337,7 @@ class BaseService(LoggerMixin, ABC):
336
337
 
337
338
  # Update health status
338
339
  self._health = ServiceHealth(
339
- status="unknown",
340
+ status=HealthStatus.UNKNOWN,
340
341
  message="Service stopped",
341
342
  timestamp=datetime.now(timezone.utc).isoformat(),
342
343
  checks={"running": False},
@@ -372,16 +373,16 @@ class BaseService(LoggerMixin, ABC):
372
373
 
373
374
  # Determine overall status
374
375
  if not checks["running"]:
375
- status = "unhealthy"
376
+ status = HealthStatus.UNHEALTHY
376
377
  message = "Service is not running"
377
378
  elif all(checks.values()):
378
- status = "healthy"
379
+ status = HealthStatus.HEALTHY
379
380
  message = "All health checks passed"
380
381
  elif any(checks.values()):
381
- status = "degraded"
382
+ status = HealthStatus.DEGRADED
382
383
  message = "Some health checks failed"
383
384
  else:
384
- status = "unhealthy"
385
+ status = HealthStatus.UNHEALTHY
385
386
  message = "Multiple health checks failed"
386
387
 
387
388
  # Update health status
@@ -402,7 +403,7 @@ class BaseService(LoggerMixin, ABC):
402
403
  except Exception as e:
403
404
  self.logger.error(f"Health check failed for {self.name}: {e}")
404
405
  self._health = ServiceHealth(
405
- status="unhealthy",
406
+ status=HealthStatus.UNHEALTHY,
406
407
  message=f"Health check error: {e!s}",
407
408
  timestamp=datetime.now(timezone.utc).isoformat(),
408
409
  checks={"health_check_error": True},
@@ -593,7 +594,7 @@ class BaseService(LoggerMixin, ABC):
593
594
  )
594
595
  else:
595
596
  return ServiceHealth(
596
- status="degraded",
597
+ status=HealthStatus.DEGRADED,
597
598
  message="Service circuit breaker is open",
598
599
  timestamp=datetime.now(timezone.utc).isoformat(),
599
600
  checks={"circuit_breaker": False},
@@ -604,7 +605,7 @@ class BaseService(LoggerMixin, ABC):
604
605
  health = await self.health_check()
605
606
 
606
607
  # Update circuit breaker
607
- if health.status in ["healthy", "degraded"]:
608
+ if health.status in (HealthStatus.HEALTHY, HealthStatus.DEGRADED):
608
609
  self._record_circuit_success()
609
610
  else:
610
611
  self._record_circuit_failure()