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.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/BASE_ENGINEER.md +286 -0
- claude_mpm/agents/BASE_PM.md +48 -17
- claude_mpm/agents/OUTPUT_STYLE.md +329 -11
- claude_mpm/agents/PM_INSTRUCTIONS.md +227 -8
- claude_mpm/agents/agent_loader.py +17 -5
- claude_mpm/agents/frontmatter_validator.py +284 -253
- claude_mpm/agents/templates/agentic-coder-optimizer.json +9 -2
- claude_mpm/agents/templates/api_qa.json +7 -1
- claude_mpm/agents/templates/clerk-ops.json +8 -1
- claude_mpm/agents/templates/code_analyzer.json +4 -1
- claude_mpm/agents/templates/dart_engineer.json +11 -1
- claude_mpm/agents/templates/data_engineer.json +11 -1
- claude_mpm/agents/templates/documentation.json +6 -1
- claude_mpm/agents/templates/engineer.json +18 -1
- claude_mpm/agents/templates/gcp_ops_agent.json +8 -1
- claude_mpm/agents/templates/golang_engineer.json +11 -1
- claude_mpm/agents/templates/java_engineer.json +12 -2
- claude_mpm/agents/templates/local_ops_agent.json +1217 -6
- claude_mpm/agents/templates/nextjs_engineer.json +11 -1
- claude_mpm/agents/templates/ops.json +8 -1
- claude_mpm/agents/templates/php-engineer.json +11 -1
- claude_mpm/agents/templates/project_organizer.json +10 -3
- claude_mpm/agents/templates/prompt-engineer.json +5 -1
- claude_mpm/agents/templates/python_engineer.json +11 -1
- claude_mpm/agents/templates/qa.json +7 -1
- claude_mpm/agents/templates/react_engineer.json +11 -1
- claude_mpm/agents/templates/refactoring_engineer.json +8 -1
- claude_mpm/agents/templates/research.json +4 -1
- claude_mpm/agents/templates/ruby-engineer.json +11 -1
- claude_mpm/agents/templates/rust_engineer.json +11 -1
- claude_mpm/agents/templates/security.json +6 -1
- claude_mpm/agents/templates/svelte-engineer.json +225 -0
- claude_mpm/agents/templates/ticketing.json +6 -1
- claude_mpm/agents/templates/typescript_engineer.json +11 -1
- claude_mpm/agents/templates/vercel_ops_agent.json +8 -1
- claude_mpm/agents/templates/version_control.json +8 -1
- claude_mpm/agents/templates/web_qa.json +7 -1
- claude_mpm/agents/templates/web_ui.json +11 -1
- claude_mpm/cli/__init__.py +34 -706
- claude_mpm/cli/commands/agent_manager.py +25 -12
- claude_mpm/cli/commands/agent_state_manager.py +186 -0
- claude_mpm/cli/commands/agents.py +204 -148
- claude_mpm/cli/commands/aggregate.py +7 -3
- claude_mpm/cli/commands/analyze.py +9 -4
- claude_mpm/cli/commands/analyze_code.py +7 -2
- claude_mpm/cli/commands/auto_configure.py +7 -9
- claude_mpm/cli/commands/config.py +47 -13
- claude_mpm/cli/commands/configure.py +294 -1788
- claude_mpm/cli/commands/configure_agent_display.py +261 -0
- claude_mpm/cli/commands/configure_behavior_manager.py +204 -0
- claude_mpm/cli/commands/configure_hook_manager.py +225 -0
- claude_mpm/cli/commands/configure_models.py +18 -0
- claude_mpm/cli/commands/configure_navigation.py +167 -0
- claude_mpm/cli/commands/configure_paths.py +104 -0
- claude_mpm/cli/commands/configure_persistence.py +254 -0
- claude_mpm/cli/commands/configure_startup_manager.py +646 -0
- claude_mpm/cli/commands/configure_template_editor.py +497 -0
- claude_mpm/cli/commands/configure_validators.py +73 -0
- claude_mpm/cli/commands/local_deploy.py +537 -0
- claude_mpm/cli/commands/memory.py +54 -20
- claude_mpm/cli/commands/mpm_init.py +39 -25
- claude_mpm/cli/commands/mpm_init_handler.py +8 -3
- claude_mpm/cli/executor.py +202 -0
- claude_mpm/cli/helpers.py +105 -0
- claude_mpm/cli/interactive/__init__.py +3 -0
- claude_mpm/cli/interactive/skills_wizard.py +491 -0
- claude_mpm/cli/parsers/__init__.py +7 -1
- claude_mpm/cli/parsers/base_parser.py +98 -3
- claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
- claude_mpm/cli/shared/output_formatters.py +28 -19
- claude_mpm/cli/startup.py +481 -0
- claude_mpm/cli/utils.py +52 -1
- claude_mpm/commands/mpm-help.md +3 -0
- claude_mpm/commands/mpm-version.md +113 -0
- claude_mpm/commands/mpm.md +1 -0
- claude_mpm/config/agent_config.py +2 -2
- claude_mpm/config/model_config.py +428 -0
- claude_mpm/core/base_service.py +13 -12
- claude_mpm/core/enums.py +452 -0
- claude_mpm/core/factories.py +1 -1
- claude_mpm/core/instruction_reinforcement_hook.py +2 -1
- claude_mpm/core/interactive_session.py +9 -3
- claude_mpm/core/logging_config.py +6 -2
- claude_mpm/core/oneshot_session.py +8 -4
- claude_mpm/core/optimized_agent_loader.py +3 -3
- claude_mpm/core/output_style_manager.py +12 -192
- claude_mpm/core/service_registry.py +5 -1
- claude_mpm/core/types.py +2 -9
- claude_mpm/core/typing_utils.py +7 -6
- claude_mpm/dashboard/static/js/dashboard.js +0 -14
- claude_mpm/dashboard/templates/index.html +3 -41
- claude_mpm/hooks/claude_hooks/response_tracking.py +35 -1
- claude_mpm/hooks/instruction_reinforcement.py +7 -2
- claude_mpm/models/resume_log.py +340 -0
- claude_mpm/services/agents/auto_config_manager.py +10 -11
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
- claude_mpm/services/agents/deployment/agent_record_service.py +1 -1
- claude_mpm/services/agents/deployment/agent_validator.py +17 -1
- claude_mpm/services/agents/deployment/async_agent_deployment.py +1 -1
- claude_mpm/services/agents/deployment/interface_adapter.py +3 -2
- claude_mpm/services/agents/deployment/local_template_deployment.py +1 -1
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +7 -6
- claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +7 -16
- claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +4 -3
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +5 -3
- claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +6 -5
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +9 -6
- claude_mpm/services/agents/deployment/validation/__init__.py +3 -1
- claude_mpm/services/agents/deployment/validation/validation_result.py +1 -9
- claude_mpm/services/agents/local_template_manager.py +1 -1
- claude_mpm/services/agents/memory/agent_memory_manager.py +5 -2
- claude_mpm/services/agents/registry/modification_tracker.py +5 -2
- claude_mpm/services/command_handler_service.py +11 -5
- claude_mpm/services/core/interfaces/__init__.py +74 -2
- claude_mpm/services/core/interfaces/health.py +172 -0
- claude_mpm/services/core/interfaces/model.py +281 -0
- claude_mpm/services/core/interfaces/process.py +372 -0
- claude_mpm/services/core/interfaces/restart.py +307 -0
- claude_mpm/services/core/interfaces/stability.py +260 -0
- claude_mpm/services/core/models/__init__.py +33 -0
- claude_mpm/services/core/models/agent_config.py +12 -28
- claude_mpm/services/core/models/health.py +162 -0
- claude_mpm/services/core/models/process.py +235 -0
- claude_mpm/services/core/models/restart.py +302 -0
- claude_mpm/services/core/models/stability.py +264 -0
- claude_mpm/services/core/path_resolver.py +23 -7
- claude_mpm/services/diagnostics/__init__.py +2 -2
- claude_mpm/services/diagnostics/checks/agent_check.py +25 -24
- claude_mpm/services/diagnostics/checks/claude_code_check.py +24 -23
- claude_mpm/services/diagnostics/checks/common_issues_check.py +25 -24
- claude_mpm/services/diagnostics/checks/configuration_check.py +24 -23
- claude_mpm/services/diagnostics/checks/filesystem_check.py +18 -17
- claude_mpm/services/diagnostics/checks/installation_check.py +30 -29
- claude_mpm/services/diagnostics/checks/instructions_check.py +20 -19
- claude_mpm/services/diagnostics/checks/mcp_check.py +50 -36
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +36 -31
- claude_mpm/services/diagnostics/checks/monitor_check.py +23 -22
- claude_mpm/services/diagnostics/checks/startup_log_check.py +9 -8
- claude_mpm/services/diagnostics/diagnostic_runner.py +6 -5
- claude_mpm/services/diagnostics/doctor_reporter.py +28 -25
- claude_mpm/services/diagnostics/models.py +19 -24
- claude_mpm/services/infrastructure/monitoring/__init__.py +1 -1
- claude_mpm/services/infrastructure/monitoring/aggregator.py +12 -12
- claude_mpm/services/infrastructure/monitoring/base.py +5 -13
- claude_mpm/services/infrastructure/monitoring/network.py +7 -6
- claude_mpm/services/infrastructure/monitoring/process.py +13 -12
- claude_mpm/services/infrastructure/monitoring/resources.py +7 -6
- claude_mpm/services/infrastructure/monitoring/service.py +16 -15
- claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
- claude_mpm/services/local_ops/__init__.py +163 -0
- claude_mpm/services/local_ops/crash_detector.py +257 -0
- claude_mpm/services/local_ops/health_checks/__init__.py +28 -0
- claude_mpm/services/local_ops/health_checks/http_check.py +224 -0
- claude_mpm/services/local_ops/health_checks/process_check.py +236 -0
- claude_mpm/services/local_ops/health_checks/resource_check.py +255 -0
- claude_mpm/services/local_ops/health_manager.py +430 -0
- claude_mpm/services/local_ops/log_monitor.py +396 -0
- claude_mpm/services/local_ops/memory_leak_detector.py +294 -0
- claude_mpm/services/local_ops/process_manager.py +595 -0
- claude_mpm/services/local_ops/resource_monitor.py +331 -0
- claude_mpm/services/local_ops/restart_manager.py +401 -0
- claude_mpm/services/local_ops/restart_policy.py +387 -0
- claude_mpm/services/local_ops/state_manager.py +372 -0
- claude_mpm/services/local_ops/unified_manager.py +600 -0
- claude_mpm/services/mcp_config_manager.py +9 -4
- claude_mpm/services/mcp_gateway/core/__init__.py +1 -2
- claude_mpm/services/mcp_gateway/core/base.py +18 -31
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +71 -24
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +30 -28
- claude_mpm/services/memory_hook_service.py +4 -1
- claude_mpm/services/model/__init__.py +147 -0
- claude_mpm/services/model/base_provider.py +365 -0
- claude_mpm/services/model/claude_provider.py +412 -0
- claude_mpm/services/model/model_router.py +453 -0
- claude_mpm/services/model/ollama_provider.py +415 -0
- claude_mpm/services/monitor/daemon_manager.py +3 -2
- claude_mpm/services/monitor/handlers/dashboard.py +2 -1
- claude_mpm/services/monitor/handlers/hooks.py +2 -1
- claude_mpm/services/monitor/management/lifecycle.py +3 -2
- claude_mpm/services/monitor/server.py +2 -1
- claude_mpm/services/session_management_service.py +3 -2
- claude_mpm/services/session_manager.py +205 -1
- claude_mpm/services/shared/async_service_base.py +16 -27
- claude_mpm/services/shared/lifecycle_service_base.py +1 -14
- claude_mpm/services/socketio/handlers/__init__.py +5 -2
- claude_mpm/services/socketio/handlers/hook.py +13 -2
- claude_mpm/services/socketio/handlers/registry.py +4 -2
- claude_mpm/services/socketio/server/main.py +10 -8
- claude_mpm/services/subprocess_launcher_service.py +14 -5
- claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +8 -7
- claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +6 -5
- claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +8 -7
- claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +7 -6
- claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +5 -4
- claude_mpm/services/unified/config_strategies/validation_strategy.py +13 -9
- claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +10 -3
- claude_mpm/services/unified/deployment_strategies/local.py +6 -5
- claude_mpm/services/unified/deployment_strategies/utils.py +6 -5
- claude_mpm/services/unified/deployment_strategies/vercel.py +7 -6
- claude_mpm/services/unified/interfaces.py +3 -1
- claude_mpm/services/unified/unified_analyzer.py +14 -10
- claude_mpm/services/unified/unified_config.py +2 -1
- claude_mpm/services/unified/unified_deployment.py +9 -4
- claude_mpm/services/version_service.py +104 -1
- claude_mpm/skills/__init__.py +21 -0
- claude_mpm/skills/bundled/__init__.py +6 -0
- claude_mpm/skills/bundled/api-documentation.md +393 -0
- claude_mpm/skills/bundled/async-testing.md +571 -0
- claude_mpm/skills/bundled/code-review.md +143 -0
- claude_mpm/skills/bundled/database-migration.md +199 -0
- claude_mpm/skills/bundled/docker-containerization.md +194 -0
- claude_mpm/skills/bundled/express-local-dev.md +1429 -0
- claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
- claude_mpm/skills/bundled/git-workflow.md +414 -0
- claude_mpm/skills/bundled/imagemagick.md +204 -0
- claude_mpm/skills/bundled/json-data-handling.md +223 -0
- claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
- claude_mpm/skills/bundled/pdf.md +141 -0
- claude_mpm/skills/bundled/performance-profiling.md +567 -0
- claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
- claude_mpm/skills/bundled/security-scanning.md +327 -0
- claude_mpm/skills/bundled/systematic-debugging.md +473 -0
- claude_mpm/skills/bundled/test-driven-development.md +378 -0
- claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
- claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
- claude_mpm/skills/bundled/xlsx.md +157 -0
- claude_mpm/skills/registry.py +286 -0
- claude_mpm/skills/skill_manager.py +310 -0
- claude_mpm/tools/code_tree_analyzer.py +177 -141
- claude_mpm/tools/code_tree_events.py +4 -2
- claude_mpm/utils/agent_dependency_loader.py +2 -2
- {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/METADATA +117 -8
- {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/RECORD +238 -174
- claude_mpm/dashboard/static/css/code-tree.css +0 -1639
- claude_mpm/dashboard/static/js/components/code-tree/tree-breadcrumb.js +0 -353
- claude_mpm/dashboard/static/js/components/code-tree/tree-constants.js +0 -235
- claude_mpm/dashboard/static/js/components/code-tree/tree-search.js +0 -409
- claude_mpm/dashboard/static/js/components/code-tree/tree-utils.js +0 -435
- claude_mpm/dashboard/static/js/components/code-tree.js +0 -5869
- claude_mpm/dashboard/static/js/components/code-viewer.js +0 -1386
- claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +0 -425
- claude_mpm/hooks/claude_hooks/hook_handler_original.py +0 -1041
- claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +0 -347
- claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +0 -575
- claude_mpm/services/project/analyzer_refactored.py +0 -450
- {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/WHEEL +0 -0
- {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Model Router Implementation for Claude MPM Framework
|
|
3
|
+
====================================================
|
|
4
|
+
|
|
5
|
+
WHY: Provides intelligent routing between local (Ollama) and cloud (Claude)
|
|
6
|
+
models with automatic fallback, enabling hybrid operation that balances
|
|
7
|
+
privacy, cost, and reliability.
|
|
8
|
+
|
|
9
|
+
DESIGN DECISION: Chain of Responsibility pattern - tries providers in priority
|
|
10
|
+
order until one succeeds. Configuration controls routing strategy (auto, local-only,
|
|
11
|
+
cloud-only, privacy-first).
|
|
12
|
+
|
|
13
|
+
ROUTING STRATEGIES:
|
|
14
|
+
- AUTO: Try Ollama first, fallback to Claude on error (default)
|
|
15
|
+
- OLLAMA: Local-only, fail if unavailable (privacy mode)
|
|
16
|
+
- CLAUDE: Cloud-only, always use Claude
|
|
17
|
+
- PRIVACY: Like OLLAMA but with better error messages
|
|
18
|
+
|
|
19
|
+
ARCHITECTURE:
|
|
20
|
+
- Manages provider lifecycle (initialization, shutdown)
|
|
21
|
+
- Routes requests based on strategy and availability
|
|
22
|
+
- Tracks routing decisions and fallbacks for monitoring
|
|
23
|
+
- Provides unified interface hiding provider complexity
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from enum import Enum
|
|
27
|
+
from typing import Any, Dict, Optional
|
|
28
|
+
|
|
29
|
+
from claude_mpm.core.logger import get_logger
|
|
30
|
+
from claude_mpm.services.core.base import BaseService
|
|
31
|
+
from claude_mpm.services.core.interfaces.model import (
|
|
32
|
+
IModelRouter,
|
|
33
|
+
ModelCapability,
|
|
34
|
+
ModelResponse,
|
|
35
|
+
)
|
|
36
|
+
from claude_mpm.services.model.claude_provider import ClaudeProvider
|
|
37
|
+
from claude_mpm.services.model.ollama_provider import OllamaProvider
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class RoutingStrategy(Enum):
|
|
41
|
+
"""
|
|
42
|
+
Routing strategies for model selection.
|
|
43
|
+
|
|
44
|
+
WHY: Provides different operational modes based on user preferences
|
|
45
|
+
for privacy, cost, and reliability.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
AUTO = "auto" # Try Ollama first, fallback to Claude
|
|
49
|
+
OLLAMA_ONLY = "ollama" # Local only, fail if unavailable
|
|
50
|
+
CLAUDE_ONLY = "claude" # Cloud only, always use Claude
|
|
51
|
+
PRIVACY_FIRST = "privacy" # Like OLLAMA_ONLY but explicit about privacy
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class ModelRouter(BaseService, IModelRouter):
|
|
55
|
+
"""
|
|
56
|
+
Intelligent model router with automatic fallback.
|
|
57
|
+
|
|
58
|
+
WHY: Provides seamless switching between local and cloud models based on
|
|
59
|
+
availability and configuration. Enables privacy-preserving operation with
|
|
60
|
+
cloud fallback when needed.
|
|
61
|
+
|
|
62
|
+
Configuration:
|
|
63
|
+
strategy: Routing strategy (auto/ollama/claude/privacy)
|
|
64
|
+
ollama_config: Configuration for Ollama provider
|
|
65
|
+
claude_config: Configuration for Claude provider
|
|
66
|
+
fallback_enabled: Allow fallback to cloud (default: True for AUTO)
|
|
67
|
+
max_retries: Maximum retry attempts per provider (default: 2)
|
|
68
|
+
|
|
69
|
+
Usage:
|
|
70
|
+
router = ModelRouter(config={
|
|
71
|
+
"strategy": "auto",
|
|
72
|
+
"ollama_config": {"host": "http://localhost:11434"}
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
await router.initialize()
|
|
76
|
+
|
|
77
|
+
response = await router.analyze_content(
|
|
78
|
+
content="Your content",
|
|
79
|
+
task=ModelCapability.SEO_ANALYSIS
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
Routing Logic:
|
|
83
|
+
AUTO: Ollama available? → Use Ollama → On error → Try Claude
|
|
84
|
+
OLLAMA_ONLY: Ollama available? → Use Ollama → On error → Fail
|
|
85
|
+
CLAUDE_ONLY: Always use Claude
|
|
86
|
+
PRIVACY_FIRST: Like OLLAMA_ONLY but with privacy-focused messages
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
def __init__(self, config: Optional[Dict[str, Any]] = None):
|
|
90
|
+
"""
|
|
91
|
+
Initialize model router.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
config: Router configuration
|
|
95
|
+
"""
|
|
96
|
+
super().__init__(service_name="model_router", config=config or {})
|
|
97
|
+
|
|
98
|
+
self.logger = get_logger("model.router")
|
|
99
|
+
|
|
100
|
+
# Parse strategy
|
|
101
|
+
strategy_str = self.get_config("strategy", "auto")
|
|
102
|
+
try:
|
|
103
|
+
self.strategy = RoutingStrategy(strategy_str.lower())
|
|
104
|
+
except ValueError:
|
|
105
|
+
self.log_warning(f"Invalid strategy '{strategy_str}', defaulting to AUTO")
|
|
106
|
+
self.strategy = RoutingStrategy.AUTO
|
|
107
|
+
|
|
108
|
+
# Fallback configuration
|
|
109
|
+
self.fallback_enabled = self.get_config(
|
|
110
|
+
"fallback_enabled",
|
|
111
|
+
self.strategy == RoutingStrategy.AUTO,
|
|
112
|
+
)
|
|
113
|
+
self.max_retries = self.get_config("max_retries", 2)
|
|
114
|
+
|
|
115
|
+
# Initialize providers
|
|
116
|
+
ollama_config = self.get_config("ollama_config", {})
|
|
117
|
+
claude_config = self.get_config("claude_config", {})
|
|
118
|
+
|
|
119
|
+
self.ollama_provider = OllamaProvider(config=ollama_config)
|
|
120
|
+
self.claude_provider = ClaudeProvider(config=claude_config)
|
|
121
|
+
|
|
122
|
+
# Routing metrics
|
|
123
|
+
self._route_count: Dict[str, int] = {"ollama": 0, "claude": 0}
|
|
124
|
+
self._fallback_count = 0
|
|
125
|
+
self._active_provider: Optional[str] = None
|
|
126
|
+
|
|
127
|
+
async def initialize(self) -> bool:
|
|
128
|
+
"""
|
|
129
|
+
Initialize router and providers.
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
True if at least one provider initialized successfully
|
|
133
|
+
"""
|
|
134
|
+
self.log_info(f"Initializing model router with strategy: {self.strategy.value}")
|
|
135
|
+
|
|
136
|
+
success = False
|
|
137
|
+
|
|
138
|
+
# Initialize providers based on strategy
|
|
139
|
+
if self.strategy in (
|
|
140
|
+
RoutingStrategy.AUTO,
|
|
141
|
+
RoutingStrategy.OLLAMA_ONLY,
|
|
142
|
+
RoutingStrategy.PRIVACY_FIRST,
|
|
143
|
+
):
|
|
144
|
+
self.log_info("Initializing Ollama provider...")
|
|
145
|
+
if await self.ollama_provider.initialize():
|
|
146
|
+
self.log_info("Ollama provider initialized successfully")
|
|
147
|
+
success = True
|
|
148
|
+
else:
|
|
149
|
+
self.log_warning("Ollama provider initialization failed")
|
|
150
|
+
|
|
151
|
+
if self.strategy in (RoutingStrategy.AUTO, RoutingStrategy.CLAUDE_ONLY):
|
|
152
|
+
self.log_info("Initializing Claude provider...")
|
|
153
|
+
if await self.claude_provider.initialize():
|
|
154
|
+
self.log_info("Claude provider initialized successfully")
|
|
155
|
+
success = True
|
|
156
|
+
else:
|
|
157
|
+
self.log_warning("Claude provider initialization failed")
|
|
158
|
+
|
|
159
|
+
if not success:
|
|
160
|
+
self.log_error("No providers initialized successfully")
|
|
161
|
+
return False
|
|
162
|
+
|
|
163
|
+
self._initialized = True
|
|
164
|
+
return True
|
|
165
|
+
|
|
166
|
+
async def shutdown(self) -> None:
|
|
167
|
+
"""Shutdown router and all providers."""
|
|
168
|
+
self.log_info("Shutting down model router")
|
|
169
|
+
|
|
170
|
+
# Shutdown providers
|
|
171
|
+
if self.ollama_provider:
|
|
172
|
+
await self.ollama_provider.shutdown()
|
|
173
|
+
|
|
174
|
+
if self.claude_provider:
|
|
175
|
+
await self.claude_provider.shutdown()
|
|
176
|
+
|
|
177
|
+
self._shutdown = True
|
|
178
|
+
|
|
179
|
+
def get_active_provider(self) -> Optional[str]:
|
|
180
|
+
"""
|
|
181
|
+
Get name of currently active provider.
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
Provider name or None
|
|
185
|
+
"""
|
|
186
|
+
return self._active_provider
|
|
187
|
+
|
|
188
|
+
async def get_provider_status(self) -> Dict[str, Dict[str, Any]]:
|
|
189
|
+
"""
|
|
190
|
+
Get status of all configured providers.
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
Dictionary mapping provider names to status info
|
|
194
|
+
"""
|
|
195
|
+
status = {}
|
|
196
|
+
|
|
197
|
+
# Check Ollama
|
|
198
|
+
if self.strategy in (
|
|
199
|
+
RoutingStrategy.AUTO,
|
|
200
|
+
RoutingStrategy.OLLAMA_ONLY,
|
|
201
|
+
RoutingStrategy.PRIVACY_FIRST,
|
|
202
|
+
):
|
|
203
|
+
ollama_available = await self.ollama_provider.is_available()
|
|
204
|
+
ollama_models = (
|
|
205
|
+
await self.ollama_provider.get_available_models()
|
|
206
|
+
if ollama_available
|
|
207
|
+
else []
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
status["ollama"] = {
|
|
211
|
+
"available": ollama_available,
|
|
212
|
+
"initialized": self.ollama_provider.is_initialized,
|
|
213
|
+
"models_count": len(ollama_models),
|
|
214
|
+
"metrics": self.ollama_provider.get_metrics(),
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
# Check Claude
|
|
218
|
+
if self.strategy in (RoutingStrategy.AUTO, RoutingStrategy.CLAUDE_ONLY):
|
|
219
|
+
claude_available = await self.claude_provider.is_available()
|
|
220
|
+
|
|
221
|
+
status["claude"] = {
|
|
222
|
+
"available": claude_available,
|
|
223
|
+
"initialized": self.claude_provider.is_initialized,
|
|
224
|
+
"metrics": self.claude_provider.get_metrics(),
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
# Add routing metrics
|
|
228
|
+
status["router"] = {
|
|
229
|
+
"strategy": self.strategy.value,
|
|
230
|
+
"fallback_enabled": self.fallback_enabled,
|
|
231
|
+
"route_count": self._route_count.copy(),
|
|
232
|
+
"fallback_count": self._fallback_count,
|
|
233
|
+
"active_provider": self._active_provider,
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return status
|
|
237
|
+
|
|
238
|
+
async def analyze_content(
|
|
239
|
+
self,
|
|
240
|
+
content: str,
|
|
241
|
+
task: ModelCapability,
|
|
242
|
+
model: Optional[str] = None,
|
|
243
|
+
**kwargs,
|
|
244
|
+
) -> ModelResponse:
|
|
245
|
+
"""
|
|
246
|
+
Route content analysis to optimal provider.
|
|
247
|
+
|
|
248
|
+
WHY: Single entry point for all content analysis. Handles provider
|
|
249
|
+
selection, fallback, and error recovery transparently.
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
content: Text content to analyze
|
|
253
|
+
task: Type of analysis
|
|
254
|
+
model: Optional specific model
|
|
255
|
+
**kwargs: Provider-specific options
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
ModelResponse from successful provider
|
|
259
|
+
"""
|
|
260
|
+
if not self._initialized:
|
|
261
|
+
return self._create_error_response(
|
|
262
|
+
task,
|
|
263
|
+
model,
|
|
264
|
+
"Router not initialized",
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
# Route based on strategy
|
|
268
|
+
if self.strategy == RoutingStrategy.CLAUDE_ONLY:
|
|
269
|
+
return await self._route_to_claude(content, task, model, **kwargs)
|
|
270
|
+
|
|
271
|
+
if self.strategy in (
|
|
272
|
+
RoutingStrategy.OLLAMA_ONLY,
|
|
273
|
+
RoutingStrategy.PRIVACY_FIRST,
|
|
274
|
+
):
|
|
275
|
+
return await self._route_to_ollama(
|
|
276
|
+
content,
|
|
277
|
+
task,
|
|
278
|
+
model,
|
|
279
|
+
require_success=True,
|
|
280
|
+
**kwargs,
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
# AUTO strategy
|
|
284
|
+
return await self._route_auto(content, task, model, **kwargs)
|
|
285
|
+
|
|
286
|
+
async def _route_auto(
|
|
287
|
+
self,
|
|
288
|
+
content: str,
|
|
289
|
+
task: ModelCapability,
|
|
290
|
+
model: Optional[str],
|
|
291
|
+
**kwargs,
|
|
292
|
+
) -> ModelResponse:
|
|
293
|
+
"""
|
|
294
|
+
Auto routing: Try Ollama first, fallback to Claude.
|
|
295
|
+
|
|
296
|
+
Args:
|
|
297
|
+
content: Content to analyze
|
|
298
|
+
task: Task to perform
|
|
299
|
+
model: Optional model
|
|
300
|
+
**kwargs: Additional options
|
|
301
|
+
|
|
302
|
+
Returns:
|
|
303
|
+
ModelResponse from successful provider
|
|
304
|
+
"""
|
|
305
|
+
# Try Ollama first
|
|
306
|
+
if await self.ollama_provider.is_available():
|
|
307
|
+
self.log_debug("Routing to Ollama (primary)")
|
|
308
|
+
response = await self._route_to_ollama(
|
|
309
|
+
content,
|
|
310
|
+
task,
|
|
311
|
+
model,
|
|
312
|
+
require_success=False,
|
|
313
|
+
**kwargs,
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
if response.success:
|
|
317
|
+
return response
|
|
318
|
+
|
|
319
|
+
# Ollama failed, try fallback
|
|
320
|
+
self.log_warning(
|
|
321
|
+
f"Ollama analysis failed: {response.error}, trying Claude fallback"
|
|
322
|
+
)
|
|
323
|
+
self._fallback_count += 1
|
|
324
|
+
|
|
325
|
+
# Ollama unavailable or failed - fallback to Claude
|
|
326
|
+
if self.fallback_enabled:
|
|
327
|
+
self.log_info("Falling back to Claude")
|
|
328
|
+
return await self._route_to_claude(content, task, model, **kwargs)
|
|
329
|
+
return self._create_error_response(
|
|
330
|
+
task,
|
|
331
|
+
model,
|
|
332
|
+
"Ollama unavailable and fallback disabled",
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
async def _route_to_ollama(
|
|
336
|
+
self,
|
|
337
|
+
content: str,
|
|
338
|
+
task: ModelCapability,
|
|
339
|
+
model: Optional[str],
|
|
340
|
+
require_success: bool = False,
|
|
341
|
+
**kwargs,
|
|
342
|
+
) -> ModelResponse:
|
|
343
|
+
"""
|
|
344
|
+
Route to Ollama provider.
|
|
345
|
+
|
|
346
|
+
Args:
|
|
347
|
+
content: Content to analyze
|
|
348
|
+
task: Task to perform
|
|
349
|
+
model: Optional model
|
|
350
|
+
require_success: If True, check availability first
|
|
351
|
+
**kwargs: Additional options
|
|
352
|
+
|
|
353
|
+
Returns:
|
|
354
|
+
ModelResponse
|
|
355
|
+
"""
|
|
356
|
+
if require_success and not await self.ollama_provider.is_available():
|
|
357
|
+
if self.strategy == RoutingStrategy.PRIVACY_FIRST:
|
|
358
|
+
error_msg = (
|
|
359
|
+
"Ollama not available. Privacy mode enabled - "
|
|
360
|
+
"not sending to cloud."
|
|
361
|
+
)
|
|
362
|
+
else:
|
|
363
|
+
error_msg = "Ollama not available and required by configuration"
|
|
364
|
+
|
|
365
|
+
return self._create_error_response(task, model, error_msg)
|
|
366
|
+
|
|
367
|
+
self._active_provider = "ollama"
|
|
368
|
+
self._route_count["ollama"] += 1
|
|
369
|
+
|
|
370
|
+
return await self.ollama_provider.analyze_content(
|
|
371
|
+
content, task, model, **kwargs
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
async def _route_to_claude(
|
|
375
|
+
self,
|
|
376
|
+
content: str,
|
|
377
|
+
task: ModelCapability,
|
|
378
|
+
model: Optional[str],
|
|
379
|
+
**kwargs,
|
|
380
|
+
) -> ModelResponse:
|
|
381
|
+
"""
|
|
382
|
+
Route to Claude provider.
|
|
383
|
+
|
|
384
|
+
Args:
|
|
385
|
+
content: Content to analyze
|
|
386
|
+
task: Task to perform
|
|
387
|
+
model: Optional model
|
|
388
|
+
**kwargs: Additional options
|
|
389
|
+
|
|
390
|
+
Returns:
|
|
391
|
+
ModelResponse
|
|
392
|
+
"""
|
|
393
|
+
self._active_provider = "claude"
|
|
394
|
+
self._route_count["claude"] += 1
|
|
395
|
+
|
|
396
|
+
return await self.claude_provider.analyze_content(
|
|
397
|
+
content, task, model, **kwargs
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
def _create_error_response(
|
|
401
|
+
self,
|
|
402
|
+
task: ModelCapability,
|
|
403
|
+
model: Optional[str],
|
|
404
|
+
error: str,
|
|
405
|
+
) -> ModelResponse:
|
|
406
|
+
"""
|
|
407
|
+
Create error response.
|
|
408
|
+
|
|
409
|
+
Args:
|
|
410
|
+
task: Task that was attempted
|
|
411
|
+
model: Model that was requested
|
|
412
|
+
error: Error message
|
|
413
|
+
|
|
414
|
+
Returns:
|
|
415
|
+
ModelResponse with error
|
|
416
|
+
"""
|
|
417
|
+
return ModelResponse(
|
|
418
|
+
success=False,
|
|
419
|
+
provider="router",
|
|
420
|
+
model=model or "unknown",
|
|
421
|
+
task=task.value,
|
|
422
|
+
result="",
|
|
423
|
+
error=error,
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
def get_routing_metrics(self) -> Dict[str, Any]:
|
|
427
|
+
"""
|
|
428
|
+
Get routing performance metrics.
|
|
429
|
+
|
|
430
|
+
Returns:
|
|
431
|
+
Dictionary of routing metrics
|
|
432
|
+
"""
|
|
433
|
+
total_routes = sum(self._route_count.values())
|
|
434
|
+
ollama_percentage = (
|
|
435
|
+
(self._route_count["ollama"] / total_routes * 100)
|
|
436
|
+
if total_routes > 0
|
|
437
|
+
else 0
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
return {
|
|
441
|
+
"strategy": self.strategy.value,
|
|
442
|
+
"total_routes": total_routes,
|
|
443
|
+
"ollama_routes": self._route_count["ollama"],
|
|
444
|
+
"claude_routes": self._route_count["claude"],
|
|
445
|
+
"ollama_percentage": ollama_percentage,
|
|
446
|
+
"fallback_count": self._fallback_count,
|
|
447
|
+
"fallback_rate": (
|
|
448
|
+
(self._fallback_count / total_routes * 100) if total_routes > 0 else 0
|
|
449
|
+
),
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
__all__ = ["ModelRouter", "RoutingStrategy"]
|