claude-mpm 4.7.4__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_AGENT_TEMPLATE.md +118 -0
- claude_mpm/agents/BASE_ENGINEER.md +286 -0
- claude_mpm/agents/BASE_PM.md +106 -1
- claude_mpm/agents/OUTPUT_STYLE.md +329 -11
- claude_mpm/agents/PM_INSTRUCTIONS.md +397 -459
- claude_mpm/agents/agent_loader.py +17 -5
- claude_mpm/agents/frontmatter_validator.py +284 -253
- claude_mpm/agents/templates/README.md +465 -0
- claude_mpm/agents/templates/agent-manager.json +4 -1
- claude_mpm/agents/templates/agentic-coder-optimizer.json +13 -3
- claude_mpm/agents/templates/api_qa.json +11 -2
- claude_mpm/agents/templates/circuit_breakers.md +638 -0
- claude_mpm/agents/templates/clerk-ops.json +12 -2
- claude_mpm/agents/templates/code_analyzer.json +8 -2
- claude_mpm/agents/templates/content-agent.json +358 -0
- claude_mpm/agents/templates/dart_engineer.json +15 -2
- claude_mpm/agents/templates/data_engineer.json +15 -2
- claude_mpm/agents/templates/documentation.json +10 -2
- claude_mpm/agents/templates/engineer.json +21 -1
- claude_mpm/agents/templates/gcp_ops_agent.json +12 -2
- claude_mpm/agents/templates/git_file_tracking.md +584 -0
- claude_mpm/agents/templates/golang_engineer.json +270 -0
- claude_mpm/agents/templates/imagemagick.json +4 -1
- claude_mpm/agents/templates/java_engineer.json +346 -0
- claude_mpm/agents/templates/local_ops_agent.json +1227 -6
- claude_mpm/agents/templates/memory_manager.json +4 -1
- claude_mpm/agents/templates/nextjs_engineer.json +141 -133
- claude_mpm/agents/templates/ops.json +12 -2
- claude_mpm/agents/templates/php-engineer.json +270 -174
- claude_mpm/agents/templates/pm_examples.md +474 -0
- claude_mpm/agents/templates/pm_red_flags.md +240 -0
- claude_mpm/agents/templates/product_owner.json +338 -0
- claude_mpm/agents/templates/project_organizer.json +14 -4
- claude_mpm/agents/templates/prompt-engineer.json +13 -2
- claude_mpm/agents/templates/python_engineer.json +174 -81
- claude_mpm/agents/templates/qa.json +11 -2
- claude_mpm/agents/templates/react_engineer.json +16 -3
- claude_mpm/agents/templates/refactoring_engineer.json +12 -2
- claude_mpm/agents/templates/research.json +34 -21
- claude_mpm/agents/templates/response_format.md +583 -0
- claude_mpm/agents/templates/ruby-engineer.json +129 -192
- claude_mpm/agents/templates/rust_engineer.json +270 -0
- claude_mpm/agents/templates/security.json +10 -2
- claude_mpm/agents/templates/svelte-engineer.json +225 -0
- claude_mpm/agents/templates/ticketing.json +10 -2
- claude_mpm/agents/templates/typescript_engineer.json +116 -125
- claude_mpm/agents/templates/validation_templates.md +312 -0
- claude_mpm/agents/templates/vercel_ops_agent.json +12 -2
- claude_mpm/agents/templates/version_control.json +12 -2
- claude_mpm/agents/templates/web_qa.json +11 -2
- claude_mpm/agents/templates/web_ui.json +15 -2
- claude_mpm/cli/__init__.py +34 -614
- 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 +235 -148
- claude_mpm/cli/commands/agents_detect.py +380 -0
- claude_mpm/cli/commands/agents_recommend.py +309 -0
- 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 +570 -0
- claude_mpm/cli/commands/config.py +47 -13
- claude_mpm/cli/commands/configure.py +419 -1571
- 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 +585 -196
- claude_mpm/cli/commands/mpm_init_handler.py +37 -3
- claude_mpm/cli/commands/search.py +170 -4
- claude_mpm/cli/commands/upgrade.py +152 -0
- 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/agents_parser.py +9 -0
- claude_mpm/cli/parsers/auto_configure_parser.py +245 -0
- claude_mpm/cli/parsers/base_parser.py +110 -3
- claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
- claude_mpm/cli/parsers/mpm_init_parser.py +65 -5
- 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-agents-detect.md +168 -0
- claude_mpm/commands/mpm-agents-recommend.md +214 -0
- claude_mpm/commands/mpm-agents.md +75 -1
- claude_mpm/commands/mpm-auto-configure.md +217 -0
- claude_mpm/commands/mpm-help.md +163 -0
- claude_mpm/commands/mpm-init.md +148 -3
- 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/constants.py +1 -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/log_manager.py +2 -0
- 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/__init__.py +20 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +4 -2
- claude_mpm/hooks/claude_hooks/response_tracking.py +35 -1
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +23 -2
- claude_mpm/hooks/failure_learning/__init__.py +60 -0
- claude_mpm/hooks/failure_learning/failure_detection_hook.py +235 -0
- claude_mpm/hooks/failure_learning/fix_detection_hook.py +217 -0
- claude_mpm/hooks/failure_learning/learning_extraction_hook.py +286 -0
- claude_mpm/hooks/instruction_reinforcement.py +7 -2
- claude_mpm/hooks/kuzu_enrichment_hook.py +263 -0
- claude_mpm/hooks/kuzu_memory_hook.py +37 -12
- claude_mpm/hooks/kuzu_response_hook.py +183 -0
- claude_mpm/models/resume_log.py +340 -0
- claude_mpm/services/agents/__init__.py +18 -5
- claude_mpm/services/agents/auto_config_manager.py +796 -0
- 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/observers.py +547 -0
- claude_mpm/services/agents/recommender.py +568 -0
- claude_mpm/services/agents/registry/modification_tracker.py +5 -2
- claude_mpm/services/command_handler_service.py +11 -5
- claude_mpm/services/core/__init__.py +33 -1
- claude_mpm/services/core/interfaces/__init__.py +90 -3
- claude_mpm/services/core/interfaces/agent.py +184 -0
- 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/project.py +121 -0
- claude_mpm/services/core/interfaces/restart.py +307 -0
- claude_mpm/services/core/interfaces/stability.py +260 -0
- claude_mpm/services/core/memory_manager.py +11 -24
- claude_mpm/services/core/models/__init__.py +79 -0
- claude_mpm/services/core/models/agent_config.py +381 -0
- 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/models/toolchain.py +306 -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 +38 -33
- 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/main.py +30 -0
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +206 -32
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +30 -28
- claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +25 -5
- claude_mpm/services/mcp_service_verifier.py +1 -1
- claude_mpm/services/memory/failure_tracker.py +563 -0
- claude_mpm/services/memory_hook_service.py +165 -4
- 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/project/__init__.py +23 -0
- claude_mpm/services/project/detection_strategies.py +719 -0
- claude_mpm/services/project/toolchain_analyzer.py +581 -0
- claude_mpm/services/self_upgrade_service.py +342 -0
- 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/storage/state_storage.py +15 -15
- 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 +40 -20
- claude_mpm/utils/display_helper.py +260 -0
- claude_mpm/utils/git_analyzer.py +407 -0
- claude_mpm/utils/robust_installer.py +73 -19
- {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/METADATA +129 -12
- {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/RECORD +295 -193
- claude_mpm/dashboard/static/css/code-tree.css +0 -1639
- claude_mpm/dashboard/static/index-hub-backup.html +0 -713
- 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.7.4.dist-info → claude_mpm-4.18.2.dist-info}/WHEEL +0 -0
- {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/top_level.txt +0 -0
|
@@ -11,6 +11,7 @@ Extracted from ClaudeRunner to follow Single Responsibility Principle.
|
|
|
11
11
|
from typing import Any, Dict
|
|
12
12
|
|
|
13
13
|
from claude_mpm.core.base_service import BaseService
|
|
14
|
+
from claude_mpm.core.enums import ServiceState
|
|
14
15
|
from claude_mpm.services.core.interfaces import MemoryHookInterface
|
|
15
16
|
|
|
16
17
|
|
|
@@ -41,8 +42,8 @@ class MemoryHookService(BaseService, MemoryHookInterface):
|
|
|
41
42
|
These hooks ensure memory is properly managed and persisted.
|
|
42
43
|
|
|
43
44
|
DESIGN DECISION: We register hooks for key lifecycle events:
|
|
44
|
-
- Before Claude interaction: Load relevant memories
|
|
45
|
-
- After Claude interaction: Save new memories
|
|
45
|
+
- Before Claude interaction: Load relevant memories (kuzu-memory + legacy)
|
|
46
|
+
- After Claude interaction: Save new memories (kuzu-memory + legacy)
|
|
46
47
|
- On error: Ensure memory state is preserved
|
|
47
48
|
"""
|
|
48
49
|
if not self.hook_service:
|
|
@@ -90,11 +91,169 @@ class MemoryHookService(BaseService, MemoryHookInterface):
|
|
|
90
91
|
if success2:
|
|
91
92
|
self.registered_hooks.append("memory_save")
|
|
92
93
|
|
|
93
|
-
self.logger.debug("
|
|
94
|
+
self.logger.debug("Legacy memory hooks registered successfully")
|
|
95
|
+
|
|
96
|
+
# Register kuzu-memory hooks if available
|
|
97
|
+
self._register_kuzu_memory_hooks()
|
|
98
|
+
|
|
99
|
+
# Register failure-learning hooks
|
|
100
|
+
self._register_failure_learning_hooks()
|
|
94
101
|
|
|
95
102
|
except Exception as e:
|
|
96
103
|
self.logger.warning(f"Failed to register memory hooks: {e}")
|
|
97
104
|
|
|
105
|
+
def _register_kuzu_memory_hooks(self):
|
|
106
|
+
"""Register kuzu-memory bidirectional enrichment hooks.
|
|
107
|
+
|
|
108
|
+
WHY: Kuzu-memory provides persistent knowledge graph storage that works
|
|
109
|
+
across conversations. This enables:
|
|
110
|
+
1. Delegation context enrichment with relevant memories (READ)
|
|
111
|
+
2. Automatic learning extraction from responses (WRITE)
|
|
112
|
+
|
|
113
|
+
DESIGN DECISION: These hooks are separate from legacy memory hooks to
|
|
114
|
+
allow independent evolution and configuration. Both systems can coexist.
|
|
115
|
+
"""
|
|
116
|
+
try:
|
|
117
|
+
# Check if kuzu-memory is enabled in config
|
|
118
|
+
from claude_mpm.core.config import Config
|
|
119
|
+
|
|
120
|
+
config = Config()
|
|
121
|
+
kuzu_config = config.get("memory.kuzu", {})
|
|
122
|
+
if isinstance(kuzu_config, dict):
|
|
123
|
+
kuzu_enabled = kuzu_config.get("enabled", True)
|
|
124
|
+
enrichment_enabled = kuzu_config.get("enrichment", True)
|
|
125
|
+
learning_enabled = kuzu_config.get("learning", True)
|
|
126
|
+
else:
|
|
127
|
+
# Default to enabled if config section doesn't exist
|
|
128
|
+
kuzu_enabled = True
|
|
129
|
+
enrichment_enabled = True
|
|
130
|
+
learning_enabled = True
|
|
131
|
+
|
|
132
|
+
if not kuzu_enabled:
|
|
133
|
+
self.logger.debug("Kuzu-memory disabled in configuration")
|
|
134
|
+
return
|
|
135
|
+
|
|
136
|
+
from claude_mpm.hooks import (
|
|
137
|
+
get_kuzu_enrichment_hook,
|
|
138
|
+
get_kuzu_response_hook,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Get kuzu-memory hooks
|
|
142
|
+
enrichment_hook = get_kuzu_enrichment_hook()
|
|
143
|
+
learning_hook = get_kuzu_response_hook()
|
|
144
|
+
|
|
145
|
+
# Register enrichment hook (PreDelegationHook) if enabled
|
|
146
|
+
if enrichment_hook.enabled and enrichment_enabled:
|
|
147
|
+
success = self.hook_service.register_hook(enrichment_hook)
|
|
148
|
+
if success:
|
|
149
|
+
self.registered_hooks.append("kuzu_memory_enrichment")
|
|
150
|
+
self.logger.info(
|
|
151
|
+
"✅ Kuzu-memory enrichment enabled (prompts → memories)"
|
|
152
|
+
)
|
|
153
|
+
else:
|
|
154
|
+
self.logger.warning(
|
|
155
|
+
"Failed to register kuzu-memory enrichment hook"
|
|
156
|
+
)
|
|
157
|
+
elif not enrichment_enabled:
|
|
158
|
+
self.logger.debug("Kuzu-memory enrichment disabled in configuration")
|
|
159
|
+
|
|
160
|
+
# Register learning hook (PostDelegationHook) if enabled
|
|
161
|
+
if learning_hook.enabled and learning_enabled:
|
|
162
|
+
success = self.hook_service.register_hook(learning_hook)
|
|
163
|
+
if success:
|
|
164
|
+
self.registered_hooks.append("kuzu_response_learner")
|
|
165
|
+
self.logger.info(
|
|
166
|
+
"✅ Kuzu-memory learning enabled (responses → memories)"
|
|
167
|
+
)
|
|
168
|
+
else:
|
|
169
|
+
self.logger.warning("Failed to register kuzu-memory learning hook")
|
|
170
|
+
elif not learning_enabled:
|
|
171
|
+
self.logger.debug("Kuzu-memory learning disabled in configuration")
|
|
172
|
+
|
|
173
|
+
# If neither hook is enabled, kuzu-memory is not available
|
|
174
|
+
if not enrichment_hook.enabled and not learning_hook.enabled:
|
|
175
|
+
self.logger.debug(
|
|
176
|
+
"Kuzu-memory not available. Install with: pipx install kuzu-memory"
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
except ImportError as e:
|
|
180
|
+
self.logger.debug(f"Kuzu-memory hooks not available: {e}")
|
|
181
|
+
except Exception as e:
|
|
182
|
+
self.logger.warning(f"Failed to register kuzu-memory hooks: {e}")
|
|
183
|
+
|
|
184
|
+
def _register_failure_learning_hooks(self):
|
|
185
|
+
"""Register failure-learning hooks for automatic learning extraction.
|
|
186
|
+
|
|
187
|
+
WHY: When tasks fail and agents fix them, we want to automatically capture
|
|
188
|
+
this as a learning. The failure-learning system provides:
|
|
189
|
+
1. Failure detection from tool outputs (errors, exceptions, test failures)
|
|
190
|
+
2. Fix detection when same task type succeeds after failure
|
|
191
|
+
3. Learning extraction and persistence to agent memory files
|
|
192
|
+
|
|
193
|
+
DESIGN DECISION: These hooks work as a chain with specific priorities:
|
|
194
|
+
- FailureDetectionHook (priority=85): Detects failures after tool execution
|
|
195
|
+
- FixDetectionHook (priority=87): Matches fixes with failures
|
|
196
|
+
- LearningExtractionHook (priority=89): Extracts and persists learnings
|
|
197
|
+
|
|
198
|
+
The system is enabled by default but can be disabled via configuration.
|
|
199
|
+
"""
|
|
200
|
+
try:
|
|
201
|
+
# Check if failure-learning is enabled in config
|
|
202
|
+
from claude_mpm.core.config import Config
|
|
203
|
+
|
|
204
|
+
config = Config()
|
|
205
|
+
failure_learning_config = config.get("memory.failure_learning", {})
|
|
206
|
+
|
|
207
|
+
if isinstance(failure_learning_config, dict):
|
|
208
|
+
enabled = failure_learning_config.get("enabled", True)
|
|
209
|
+
else:
|
|
210
|
+
# Default to enabled if config section doesn't exist
|
|
211
|
+
enabled = True
|
|
212
|
+
|
|
213
|
+
if not enabled:
|
|
214
|
+
self.logger.debug("Failure-learning disabled in configuration")
|
|
215
|
+
return
|
|
216
|
+
|
|
217
|
+
# Import failure-learning hooks
|
|
218
|
+
from claude_mpm.hooks.failure_learning import (
|
|
219
|
+
get_failure_detection_hook,
|
|
220
|
+
get_fix_detection_hook,
|
|
221
|
+
get_learning_extraction_hook,
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
# Get hook instances
|
|
225
|
+
failure_hook = get_failure_detection_hook()
|
|
226
|
+
fix_hook = get_fix_detection_hook()
|
|
227
|
+
learning_hook = get_learning_extraction_hook()
|
|
228
|
+
|
|
229
|
+
# Register hooks in priority order
|
|
230
|
+
success1 = self.hook_service.register_hook(failure_hook)
|
|
231
|
+
success2 = self.hook_service.register_hook(fix_hook)
|
|
232
|
+
success3 = self.hook_service.register_hook(learning_hook)
|
|
233
|
+
|
|
234
|
+
if success1:
|
|
235
|
+
self.registered_hooks.append("failure_detection")
|
|
236
|
+
self.logger.debug("✅ Failure detection enabled")
|
|
237
|
+
|
|
238
|
+
if success2:
|
|
239
|
+
self.registered_hooks.append("fix_detection")
|
|
240
|
+
self.logger.debug("✅ Fix detection enabled")
|
|
241
|
+
|
|
242
|
+
if success3:
|
|
243
|
+
self.registered_hooks.append("learning_extraction")
|
|
244
|
+
self.logger.debug("✅ Learning extraction enabled")
|
|
245
|
+
|
|
246
|
+
if success1 and success2 and success3:
|
|
247
|
+
self.logger.info(
|
|
248
|
+
"✅ Failure-learning system enabled "
|
|
249
|
+
"(failures → fixes → learnings → memory)"
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
except ImportError as e:
|
|
253
|
+
self.logger.debug(f"Failure-learning hooks not available: {e}")
|
|
254
|
+
except Exception as e:
|
|
255
|
+
self.logger.warning(f"Failed to register failure-learning hooks: {e}")
|
|
256
|
+
|
|
98
257
|
def _load_relevant_memories_hook(self, context):
|
|
99
258
|
"""Hook function to load relevant memories before Claude interaction.
|
|
100
259
|
|
|
@@ -305,5 +464,7 @@ class MemoryHookService(BaseService, MemoryHookInterface):
|
|
|
305
464
|
"hook_service_available": self.hook_service is not None,
|
|
306
465
|
"memory_enabled": self.is_memory_enabled(),
|
|
307
466
|
"total_hooks": len(self.registered_hooks),
|
|
308
|
-
"status":
|
|
467
|
+
"status": (
|
|
468
|
+
ServiceState.RUNNING if self.registered_hooks else ServiceState.IDLE
|
|
469
|
+
),
|
|
309
470
|
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Model Services Package for Claude MPM Framework
|
|
3
|
+
================================================
|
|
4
|
+
|
|
5
|
+
WHY: Provides hybrid local/cloud content processing with intelligent routing
|
|
6
|
+
and automatic fallback. Enables privacy-preserving local model execution via
|
|
7
|
+
Ollama with cloud fallback to Claude for reliability.
|
|
8
|
+
|
|
9
|
+
ARCHITECTURE:
|
|
10
|
+
- Interfaces: Core contracts for model providers and routing (IModelProvider, IModelRouter)
|
|
11
|
+
- Base Provider: Common functionality for all providers (BaseModelProvider)
|
|
12
|
+
- Providers: Ollama (local) and Claude (cloud) implementations
|
|
13
|
+
- Router: Intelligent routing with fallback logic (ModelRouter)
|
|
14
|
+
- Configuration: Centralized config management (model_config.py)
|
|
15
|
+
|
|
16
|
+
USAGE:
|
|
17
|
+
|
|
18
|
+
Basic Usage with Auto-Routing:
|
|
19
|
+
```python
|
|
20
|
+
from claude_mpm.services.model import ModelRouter
|
|
21
|
+
from claude_mpm.services.core.interfaces.model import ModelCapability
|
|
22
|
+
|
|
23
|
+
# Create router with default config (auto mode)
|
|
24
|
+
router = ModelRouter()
|
|
25
|
+
await router.initialize()
|
|
26
|
+
|
|
27
|
+
# Analyze content (tries Ollama first, falls back to Claude)
|
|
28
|
+
response = await router.analyze_content(
|
|
29
|
+
content="Your article text here...",
|
|
30
|
+
task=ModelCapability.SEO_ANALYSIS
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
if response.success:
|
|
34
|
+
print(f"Analysis by {response.provider}:")
|
|
35
|
+
print(response.result)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Direct Provider Usage:
|
|
39
|
+
```python
|
|
40
|
+
from claude_mpm.services.model import OllamaProvider
|
|
41
|
+
|
|
42
|
+
# Use Ollama directly
|
|
43
|
+
provider = OllamaProvider(config={
|
|
44
|
+
"host": "http://localhost:11434"
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
if await provider.is_available():
|
|
48
|
+
response = await provider.analyze_content(
|
|
49
|
+
content="Text to analyze",
|
|
50
|
+
task=ModelCapability.READABILITY
|
|
51
|
+
)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Configuration-Based Setup:
|
|
55
|
+
```python
|
|
56
|
+
from claude_mpm.config.model_config import ModelConfigManager
|
|
57
|
+
from claude_mpm.services.model import ModelRouter
|
|
58
|
+
|
|
59
|
+
# Load config from file
|
|
60
|
+
config = ModelConfigManager.load_config(".claude/configuration.yaml")
|
|
61
|
+
router_config = ModelConfigManager.get_router_config(config)
|
|
62
|
+
|
|
63
|
+
# Create router with loaded config
|
|
64
|
+
router = ModelRouter(config=router_config)
|
|
65
|
+
await router.initialize()
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
PROVIDER STRATEGIES:
|
|
69
|
+
- AUTO: Try Ollama first, fallback to Claude (default)
|
|
70
|
+
- OLLAMA: Local-only, fail if unavailable (privacy mode)
|
|
71
|
+
- CLAUDE: Cloud-only, always use Claude
|
|
72
|
+
- PRIVACY: Like OLLAMA with privacy-focused messages
|
|
73
|
+
|
|
74
|
+
RECOMMENDED MODELS (Ollama):
|
|
75
|
+
- SEO Analysis: llama3.3:70b - Comprehensive SEO insights
|
|
76
|
+
- Readability: gemma2:9b - Fast, accurate readability scoring
|
|
77
|
+
- Grammar: qwen3:14b - Specialized grammar checking
|
|
78
|
+
- Summarization: mistral:7b - Concise, effective summaries
|
|
79
|
+
- Keyword Extraction: seoassistant - SEO-specialized model
|
|
80
|
+
|
|
81
|
+
CONFIGURATION:
|
|
82
|
+
See claude_mpm.config.model_config for detailed configuration options.
|
|
83
|
+
|
|
84
|
+
Example configuration.yaml:
|
|
85
|
+
```yaml
|
|
86
|
+
content_agent:
|
|
87
|
+
model_provider: auto
|
|
88
|
+
|
|
89
|
+
ollama:
|
|
90
|
+
enabled: true
|
|
91
|
+
host: http://localhost:11434
|
|
92
|
+
fallback_to_cloud: true
|
|
93
|
+
models:
|
|
94
|
+
seo_analysis: llama3.3:70b
|
|
95
|
+
readability: gemma2:9b
|
|
96
|
+
|
|
97
|
+
claude:
|
|
98
|
+
enabled: true
|
|
99
|
+
model: claude-3-5-sonnet-20241022
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
PHASE 1 STATUS (Current):
|
|
103
|
+
✅ Core interfaces and contracts defined
|
|
104
|
+
✅ Base provider with common functionality
|
|
105
|
+
✅ Ollama provider with direct API integration
|
|
106
|
+
✅ Claude provider (Phase 1: mock responses)
|
|
107
|
+
✅ Router with intelligent fallback logic
|
|
108
|
+
✅ Configuration management with validation
|
|
109
|
+
⏳ Claude API integration (Phase 2)
|
|
110
|
+
⏳ Content agent integration (Phase 2)
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
# Re-export interfaces for convenience
|
|
114
|
+
from claude_mpm.services.core.interfaces.model import (
|
|
115
|
+
IModelProvider,
|
|
116
|
+
IModelRouter,
|
|
117
|
+
ModelCapability,
|
|
118
|
+
ModelProvider,
|
|
119
|
+
ModelResponse,
|
|
120
|
+
)
|
|
121
|
+
from claude_mpm.services.model.base_provider import BaseModelProvider
|
|
122
|
+
from claude_mpm.services.model.claude_provider import ClaudeProvider
|
|
123
|
+
from claude_mpm.services.model.model_router import ModelRouter, RoutingStrategy
|
|
124
|
+
from claude_mpm.services.model.ollama_provider import OllamaProvider
|
|
125
|
+
|
|
126
|
+
__all__ = [ # noqa: RUF022 - Semantic grouping preferred over alphabetical
|
|
127
|
+
# Base classes
|
|
128
|
+
"BaseModelProvider",
|
|
129
|
+
# Providers
|
|
130
|
+
"ClaudeProvider",
|
|
131
|
+
"OllamaProvider",
|
|
132
|
+
# Router
|
|
133
|
+
"ModelRouter",
|
|
134
|
+
"RoutingStrategy",
|
|
135
|
+
# Interfaces
|
|
136
|
+
"IModelProvider",
|
|
137
|
+
"IModelRouter",
|
|
138
|
+
# Data types
|
|
139
|
+
"ModelCapability",
|
|
140
|
+
"ModelProvider",
|
|
141
|
+
"ModelResponse",
|
|
142
|
+
]
|
|
143
|
+
|
|
144
|
+
# Version and metadata
|
|
145
|
+
__version__ = "1.0.0"
|
|
146
|
+
__phase__ = "1"
|
|
147
|
+
__status__ = "Core Infrastructure Complete"
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base Model Provider Implementation for Claude MPM Framework
|
|
3
|
+
===========================================================
|
|
4
|
+
|
|
5
|
+
WHY: Provides common functionality for all model providers, reducing
|
|
6
|
+
duplication and ensuring consistent behavior across implementations.
|
|
7
|
+
|
|
8
|
+
DESIGN DECISION: Abstract base class extends both BaseService and IModelProvider,
|
|
9
|
+
providing service lifecycle management plus model-specific utilities.
|
|
10
|
+
|
|
11
|
+
RESPONSIBILITIES:
|
|
12
|
+
- Common error handling and logging
|
|
13
|
+
- Task-specific prompt generation
|
|
14
|
+
- Response formatting and validation
|
|
15
|
+
- Performance metrics tracking
|
|
16
|
+
- Retry logic for transient failures
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import asyncio
|
|
20
|
+
import time
|
|
21
|
+
from abc import abstractmethod
|
|
22
|
+
from typing import Any, Dict, Optional
|
|
23
|
+
|
|
24
|
+
from claude_mpm.core.logger import get_logger
|
|
25
|
+
from claude_mpm.services.core.base import BaseService
|
|
26
|
+
from claude_mpm.services.core.interfaces.model import (
|
|
27
|
+
IModelProvider,
|
|
28
|
+
ModelCapability,
|
|
29
|
+
ModelResponse,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class BaseModelProvider(BaseService, IModelProvider):
|
|
34
|
+
"""
|
|
35
|
+
Abstract base class for model providers.
|
|
36
|
+
|
|
37
|
+
WHY: Centralizes common provider functionality like prompt generation,
|
|
38
|
+
error handling, and response formatting.
|
|
39
|
+
|
|
40
|
+
Usage:
|
|
41
|
+
class MyProvider(BaseModelProvider):
|
|
42
|
+
async def analyze_content(self, content, task, model=None, **kwargs):
|
|
43
|
+
prompt = self.get_task_prompt(task, content)
|
|
44
|
+
# Call provider API
|
|
45
|
+
return self.create_response(...)
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
# Task-specific prompt templates
|
|
49
|
+
TASK_PROMPTS: Dict[ModelCapability, str] = {
|
|
50
|
+
ModelCapability.SEO_ANALYSIS: """Analyze the following content for SEO effectiveness. Provide:
|
|
51
|
+
1. Primary and secondary keywords identified
|
|
52
|
+
2. Keyword density analysis
|
|
53
|
+
3. Meta description recommendation
|
|
54
|
+
4. Title tag optimization suggestions
|
|
55
|
+
5. Content structure analysis (H1, H2, etc.)
|
|
56
|
+
6. SEO score (0-100) with justification
|
|
57
|
+
|
|
58
|
+
Content to analyze:
|
|
59
|
+
{content}
|
|
60
|
+
|
|
61
|
+
Provide your analysis in a structured format.""",
|
|
62
|
+
ModelCapability.READABILITY: """Analyze the readability of the following content. Provide:
|
|
63
|
+
1. Readability score (Flesch-Kincaid or similar)
|
|
64
|
+
2. Average sentence length
|
|
65
|
+
3. Complex word count
|
|
66
|
+
4. Grade level assessment
|
|
67
|
+
5. Suggestions for improvement
|
|
68
|
+
6. Overall readability rating (Easy/Medium/Hard)
|
|
69
|
+
|
|
70
|
+
Content to analyze:
|
|
71
|
+
{content}
|
|
72
|
+
|
|
73
|
+
Provide your analysis in a structured format.""",
|
|
74
|
+
ModelCapability.GRAMMAR: """Perform a grammar and style check on the following content. Identify:
|
|
75
|
+
1. Grammatical errors with corrections
|
|
76
|
+
2. Spelling mistakes
|
|
77
|
+
3. Punctuation issues
|
|
78
|
+
4. Style inconsistencies
|
|
79
|
+
5. Clarity improvements
|
|
80
|
+
6. Overall quality score (0-100)
|
|
81
|
+
|
|
82
|
+
Content to analyze:
|
|
83
|
+
{content}
|
|
84
|
+
|
|
85
|
+
Provide your analysis in a structured format.""",
|
|
86
|
+
ModelCapability.SUMMARIZATION: """Provide a comprehensive summary of the following content:
|
|
87
|
+
1. Main topic and key points (3-5 bullet points)
|
|
88
|
+
2. Supporting details
|
|
89
|
+
3. Conclusions or recommendations
|
|
90
|
+
4. One-sentence TL;DR
|
|
91
|
+
|
|
92
|
+
Content to summarize:
|
|
93
|
+
{content}
|
|
94
|
+
|
|
95
|
+
Provide your summary in a structured format.""",
|
|
96
|
+
ModelCapability.KEYWORD_EXTRACTION: """Extract and analyze keywords from the following content:
|
|
97
|
+
1. Primary keywords (top 5-10)
|
|
98
|
+
2. Long-tail keyword phrases
|
|
99
|
+
3. Semantic relationships between keywords
|
|
100
|
+
4. Keyword relevance scores
|
|
101
|
+
5. Suggested additional keywords
|
|
102
|
+
|
|
103
|
+
Content to analyze:
|
|
104
|
+
{content}
|
|
105
|
+
|
|
106
|
+
Provide your keyword analysis in a structured format.""",
|
|
107
|
+
ModelCapability.ACCESSIBILITY: """Analyze the accessibility of the following content:
|
|
108
|
+
1. Language complexity level
|
|
109
|
+
2. Inclusivity assessment
|
|
110
|
+
3. Plain language recommendations
|
|
111
|
+
4. Potential barriers for readers with disabilities
|
|
112
|
+
5. WCAG compliance suggestions
|
|
113
|
+
6. Accessibility score (0-100)
|
|
114
|
+
|
|
115
|
+
Content to analyze:
|
|
116
|
+
{content}
|
|
117
|
+
|
|
118
|
+
Provide your analysis in a structured format.""",
|
|
119
|
+
ModelCapability.SENTIMENT: """Analyze the sentiment and tone of the following content:
|
|
120
|
+
1. Overall sentiment (Positive/Negative/Neutral)
|
|
121
|
+
2. Sentiment score (-1 to +1)
|
|
122
|
+
3. Emotional tone detected
|
|
123
|
+
4. Audience perception analysis
|
|
124
|
+
5. Tone consistency evaluation
|
|
125
|
+
|
|
126
|
+
Content to analyze:
|
|
127
|
+
{content}
|
|
128
|
+
|
|
129
|
+
Provide your analysis in a structured format.""",
|
|
130
|
+
ModelCapability.GENERAL: """Analyze the following content and provide:
|
|
131
|
+
1. Overview and main themes
|
|
132
|
+
2. Quality assessment
|
|
133
|
+
3. Structural analysis
|
|
134
|
+
4. Improvement suggestions
|
|
135
|
+
5. Overall effectiveness rating
|
|
136
|
+
|
|
137
|
+
Content to analyze:
|
|
138
|
+
{content}
|
|
139
|
+
|
|
140
|
+
Provide your analysis in a structured format.""",
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
def __init__(
|
|
144
|
+
self,
|
|
145
|
+
provider_name: str,
|
|
146
|
+
config: Optional[Dict[str, Any]] = None,
|
|
147
|
+
):
|
|
148
|
+
"""
|
|
149
|
+
Initialize base model provider.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
provider_name: Name of the provider (e.g., "ollama", "claude")
|
|
153
|
+
config: Provider-specific configuration
|
|
154
|
+
"""
|
|
155
|
+
super().__init__(service_name=f"{provider_name}_provider", config=config)
|
|
156
|
+
self.provider_name = provider_name
|
|
157
|
+
self.logger = get_logger(f"model.{provider_name}")
|
|
158
|
+
self._request_count = 0
|
|
159
|
+
self._error_count = 0
|
|
160
|
+
self._total_latency = 0.0
|
|
161
|
+
|
|
162
|
+
def get_task_prompt(self, task: ModelCapability, content: str) -> str:
|
|
163
|
+
"""
|
|
164
|
+
Generate task-specific prompt.
|
|
165
|
+
|
|
166
|
+
WHY: Centralizes prompt engineering. Tasks require different analysis
|
|
167
|
+
approaches and output formats.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
task: Type of analysis to perform
|
|
171
|
+
content: Content to analyze
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
Formatted prompt string
|
|
175
|
+
"""
|
|
176
|
+
template = self.TASK_PROMPTS.get(
|
|
177
|
+
task, self.TASK_PROMPTS[ModelCapability.GENERAL]
|
|
178
|
+
)
|
|
179
|
+
return template.format(content=content)
|
|
180
|
+
|
|
181
|
+
def create_response(
|
|
182
|
+
self,
|
|
183
|
+
success: bool,
|
|
184
|
+
model: str,
|
|
185
|
+
task: ModelCapability,
|
|
186
|
+
result: str = "",
|
|
187
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
188
|
+
error: Optional[str] = None,
|
|
189
|
+
) -> ModelResponse:
|
|
190
|
+
"""
|
|
191
|
+
Create standardized model response.
|
|
192
|
+
|
|
193
|
+
WHY: Ensures consistent response format across all providers.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
success: Whether operation succeeded
|
|
197
|
+
model: Model used
|
|
198
|
+
task: Task performed
|
|
199
|
+
result: Analysis result
|
|
200
|
+
metadata: Additional metadata
|
|
201
|
+
error: Error message if failed
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
ModelResponse object
|
|
205
|
+
"""
|
|
206
|
+
return ModelResponse(
|
|
207
|
+
success=success,
|
|
208
|
+
provider=self.provider_name,
|
|
209
|
+
model=model,
|
|
210
|
+
task=task.value,
|
|
211
|
+
result=result,
|
|
212
|
+
metadata=metadata or {},
|
|
213
|
+
error=error,
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
async def analyze_with_retry(
|
|
217
|
+
self,
|
|
218
|
+
analyze_func,
|
|
219
|
+
content: str,
|
|
220
|
+
task: ModelCapability,
|
|
221
|
+
model: Optional[str] = None,
|
|
222
|
+
max_retries: int = 3,
|
|
223
|
+
**kwargs,
|
|
224
|
+
) -> ModelResponse:
|
|
225
|
+
"""
|
|
226
|
+
Execute analysis with retry logic.
|
|
227
|
+
|
|
228
|
+
WHY: Handles transient failures (network issues, rate limits, etc.)
|
|
229
|
+
without requiring retry logic in each provider implementation.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
analyze_func: Async function to call for analysis
|
|
233
|
+
content: Content to analyze
|
|
234
|
+
task: Task to perform
|
|
235
|
+
model: Optional model to use
|
|
236
|
+
max_retries: Maximum retry attempts
|
|
237
|
+
**kwargs: Additional arguments for analyze_func
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
ModelResponse from successful attempt or final error
|
|
241
|
+
"""
|
|
242
|
+
last_error = None
|
|
243
|
+
|
|
244
|
+
for attempt in range(max_retries):
|
|
245
|
+
try:
|
|
246
|
+
start_time = time.time()
|
|
247
|
+
response = await analyze_func(content, task, model, **kwargs)
|
|
248
|
+
latency = time.time() - start_time
|
|
249
|
+
|
|
250
|
+
# Track metrics
|
|
251
|
+
self._request_count += 1
|
|
252
|
+
self._total_latency += latency
|
|
253
|
+
|
|
254
|
+
if response.success:
|
|
255
|
+
response.metadata["latency_seconds"] = latency
|
|
256
|
+
response.metadata["attempt"] = attempt + 1
|
|
257
|
+
self.log_debug(
|
|
258
|
+
f"Analysis completed in {latency:.2f}s (attempt {attempt + 1})"
|
|
259
|
+
)
|
|
260
|
+
return response
|
|
261
|
+
last_error = response.error
|
|
262
|
+
self._error_count += 1
|
|
263
|
+
|
|
264
|
+
except Exception as e:
|
|
265
|
+
last_error = str(e)
|
|
266
|
+
self._error_count += 1
|
|
267
|
+
self.log_warning(
|
|
268
|
+
f"Analysis attempt {attempt + 1} failed: {e}",
|
|
269
|
+
exc_info=True,
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
# Wait before retry (exponential backoff)
|
|
273
|
+
if attempt < max_retries - 1:
|
|
274
|
+
wait_time = 2**attempt # 1s, 2s, 4s
|
|
275
|
+
self.log_info(f"Retrying in {wait_time}s...")
|
|
276
|
+
await asyncio.sleep(wait_time)
|
|
277
|
+
|
|
278
|
+
# All retries failed
|
|
279
|
+
self.log_error(f"All {max_retries} attempts failed. Last error: {last_error}")
|
|
280
|
+
return self.create_response(
|
|
281
|
+
success=False,
|
|
282
|
+
model=model or "unknown",
|
|
283
|
+
task=task,
|
|
284
|
+
error=f"Failed after {max_retries} attempts: {last_error}",
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
def validate_content(self, content: str, max_length: Optional[int] = None) -> bool:
|
|
288
|
+
"""
|
|
289
|
+
Validate content before analysis.
|
|
290
|
+
|
|
291
|
+
WHY: Prevents invalid requests and provides early error detection.
|
|
292
|
+
|
|
293
|
+
Args:
|
|
294
|
+
content: Content to validate
|
|
295
|
+
max_length: Optional maximum length in characters
|
|
296
|
+
|
|
297
|
+
Returns:
|
|
298
|
+
True if valid, False otherwise
|
|
299
|
+
"""
|
|
300
|
+
if not content or not content.strip():
|
|
301
|
+
self.log_warning("Empty content provided for analysis")
|
|
302
|
+
return False
|
|
303
|
+
|
|
304
|
+
if max_length and len(content) > max_length:
|
|
305
|
+
self.log_warning(
|
|
306
|
+
f"Content length {len(content)} exceeds maximum {max_length}"
|
|
307
|
+
)
|
|
308
|
+
return False
|
|
309
|
+
|
|
310
|
+
return True
|
|
311
|
+
|
|
312
|
+
def get_metrics(self) -> Dict[str, Any]:
|
|
313
|
+
"""
|
|
314
|
+
Get provider performance metrics.
|
|
315
|
+
|
|
316
|
+
Returns:
|
|
317
|
+
Dictionary of metrics (request count, error rate, avg latency)
|
|
318
|
+
"""
|
|
319
|
+
avg_latency = (
|
|
320
|
+
self._total_latency / self._request_count if self._request_count > 0 else 0
|
|
321
|
+
)
|
|
322
|
+
error_rate = (
|
|
323
|
+
self._error_count / self._request_count if self._request_count > 0 else 0
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
return {
|
|
327
|
+
"provider": self.provider_name,
|
|
328
|
+
"request_count": self._request_count,
|
|
329
|
+
"error_count": self._error_count,
|
|
330
|
+
"error_rate": error_rate,
|
|
331
|
+
"avg_latency_seconds": avg_latency,
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
async def initialize(self) -> bool:
|
|
335
|
+
"""
|
|
336
|
+
Initialize provider.
|
|
337
|
+
|
|
338
|
+
Subclasses should override to perform provider-specific setup.
|
|
339
|
+
|
|
340
|
+
Returns:
|
|
341
|
+
True if initialization successful
|
|
342
|
+
"""
|
|
343
|
+
self.log_info(f"Initializing {self.provider_name} provider")
|
|
344
|
+
self._initialized = True
|
|
345
|
+
return True
|
|
346
|
+
|
|
347
|
+
async def shutdown(self) -> None:
|
|
348
|
+
"""
|
|
349
|
+
Shutdown provider and cleanup resources.
|
|
350
|
+
|
|
351
|
+
Subclasses should override to perform provider-specific cleanup.
|
|
352
|
+
"""
|
|
353
|
+
self.log_info(f"Shutting down {self.provider_name} provider")
|
|
354
|
+
self._shutdown = True
|
|
355
|
+
|
|
356
|
+
@abstractmethod
|
|
357
|
+
async def get_model_info(self, model: str) -> Dict[str, Any]:
|
|
358
|
+
"""
|
|
359
|
+
Get model information.
|
|
360
|
+
|
|
361
|
+
Must be implemented by subclasses.
|
|
362
|
+
"""
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
__all__ = ["BaseModelProvider"]
|