attune-ai 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- attune/__init__.py +358 -0
- attune/adaptive/__init__.py +13 -0
- attune/adaptive/task_complexity.py +127 -0
- attune/agent_monitoring.py +414 -0
- attune/cache/__init__.py +117 -0
- attune/cache/base.py +166 -0
- attune/cache/dependency_manager.py +256 -0
- attune/cache/hash_only.py +251 -0
- attune/cache/hybrid.py +457 -0
- attune/cache/storage.py +285 -0
- attune/cache_monitor.py +356 -0
- attune/cache_stats.py +298 -0
- attune/cli/__init__.py +152 -0
- attune/cli/__main__.py +12 -0
- attune/cli/commands/__init__.py +1 -0
- attune/cli/commands/batch.py +264 -0
- attune/cli/commands/cache.py +248 -0
- attune/cli/commands/help.py +331 -0
- attune/cli/commands/info.py +140 -0
- attune/cli/commands/inspect.py +436 -0
- attune/cli/commands/inspection.py +57 -0
- attune/cli/commands/memory.py +48 -0
- attune/cli/commands/metrics.py +92 -0
- attune/cli/commands/orchestrate.py +184 -0
- attune/cli/commands/patterns.py +207 -0
- attune/cli/commands/profiling.py +202 -0
- attune/cli/commands/provider.py +98 -0
- attune/cli/commands/routing.py +285 -0
- attune/cli/commands/setup.py +96 -0
- attune/cli/commands/status.py +235 -0
- attune/cli/commands/sync.py +166 -0
- attune/cli/commands/tier.py +121 -0
- attune/cli/commands/utilities.py +114 -0
- attune/cli/commands/workflow.py +579 -0
- attune/cli/core.py +32 -0
- attune/cli/parsers/__init__.py +68 -0
- attune/cli/parsers/batch.py +118 -0
- attune/cli/parsers/cache.py +65 -0
- attune/cli/parsers/help.py +41 -0
- attune/cli/parsers/info.py +26 -0
- attune/cli/parsers/inspect.py +66 -0
- attune/cli/parsers/metrics.py +42 -0
- attune/cli/parsers/orchestrate.py +61 -0
- attune/cli/parsers/patterns.py +54 -0
- attune/cli/parsers/provider.py +40 -0
- attune/cli/parsers/routing.py +110 -0
- attune/cli/parsers/setup.py +42 -0
- attune/cli/parsers/status.py +47 -0
- attune/cli/parsers/sync.py +31 -0
- attune/cli/parsers/tier.py +33 -0
- attune/cli/parsers/workflow.py +77 -0
- attune/cli/utils/__init__.py +1 -0
- attune/cli/utils/data.py +242 -0
- attune/cli/utils/helpers.py +68 -0
- attune/cli_legacy.py +3957 -0
- attune/cli_minimal.py +1159 -0
- attune/cli_router.py +437 -0
- attune/cli_unified.py +814 -0
- attune/config/__init__.py +66 -0
- attune/config/xml_config.py +286 -0
- attune/config.py +545 -0
- attune/coordination.py +870 -0
- attune/core.py +1511 -0
- attune/core_modules/__init__.py +15 -0
- attune/cost_tracker.py +626 -0
- attune/dashboard/__init__.py +41 -0
- attune/dashboard/app.py +512 -0
- attune/dashboard/simple_server.py +435 -0
- attune/dashboard/standalone_server.py +547 -0
- attune/discovery.py +306 -0
- attune/emergence.py +306 -0
- attune/exceptions.py +123 -0
- attune/feedback_loops.py +373 -0
- attune/hot_reload/README.md +473 -0
- attune/hot_reload/__init__.py +62 -0
- attune/hot_reload/config.py +83 -0
- attune/hot_reload/integration.py +229 -0
- attune/hot_reload/reloader.py +298 -0
- attune/hot_reload/watcher.py +183 -0
- attune/hot_reload/websocket.py +177 -0
- attune/levels.py +577 -0
- attune/leverage_points.py +441 -0
- attune/logging_config.py +261 -0
- attune/mcp/__init__.py +10 -0
- attune/mcp/server.py +506 -0
- attune/memory/__init__.py +237 -0
- attune/memory/claude_memory.py +469 -0
- attune/memory/config.py +224 -0
- attune/memory/control_panel.py +1290 -0
- attune/memory/control_panel_support.py +145 -0
- attune/memory/cross_session.py +845 -0
- attune/memory/edges.py +179 -0
- attune/memory/encryption.py +159 -0
- attune/memory/file_session.py +770 -0
- attune/memory/graph.py +570 -0
- attune/memory/long_term.py +913 -0
- attune/memory/long_term_types.py +99 -0
- attune/memory/mixins/__init__.py +25 -0
- attune/memory/mixins/backend_init_mixin.py +249 -0
- attune/memory/mixins/capabilities_mixin.py +208 -0
- attune/memory/mixins/handoff_mixin.py +208 -0
- attune/memory/mixins/lifecycle_mixin.py +49 -0
- attune/memory/mixins/long_term_mixin.py +352 -0
- attune/memory/mixins/promotion_mixin.py +109 -0
- attune/memory/mixins/short_term_mixin.py +182 -0
- attune/memory/nodes.py +179 -0
- attune/memory/redis_bootstrap.py +540 -0
- attune/memory/security/__init__.py +31 -0
- attune/memory/security/audit_logger.py +932 -0
- attune/memory/security/pii_scrubber.py +640 -0
- attune/memory/security/secrets_detector.py +678 -0
- attune/memory/short_term.py +2192 -0
- attune/memory/simple_storage.py +302 -0
- attune/memory/storage/__init__.py +15 -0
- attune/memory/storage_backend.py +167 -0
- attune/memory/summary_index.py +583 -0
- attune/memory/types.py +446 -0
- attune/memory/unified.py +182 -0
- attune/meta_workflows/__init__.py +74 -0
- attune/meta_workflows/agent_creator.py +248 -0
- attune/meta_workflows/builtin_templates.py +567 -0
- attune/meta_workflows/cli_commands/__init__.py +56 -0
- attune/meta_workflows/cli_commands/agent_commands.py +321 -0
- attune/meta_workflows/cli_commands/analytics_commands.py +442 -0
- attune/meta_workflows/cli_commands/config_commands.py +232 -0
- attune/meta_workflows/cli_commands/memory_commands.py +182 -0
- attune/meta_workflows/cli_commands/template_commands.py +354 -0
- attune/meta_workflows/cli_commands/workflow_commands.py +382 -0
- attune/meta_workflows/cli_meta_workflows.py +59 -0
- attune/meta_workflows/form_engine.py +292 -0
- attune/meta_workflows/intent_detector.py +409 -0
- attune/meta_workflows/models.py +569 -0
- attune/meta_workflows/pattern_learner.py +738 -0
- attune/meta_workflows/plan_generator.py +384 -0
- attune/meta_workflows/session_context.py +397 -0
- attune/meta_workflows/template_registry.py +229 -0
- attune/meta_workflows/workflow.py +984 -0
- attune/metrics/__init__.py +12 -0
- attune/metrics/collector.py +31 -0
- attune/metrics/prompt_metrics.py +194 -0
- attune/models/__init__.py +172 -0
- attune/models/__main__.py +13 -0
- attune/models/adaptive_routing.py +437 -0
- attune/models/auth_cli.py +444 -0
- attune/models/auth_strategy.py +450 -0
- attune/models/cli.py +655 -0
- attune/models/empathy_executor.py +354 -0
- attune/models/executor.py +257 -0
- attune/models/fallback.py +762 -0
- attune/models/provider_config.py +282 -0
- attune/models/registry.py +472 -0
- attune/models/tasks.py +359 -0
- attune/models/telemetry/__init__.py +71 -0
- attune/models/telemetry/analytics.py +594 -0
- attune/models/telemetry/backend.py +196 -0
- attune/models/telemetry/data_models.py +431 -0
- attune/models/telemetry/storage.py +489 -0
- attune/models/token_estimator.py +420 -0
- attune/models/validation.py +280 -0
- attune/monitoring/__init__.py +52 -0
- attune/monitoring/alerts.py +946 -0
- attune/monitoring/alerts_cli.py +448 -0
- attune/monitoring/multi_backend.py +271 -0
- attune/monitoring/otel_backend.py +362 -0
- attune/optimization/__init__.py +19 -0
- attune/optimization/context_optimizer.py +272 -0
- attune/orchestration/__init__.py +67 -0
- attune/orchestration/agent_templates.py +707 -0
- attune/orchestration/config_store.py +499 -0
- attune/orchestration/execution_strategies.py +2111 -0
- attune/orchestration/meta_orchestrator.py +1168 -0
- attune/orchestration/pattern_learner.py +696 -0
- attune/orchestration/real_tools.py +931 -0
- attune/pattern_cache.py +187 -0
- attune/pattern_library.py +542 -0
- attune/patterns/debugging/all_patterns.json +81 -0
- attune/patterns/debugging/workflow_20260107_1770825e.json +77 -0
- attune/patterns/refactoring_memory.json +89 -0
- attune/persistence.py +564 -0
- attune/platform_utils.py +265 -0
- attune/plugins/__init__.py +28 -0
- attune/plugins/base.py +361 -0
- attune/plugins/registry.py +268 -0
- attune/project_index/__init__.py +32 -0
- attune/project_index/cli.py +335 -0
- attune/project_index/index.py +667 -0
- attune/project_index/models.py +504 -0
- attune/project_index/reports.py +474 -0
- attune/project_index/scanner.py +777 -0
- attune/project_index/scanner_parallel.py +291 -0
- attune/prompts/__init__.py +61 -0
- attune/prompts/config.py +77 -0
- attune/prompts/context.py +177 -0
- attune/prompts/parser.py +285 -0
- attune/prompts/registry.py +313 -0
- attune/prompts/templates.py +208 -0
- attune/redis_config.py +302 -0
- attune/redis_memory.py +799 -0
- attune/resilience/__init__.py +56 -0
- attune/resilience/circuit_breaker.py +256 -0
- attune/resilience/fallback.py +179 -0
- attune/resilience/health.py +300 -0
- attune/resilience/retry.py +209 -0
- attune/resilience/timeout.py +135 -0
- attune/routing/__init__.py +43 -0
- attune/routing/chain_executor.py +433 -0
- attune/routing/classifier.py +217 -0
- attune/routing/smart_router.py +234 -0
- attune/routing/workflow_registry.py +343 -0
- attune/scaffolding/README.md +589 -0
- attune/scaffolding/__init__.py +35 -0
- attune/scaffolding/__main__.py +14 -0
- attune/scaffolding/cli.py +240 -0
- attune/scaffolding/templates/base_wizard.py.jinja2 +121 -0
- attune/scaffolding/templates/coach_wizard.py.jinja2 +321 -0
- attune/scaffolding/templates/domain_wizard.py.jinja2 +408 -0
- attune/scaffolding/templates/linear_flow_wizard.py.jinja2 +203 -0
- attune/socratic/__init__.py +256 -0
- attune/socratic/ab_testing.py +958 -0
- attune/socratic/blueprint.py +533 -0
- attune/socratic/cli.py +703 -0
- attune/socratic/collaboration.py +1114 -0
- attune/socratic/domain_templates.py +924 -0
- attune/socratic/embeddings.py +738 -0
- attune/socratic/engine.py +794 -0
- attune/socratic/explainer.py +682 -0
- attune/socratic/feedback.py +772 -0
- attune/socratic/forms.py +629 -0
- attune/socratic/generator.py +732 -0
- attune/socratic/llm_analyzer.py +637 -0
- attune/socratic/mcp_server.py +702 -0
- attune/socratic/session.py +312 -0
- attune/socratic/storage.py +667 -0
- attune/socratic/success.py +730 -0
- attune/socratic/visual_editor.py +860 -0
- attune/socratic/web_ui.py +958 -0
- attune/telemetry/__init__.py +39 -0
- attune/telemetry/agent_coordination.py +475 -0
- attune/telemetry/agent_tracking.py +367 -0
- attune/telemetry/approval_gates.py +545 -0
- attune/telemetry/cli.py +1231 -0
- attune/telemetry/commands/__init__.py +14 -0
- attune/telemetry/commands/dashboard_commands.py +696 -0
- attune/telemetry/event_streaming.py +409 -0
- attune/telemetry/feedback_loop.py +567 -0
- attune/telemetry/usage_tracker.py +591 -0
- attune/templates.py +754 -0
- attune/test_generator/__init__.py +38 -0
- attune/test_generator/__main__.py +14 -0
- attune/test_generator/cli.py +234 -0
- attune/test_generator/generator.py +355 -0
- attune/test_generator/risk_analyzer.py +216 -0
- attune/test_generator/templates/unit_test.py.jinja2 +272 -0
- attune/tier_recommender.py +384 -0
- attune/tools.py +183 -0
- attune/trust/__init__.py +28 -0
- attune/trust/circuit_breaker.py +579 -0
- attune/trust_building.py +527 -0
- attune/validation/__init__.py +19 -0
- attune/validation/xml_validator.py +281 -0
- attune/vscode_bridge.py +173 -0
- attune/workflow_commands.py +780 -0
- attune/workflow_patterns/__init__.py +33 -0
- attune/workflow_patterns/behavior.py +249 -0
- attune/workflow_patterns/core.py +76 -0
- attune/workflow_patterns/output.py +99 -0
- attune/workflow_patterns/registry.py +255 -0
- attune/workflow_patterns/structural.py +288 -0
- attune/workflows/__init__.py +539 -0
- attune/workflows/autonomous_test_gen.py +1268 -0
- attune/workflows/base.py +2667 -0
- attune/workflows/batch_processing.py +342 -0
- attune/workflows/bug_predict.py +1084 -0
- attune/workflows/builder.py +273 -0
- attune/workflows/caching.py +253 -0
- attune/workflows/code_review.py +1048 -0
- attune/workflows/code_review_adapters.py +312 -0
- attune/workflows/code_review_pipeline.py +722 -0
- attune/workflows/config.py +645 -0
- attune/workflows/dependency_check.py +644 -0
- attune/workflows/document_gen/__init__.py +25 -0
- attune/workflows/document_gen/config.py +30 -0
- attune/workflows/document_gen/report_formatter.py +162 -0
- attune/workflows/document_gen/workflow.py +1426 -0
- attune/workflows/document_manager.py +216 -0
- attune/workflows/document_manager_README.md +134 -0
- attune/workflows/documentation_orchestrator.py +1205 -0
- attune/workflows/history.py +510 -0
- attune/workflows/keyboard_shortcuts/__init__.py +39 -0
- attune/workflows/keyboard_shortcuts/generators.py +391 -0
- attune/workflows/keyboard_shortcuts/parsers.py +416 -0
- attune/workflows/keyboard_shortcuts/prompts.py +295 -0
- attune/workflows/keyboard_shortcuts/schema.py +193 -0
- attune/workflows/keyboard_shortcuts/workflow.py +509 -0
- attune/workflows/llm_base.py +363 -0
- attune/workflows/manage_docs.py +87 -0
- attune/workflows/manage_docs_README.md +134 -0
- attune/workflows/manage_documentation.py +821 -0
- attune/workflows/new_sample_workflow1.py +149 -0
- attune/workflows/new_sample_workflow1_README.md +150 -0
- attune/workflows/orchestrated_health_check.py +849 -0
- attune/workflows/orchestrated_release_prep.py +600 -0
- attune/workflows/output.py +413 -0
- attune/workflows/perf_audit.py +863 -0
- attune/workflows/pr_review.py +762 -0
- attune/workflows/progress.py +785 -0
- attune/workflows/progress_server.py +322 -0
- attune/workflows/progressive/README 2.md +454 -0
- attune/workflows/progressive/README.md +454 -0
- attune/workflows/progressive/__init__.py +82 -0
- attune/workflows/progressive/cli.py +219 -0
- attune/workflows/progressive/core.py +488 -0
- attune/workflows/progressive/orchestrator.py +723 -0
- attune/workflows/progressive/reports.py +520 -0
- attune/workflows/progressive/telemetry.py +274 -0
- attune/workflows/progressive/test_gen.py +495 -0
- attune/workflows/progressive/workflow.py +589 -0
- attune/workflows/refactor_plan.py +694 -0
- attune/workflows/release_prep.py +895 -0
- attune/workflows/release_prep_crew.py +969 -0
- attune/workflows/research_synthesis.py +404 -0
- attune/workflows/routing.py +168 -0
- attune/workflows/secure_release.py +593 -0
- attune/workflows/security_adapters.py +297 -0
- attune/workflows/security_audit.py +1329 -0
- attune/workflows/security_audit_phase3.py +355 -0
- attune/workflows/seo_optimization.py +633 -0
- attune/workflows/step_config.py +234 -0
- attune/workflows/telemetry_mixin.py +269 -0
- attune/workflows/test5.py +125 -0
- attune/workflows/test5_README.md +158 -0
- attune/workflows/test_coverage_boost_crew.py +849 -0
- attune/workflows/test_gen/__init__.py +52 -0
- attune/workflows/test_gen/ast_analyzer.py +249 -0
- attune/workflows/test_gen/config.py +88 -0
- attune/workflows/test_gen/data_models.py +38 -0
- attune/workflows/test_gen/report_formatter.py +289 -0
- attune/workflows/test_gen/test_templates.py +381 -0
- attune/workflows/test_gen/workflow.py +655 -0
- attune/workflows/test_gen.py +54 -0
- attune/workflows/test_gen_behavioral.py +477 -0
- attune/workflows/test_gen_parallel.py +341 -0
- attune/workflows/test_lifecycle.py +526 -0
- attune/workflows/test_maintenance.py +627 -0
- attune/workflows/test_maintenance_cli.py +590 -0
- attune/workflows/test_maintenance_crew.py +840 -0
- attune/workflows/test_runner.py +622 -0
- attune/workflows/tier_tracking.py +531 -0
- attune/workflows/xml_enhanced_crew.py +285 -0
- attune_ai-2.0.0.dist-info/METADATA +1026 -0
- attune_ai-2.0.0.dist-info/RECORD +457 -0
- attune_ai-2.0.0.dist-info/WHEEL +5 -0
- attune_ai-2.0.0.dist-info/entry_points.txt +26 -0
- attune_ai-2.0.0.dist-info/licenses/LICENSE +201 -0
- attune_ai-2.0.0.dist-info/licenses/LICENSE_CHANGE_ANNOUNCEMENT.md +101 -0
- attune_ai-2.0.0.dist-info/top_level.txt +5 -0
- attune_healthcare/__init__.py +13 -0
- attune_healthcare/monitors/__init__.py +9 -0
- attune_healthcare/monitors/clinical_protocol_monitor.py +315 -0
- attune_healthcare/monitors/monitoring/__init__.py +44 -0
- attune_healthcare/monitors/monitoring/protocol_checker.py +300 -0
- attune_healthcare/monitors/monitoring/protocol_loader.py +214 -0
- attune_healthcare/monitors/monitoring/sensor_parsers.py +306 -0
- attune_healthcare/monitors/monitoring/trajectory_analyzer.py +389 -0
- attune_llm/README.md +553 -0
- attune_llm/__init__.py +28 -0
- attune_llm/agent_factory/__init__.py +53 -0
- attune_llm/agent_factory/adapters/__init__.py +85 -0
- attune_llm/agent_factory/adapters/autogen_adapter.py +312 -0
- attune_llm/agent_factory/adapters/crewai_adapter.py +483 -0
- attune_llm/agent_factory/adapters/haystack_adapter.py +298 -0
- attune_llm/agent_factory/adapters/langchain_adapter.py +362 -0
- attune_llm/agent_factory/adapters/langgraph_adapter.py +333 -0
- attune_llm/agent_factory/adapters/native.py +228 -0
- attune_llm/agent_factory/adapters/wizard_adapter.py +423 -0
- attune_llm/agent_factory/base.py +305 -0
- attune_llm/agent_factory/crews/__init__.py +67 -0
- attune_llm/agent_factory/crews/code_review.py +1113 -0
- attune_llm/agent_factory/crews/health_check.py +1262 -0
- attune_llm/agent_factory/crews/refactoring.py +1128 -0
- attune_llm/agent_factory/crews/security_audit.py +1018 -0
- attune_llm/agent_factory/decorators.py +287 -0
- attune_llm/agent_factory/factory.py +558 -0
- attune_llm/agent_factory/framework.py +193 -0
- attune_llm/agent_factory/memory_integration.py +328 -0
- attune_llm/agent_factory/resilient.py +320 -0
- attune_llm/agents_md/__init__.py +22 -0
- attune_llm/agents_md/loader.py +218 -0
- attune_llm/agents_md/parser.py +271 -0
- attune_llm/agents_md/registry.py +307 -0
- attune_llm/claude_memory.py +466 -0
- attune_llm/cli/__init__.py +8 -0
- attune_llm/cli/sync_claude.py +487 -0
- attune_llm/code_health.py +1313 -0
- attune_llm/commands/__init__.py +51 -0
- attune_llm/commands/context.py +375 -0
- attune_llm/commands/loader.py +301 -0
- attune_llm/commands/models.py +231 -0
- attune_llm/commands/parser.py +371 -0
- attune_llm/commands/registry.py +429 -0
- attune_llm/config/__init__.py +29 -0
- attune_llm/config/unified.py +291 -0
- attune_llm/context/__init__.py +22 -0
- attune_llm/context/compaction.py +455 -0
- attune_llm/context/manager.py +434 -0
- attune_llm/contextual_patterns.py +361 -0
- attune_llm/core.py +907 -0
- attune_llm/git_pattern_extractor.py +435 -0
- attune_llm/hooks/__init__.py +24 -0
- attune_llm/hooks/config.py +306 -0
- attune_llm/hooks/executor.py +289 -0
- attune_llm/hooks/registry.py +302 -0
- attune_llm/hooks/scripts/__init__.py +39 -0
- attune_llm/hooks/scripts/evaluate_session.py +201 -0
- attune_llm/hooks/scripts/first_time_init.py +285 -0
- attune_llm/hooks/scripts/pre_compact.py +207 -0
- attune_llm/hooks/scripts/session_end.py +183 -0
- attune_llm/hooks/scripts/session_start.py +163 -0
- attune_llm/hooks/scripts/suggest_compact.py +225 -0
- attune_llm/learning/__init__.py +30 -0
- attune_llm/learning/evaluator.py +438 -0
- attune_llm/learning/extractor.py +514 -0
- attune_llm/learning/storage.py +560 -0
- attune_llm/levels.py +227 -0
- attune_llm/pattern_confidence.py +414 -0
- attune_llm/pattern_resolver.py +272 -0
- attune_llm/pattern_summary.py +350 -0
- attune_llm/providers.py +967 -0
- attune_llm/routing/__init__.py +32 -0
- attune_llm/routing/model_router.py +362 -0
- attune_llm/security/IMPLEMENTATION_SUMMARY.md +413 -0
- attune_llm/security/PHASE2_COMPLETE.md +384 -0
- attune_llm/security/PHASE2_SECRETS_DETECTOR_COMPLETE.md +271 -0
- attune_llm/security/QUICK_REFERENCE.md +316 -0
- attune_llm/security/README.md +262 -0
- attune_llm/security/__init__.py +62 -0
- attune_llm/security/audit_logger.py +929 -0
- attune_llm/security/audit_logger_example.py +152 -0
- attune_llm/security/pii_scrubber.py +640 -0
- attune_llm/security/secrets_detector.py +678 -0
- attune_llm/security/secrets_detector_example.py +304 -0
- attune_llm/security/secure_memdocs.py +1192 -0
- attune_llm/security/secure_memdocs_example.py +278 -0
- attune_llm/session_status.py +745 -0
- attune_llm/state.py +246 -0
- attune_llm/utils/__init__.py +5 -0
- attune_llm/utils/tokens.py +349 -0
- attune_software/SOFTWARE_PLUGIN_README.md +57 -0
- attune_software/__init__.py +13 -0
- attune_software/cli/__init__.py +120 -0
- attune_software/cli/inspect.py +362 -0
- attune_software/cli.py +574 -0
- attune_software/plugin.py +188 -0
- workflow_scaffolding/__init__.py +11 -0
- workflow_scaffolding/__main__.py +12 -0
- workflow_scaffolding/cli.py +206 -0
- workflow_scaffolding/generator.py +265 -0
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
# Hot-Reload Infrastructure
|
|
2
|
+
|
|
3
|
+
**Version:** 1.0.0
|
|
4
|
+
**Status:** Production Ready
|
|
5
|
+
**Phase:** 2 of 4 - Wizard Factory Enhancement
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
The Hot-Reload Infrastructure enables real-time wizard reloading during development without server restarts. When you modify a wizard file, it's automatically reloaded and re-registered with the wizard API.
|
|
10
|
+
|
|
11
|
+
### Key Features
|
|
12
|
+
|
|
13
|
+
- ✅ **Zero Downtime** - No server restart required
|
|
14
|
+
- ✅ **File Watching** - Automatic detection of wizard file changes
|
|
15
|
+
- ✅ **WebSocket Notifications** - Real-time client notifications
|
|
16
|
+
- ✅ **Graceful Error Handling** - Reload failures don't crash the server
|
|
17
|
+
- ✅ **Development Mode Toggle** - Enable/disable via environment variable
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
### 1. Install Dependencies
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install watchdog
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### 2. Enable Hot-Reload
|
|
30
|
+
|
|
31
|
+
Set environment variable:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
export HOT_RELOAD_ENABLED=true
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### 3. Integrate with Wizard API
|
|
38
|
+
|
|
39
|
+
Add to `backend/api/wizard_api.py`:
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from hot_reload.integration import HotReloadIntegration
|
|
43
|
+
|
|
44
|
+
app = FastAPI(title="Empathy Wizard API")
|
|
45
|
+
|
|
46
|
+
# Create hot-reload integration
|
|
47
|
+
hot_reload = HotReloadIntegration(app, register_wizard)
|
|
48
|
+
|
|
49
|
+
@app.on_event("startup")
|
|
50
|
+
async def startup_event():
|
|
51
|
+
init_wizards() # Initialize wizards
|
|
52
|
+
hot_reload.start() # Start hot-reload
|
|
53
|
+
|
|
54
|
+
@app.on_event("shutdown")
|
|
55
|
+
async def shutdown_event():
|
|
56
|
+
hot_reload.stop()
|
|
57
|
+
|
|
58
|
+
# Add status endpoint
|
|
59
|
+
@app.get("/api/hot-reload/status")
|
|
60
|
+
async def get_hot_reload_status():
|
|
61
|
+
if not hot_reload:
|
|
62
|
+
return {"enabled": False}
|
|
63
|
+
return hot_reload.get_status()
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 4. Connect Frontend (Optional)
|
|
67
|
+
|
|
68
|
+
```javascript
|
|
69
|
+
// Connect to WebSocket for reload notifications
|
|
70
|
+
const ws = new WebSocket('ws://localhost:8001/ws/hot-reload');
|
|
71
|
+
|
|
72
|
+
ws.onmessage = (event) => {
|
|
73
|
+
const message = JSON.parse(event.data);
|
|
74
|
+
|
|
75
|
+
if (message.event === 'wizard_reloaded') {
|
|
76
|
+
console.log(`✓ Wizard reloaded: ${message.wizard_id}`);
|
|
77
|
+
// Refresh wizard list or show notification
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (message.event === 'wizard_reload_failed') {
|
|
81
|
+
console.error(`✗ Reload failed: ${message.error}`);
|
|
82
|
+
// Show error notification
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// Keep connection alive
|
|
87
|
+
setInterval(() => ws.send('ping'), 30000);
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Architecture
|
|
93
|
+
|
|
94
|
+
### Components
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
hot_reload/
|
|
98
|
+
├── __init__.py # Package exports
|
|
99
|
+
├── config.py # Configuration from environment
|
|
100
|
+
├── watcher.py # File system watcher (watchdog)
|
|
101
|
+
├── reloader.py # Dynamic module reloader
|
|
102
|
+
├── websocket.py # WebSocket notifications
|
|
103
|
+
└── integration.py # FastAPI integration
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Flow Diagram
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
File Change → Watcher → Reloader → Register → Notify Clients
|
|
110
|
+
↓ ↓ ↓ ↓
|
|
111
|
+
watchdog importlib wizard_api WebSocket
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Detailed Flow
|
|
115
|
+
|
|
116
|
+
1. **File Watcher** detects `.py` file change in wizard directory
|
|
117
|
+
2. **Extract Wizard ID** from filename (`debug_wizard.py` → `debug`)
|
|
118
|
+
3. **Unload Module** from `sys.modules`
|
|
119
|
+
4. **Reload Module** with `importlib.import_module()`
|
|
120
|
+
5. **Find Wizard Class** (classes ending with "Wizard")
|
|
121
|
+
6. **Re-Register** with wizard API
|
|
122
|
+
7. **Notify Clients** via WebSocket
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Configuration
|
|
127
|
+
|
|
128
|
+
### Environment Variables
|
|
129
|
+
|
|
130
|
+
| Variable | Default | Description |
|
|
131
|
+
|----------|---------|-------------|
|
|
132
|
+
| `HOT_RELOAD_ENABLED` | `false` | Enable hot-reload |
|
|
133
|
+
| `HOT_RELOAD_WATCH_DIRS` | Auto-detect | Comma-separated directories to watch |
|
|
134
|
+
| `HOT_RELOAD_WS_PATH` | `/ws/hot-reload` | WebSocket endpoint path |
|
|
135
|
+
| `HOT_RELOAD_DELAY` | `0.5` | Debounce delay in seconds |
|
|
136
|
+
|
|
137
|
+
### Watched Directories (Default)
|
|
138
|
+
|
|
139
|
+
- `wizards/` - Healthcare wizards
|
|
140
|
+
- `coach_wizards/` - Coach wizards
|
|
141
|
+
- `empathy_software_plugin/wizards/` - AI wizards
|
|
142
|
+
- `attune_llm/wizards/` - Domain wizards
|
|
143
|
+
|
|
144
|
+
### Custom Watch Directories
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
export HOT_RELOAD_WATCH_DIRS="/path/to/custom/wizards,/another/path"
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## API Reference
|
|
153
|
+
|
|
154
|
+
### HotReloadIntegration
|
|
155
|
+
|
|
156
|
+
Main integration class for hot-reload.
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
class HotReloadIntegration:
|
|
160
|
+
def __init__(self, app: FastAPI, register_callback: callable):
|
|
161
|
+
"""Initialize hot-reload integration.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
app: FastAPI application
|
|
165
|
+
register_callback: Function to register wizard
|
|
166
|
+
(wizard_id, wizard_class) -> bool
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
def start(self) -> None:
|
|
170
|
+
"""Start file watcher"""
|
|
171
|
+
|
|
172
|
+
def stop(self) -> None:
|
|
173
|
+
"""Stop file watcher"""
|
|
174
|
+
|
|
175
|
+
def get_status(self) -> dict:
|
|
176
|
+
"""Get hot-reload status"""
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### WizardReloader
|
|
180
|
+
|
|
181
|
+
Handles dynamic module reloading.
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
class WizardReloader:
|
|
185
|
+
def reload_wizard(self, wizard_id: str, file_path: str) -> ReloadResult:
|
|
186
|
+
"""Reload a wizard module"""
|
|
187
|
+
|
|
188
|
+
def get_reload_count(self) -> int:
|
|
189
|
+
"""Get total successful reloads"""
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### ReloadResult
|
|
193
|
+
|
|
194
|
+
Result of a reload operation.
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
@dataclass
|
|
198
|
+
class ReloadResult:
|
|
199
|
+
success: bool
|
|
200
|
+
wizard_id: str
|
|
201
|
+
message: str
|
|
202
|
+
error: str | None
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Usage Examples
|
|
208
|
+
|
|
209
|
+
### Example 1: Basic Integration
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
from fastapi import FastAPI
|
|
213
|
+
from hot_reload.integration import HotReloadIntegration
|
|
214
|
+
|
|
215
|
+
app = FastAPI()
|
|
216
|
+
|
|
217
|
+
def register_wizard(wizard_id: str, wizard_class: type) -> bool:
|
|
218
|
+
"""Your wizard registration logic"""
|
|
219
|
+
try:
|
|
220
|
+
WIZARDS[wizard_id] = wizard_class()
|
|
221
|
+
return True
|
|
222
|
+
except Exception:
|
|
223
|
+
return False
|
|
224
|
+
|
|
225
|
+
# Initialize
|
|
226
|
+
hot_reload = HotReloadIntegration(app, register_wizard)
|
|
227
|
+
|
|
228
|
+
@app.on_event("startup")
|
|
229
|
+
async def startup():
|
|
230
|
+
hot_reload.start()
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Example 2: Manual Reload
|
|
234
|
+
|
|
235
|
+
```python
|
|
236
|
+
from hot_reload import WizardReloader
|
|
237
|
+
|
|
238
|
+
reloader = WizardReloader(
|
|
239
|
+
register_callback=register_wizard,
|
|
240
|
+
notification_callback=notify_clients
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
# Manually reload a wizard
|
|
244
|
+
result = reloader.reload_wizard("debug", "/path/to/debug_wizard.py")
|
|
245
|
+
|
|
246
|
+
if result.success:
|
|
247
|
+
print(f"✓ {result.message}")
|
|
248
|
+
else:
|
|
249
|
+
print(f"✗ Error: {result.error}")
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Example 3: Custom File Watcher
|
|
253
|
+
|
|
254
|
+
```python
|
|
255
|
+
from pathlib import Path
|
|
256
|
+
from hot_reload import WizardFileWatcher
|
|
257
|
+
|
|
258
|
+
def on_change(wizard_id: str, file_path: str):
|
|
259
|
+
print(f"File changed: {wizard_id} at {file_path}")
|
|
260
|
+
|
|
261
|
+
watcher = WizardFileWatcher(
|
|
262
|
+
wizard_dirs=[Path("custom/wizards")],
|
|
263
|
+
reload_callback=on_change
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
with watcher: # Context manager auto-starts/stops
|
|
267
|
+
# Watcher is active
|
|
268
|
+
input("Press Enter to stop...")
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## WebSocket Protocol
|
|
274
|
+
|
|
275
|
+
### Events Sent by Server
|
|
276
|
+
|
|
277
|
+
#### `connected`
|
|
278
|
+
```json
|
|
279
|
+
{
|
|
280
|
+
"event": "connected",
|
|
281
|
+
"message": "Hot-reload notifications enabled",
|
|
282
|
+
"active_connections": 1
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
#### `wizard_reloaded`
|
|
287
|
+
```json
|
|
288
|
+
{
|
|
289
|
+
"event": "wizard_reloaded",
|
|
290
|
+
"wizard_id": "debug",
|
|
291
|
+
"success": true,
|
|
292
|
+
"reload_count": 5
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
#### `wizard_reload_failed`
|
|
297
|
+
```json
|
|
298
|
+
{
|
|
299
|
+
"event": "wizard_reload_failed",
|
|
300
|
+
"wizard_id": "debug",
|
|
301
|
+
"success": false,
|
|
302
|
+
"error": "Failed to import module: ModuleNotFoundError"
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Client Messages
|
|
307
|
+
|
|
308
|
+
Send `"ping"` to keep connection alive.
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## Error Handling
|
|
313
|
+
|
|
314
|
+
### Graceful Degradation
|
|
315
|
+
|
|
316
|
+
Hot-reload failures **never crash the server**. Common errors are handled gracefully:
|
|
317
|
+
|
|
318
|
+
1. **Import Errors** - Logged, old wizard continues working
|
|
319
|
+
2. **Registration Errors** - Logged, old wizard continues working
|
|
320
|
+
3. **File System Errors** - Logged, watcher continues monitoring
|
|
321
|
+
4. **WebSocket Errors** - Client disconnected, others unaffected
|
|
322
|
+
|
|
323
|
+
### Error Scenarios
|
|
324
|
+
|
|
325
|
+
| Scenario | Behavior | Old Wizard |
|
|
326
|
+
|----------|----------|------------|
|
|
327
|
+
| Syntax error in wizard | Reload fails, error logged | Still works |
|
|
328
|
+
| Missing dependency | Import fails, error logged | Still works |
|
|
329
|
+
| Registration fails | Logged, notification sent | Still works |
|
|
330
|
+
| WebSocket disconnect | Client removed | Others unaffected |
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## Performance
|
|
335
|
+
|
|
336
|
+
### Reload Speed
|
|
337
|
+
|
|
338
|
+
- **File Detection**: <100ms (watchdog)
|
|
339
|
+
- **Module Reload**: ~50-200ms
|
|
340
|
+
- **Total Reload Time**: <500ms
|
|
341
|
+
|
|
342
|
+
### Resource Usage
|
|
343
|
+
|
|
344
|
+
- **Memory**: ~2-5MB (watchdog observer)
|
|
345
|
+
- **CPU**: <1% (idle), ~5-10% (during reload)
|
|
346
|
+
- **Network**: Minimal (WebSocket keepalive only)
|
|
347
|
+
|
|
348
|
+
### Optimization Tips
|
|
349
|
+
|
|
350
|
+
1. **Use Reload Delay**: Set `HOT_RELOAD_DELAY=1.0` to debounce multiple file saves
|
|
351
|
+
2. **Limit Watch Directories**: Only watch directories with wizards
|
|
352
|
+
3. **Disable in Production**: `HOT_RELOAD_ENABLED=false` (default)
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
## Troubleshooting
|
|
357
|
+
|
|
358
|
+
### Hot-Reload Not Working
|
|
359
|
+
|
|
360
|
+
**Check if enabled:**
|
|
361
|
+
```bash
|
|
362
|
+
curl http://localhost:8001/api/hot-reload/status
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
**Expected response:**
|
|
366
|
+
```json
|
|
367
|
+
{
|
|
368
|
+
"enabled": true,
|
|
369
|
+
"running": true,
|
|
370
|
+
"watch_dirs": ["/path/to/wizards"],
|
|
371
|
+
"reload_count": 0,
|
|
372
|
+
"websocket_connections": 0
|
|
373
|
+
}
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
**If enabled=false:**
|
|
377
|
+
```bash
|
|
378
|
+
export HOT_RELOAD_ENABLED=true
|
|
379
|
+
# Restart server
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
### Wizard Not Reloading
|
|
383
|
+
|
|
384
|
+
**Check logs for errors:**
|
|
385
|
+
```bash
|
|
386
|
+
tail -f app.log | grep "hot.reload"
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
**Common issues:**
|
|
390
|
+
1. Wizard file not in watched directory
|
|
391
|
+
2. Syntax error in wizard (check reload_failed event)
|
|
392
|
+
3. Import error (missing dependency)
|
|
393
|
+
|
|
394
|
+
### WebSocket Not Connecting
|
|
395
|
+
|
|
396
|
+
**Check endpoint:**
|
|
397
|
+
```bash
|
|
398
|
+
# Default path
|
|
399
|
+
wscat -c ws://localhost:8001/ws/hot-reload
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
**If connection fails:**
|
|
403
|
+
1. Check `HOT_RELOAD_WS_PATH` environment variable
|
|
404
|
+
2. Verify FastAPI app is running
|
|
405
|
+
3. Check firewall/proxy settings
|
|
406
|
+
|
|
407
|
+
---
|
|
408
|
+
|
|
409
|
+
## Testing
|
|
410
|
+
|
|
411
|
+
### Manual Testing
|
|
412
|
+
|
|
413
|
+
1. Start API with hot-reload enabled
|
|
414
|
+
2. Modify a wizard file
|
|
415
|
+
3. Check logs for reload message
|
|
416
|
+
4. Verify wizard works with new code
|
|
417
|
+
|
|
418
|
+
### Integration Tests
|
|
419
|
+
|
|
420
|
+
See `tests/integration/hot_reload/` for automated tests.
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
## Security Considerations
|
|
425
|
+
|
|
426
|
+
### Development Only
|
|
427
|
+
|
|
428
|
+
⚠️ **Hot-reload is for DEVELOPMENT ONLY**
|
|
429
|
+
|
|
430
|
+
- Do NOT enable in production (`HOT_RELOAD_ENABLED=false`)
|
|
431
|
+
- No authentication on WebSocket endpoint
|
|
432
|
+
- File system watching has security implications
|
|
433
|
+
|
|
434
|
+
### Recommended Setup
|
|
435
|
+
|
|
436
|
+
**Development:**
|
|
437
|
+
```bash
|
|
438
|
+
export HOT_RELOAD_ENABLED=true
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
**Production:**
|
|
442
|
+
```bash
|
|
443
|
+
export HOT_RELOAD_ENABLED=false # Default
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
448
|
+
## Roadmap
|
|
449
|
+
|
|
450
|
+
### Phase 2 (Current)
|
|
451
|
+
- ✅ File watching with watchdog
|
|
452
|
+
- ✅ Dynamic module reloading
|
|
453
|
+
- ✅ WebSocket notifications
|
|
454
|
+
- ✅ FastAPI integration
|
|
455
|
+
- ✅ Configuration system
|
|
456
|
+
|
|
457
|
+
### Future Enhancements
|
|
458
|
+
- [ ] Selective reload (only changed functions)
|
|
459
|
+
- [ ] Rollback on failed reload
|
|
460
|
+
- [ ] Reload history/undo
|
|
461
|
+
- [ ] Hot-reload for config files
|
|
462
|
+
- [ ] Browser extension for notifications
|
|
463
|
+
|
|
464
|
+
---
|
|
465
|
+
|
|
466
|
+
## License
|
|
467
|
+
|
|
468
|
+
Copyright 2025 Smart AI Memory, LLC
|
|
469
|
+
Licensed under Fair Source 0.9
|
|
470
|
+
|
|
471
|
+
---
|
|
472
|
+
|
|
473
|
+
**For questions or issues, see:** [Wizard Factory Enhancement Plan](../docs/architecture/WIZARD_FACTORY_DISCOVERY.md)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Hot-Reload Infrastructure for Workflow Factory.
|
|
2
|
+
|
|
3
|
+
Enables real-time workflow reloading during development without server restarts.
|
|
4
|
+
|
|
5
|
+
Features:
|
|
6
|
+
- File system monitoring with watchdog
|
|
7
|
+
- Dynamic module reloading
|
|
8
|
+
- WebSocket notifications to frontend
|
|
9
|
+
- Graceful error handling
|
|
10
|
+
- Development mode toggle
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
from hot_reload.integration import HotReloadIntegration
|
|
14
|
+
|
|
15
|
+
app = FastAPI()
|
|
16
|
+
hot_reload = HotReloadIntegration(app, register_workflow)
|
|
17
|
+
|
|
18
|
+
@app.on_event("startup")
|
|
19
|
+
async def startup():
|
|
20
|
+
hot_reload.start()
|
|
21
|
+
|
|
22
|
+
@app.on_event("shutdown")
|
|
23
|
+
async def shutdown():
|
|
24
|
+
hot_reload.stop()
|
|
25
|
+
|
|
26
|
+
Environment Variables:
|
|
27
|
+
HOT_RELOAD_ENABLED: Enable hot-reload (default: false)
|
|
28
|
+
HOT_RELOAD_WATCH_DIRS: Comma-separated directories to watch
|
|
29
|
+
HOT_RELOAD_WS_PATH: WebSocket path (default: /ws/hot-reload)
|
|
30
|
+
HOT_RELOAD_DELAY: Reload delay in seconds (default: 0.5)
|
|
31
|
+
|
|
32
|
+
Copyright 2025 Smart AI Memory, LLC
|
|
33
|
+
Licensed under Fair Source 0.9
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
from .config import HotReloadConfig, get_hot_reload_config
|
|
37
|
+
from .integration import HotReloadIntegration
|
|
38
|
+
from .reloader import ReloadResult, WorkflowReloader
|
|
39
|
+
from .watcher import WorkflowFileWatcher
|
|
40
|
+
from .websocket import (
|
|
41
|
+
ReloadNotificationManager,
|
|
42
|
+
create_notification_callback,
|
|
43
|
+
get_notification_manager,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
__all__ = [
|
|
47
|
+
# Core components
|
|
48
|
+
"WorkflowFileWatcher",
|
|
49
|
+
"WorkflowReloader",
|
|
50
|
+
"ReloadResult",
|
|
51
|
+
# WebSocket
|
|
52
|
+
"ReloadNotificationManager",
|
|
53
|
+
"get_notification_manager",
|
|
54
|
+
"create_notification_callback",
|
|
55
|
+
# Config
|
|
56
|
+
"HotReloadConfig",
|
|
57
|
+
"get_hot_reload_config",
|
|
58
|
+
# Integration
|
|
59
|
+
"HotReloadIntegration",
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
__version__ = "1.0.0"
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""Configuration for hot-reload system.
|
|
2
|
+
|
|
3
|
+
Copyright 2025 Smart AI Memory, LLC
|
|
4
|
+
Licensed under Fair Source 0.9
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class HotReloadConfig:
|
|
12
|
+
"""Configuration for hot-reload system."""
|
|
13
|
+
|
|
14
|
+
def __init__(self):
|
|
15
|
+
"""Initialize configuration from environment."""
|
|
16
|
+
# Development mode (enables hot-reload)
|
|
17
|
+
self.enabled = os.getenv("HOT_RELOAD_ENABLED", "false").lower() == "true"
|
|
18
|
+
|
|
19
|
+
# Workflow directories to watch
|
|
20
|
+
self.watch_dirs = self._get_watch_dirs()
|
|
21
|
+
|
|
22
|
+
# WebSocket endpoint for notifications
|
|
23
|
+
self.websocket_path = os.getenv("HOT_RELOAD_WS_PATH", "/ws/hot-reload")
|
|
24
|
+
|
|
25
|
+
# Reload delay (seconds) to debounce multiple file changes
|
|
26
|
+
self.reload_delay = float(os.getenv("HOT_RELOAD_DELAY", "0.5"))
|
|
27
|
+
|
|
28
|
+
def _get_watch_dirs(self) -> list[Path]:
|
|
29
|
+
"""Get directories to watch for workflow changes.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
List of directories to watch
|
|
33
|
+
|
|
34
|
+
"""
|
|
35
|
+
# Default workflow directories
|
|
36
|
+
project_root = Path(__file__).parent.parent
|
|
37
|
+
|
|
38
|
+
default_dirs = [
|
|
39
|
+
project_root / "workflows",
|
|
40
|
+
project_root / "empathy_software_plugin" / "workflows",
|
|
41
|
+
project_root / "empathy_llm_toolkit" / "workflows",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
# Filter to only existing directories
|
|
45
|
+
watch_dirs = [d for d in default_dirs if d.exists()]
|
|
46
|
+
|
|
47
|
+
# Allow override via environment variable
|
|
48
|
+
env_dirs = os.getenv("HOT_RELOAD_WATCH_DIRS")
|
|
49
|
+
if env_dirs:
|
|
50
|
+
watch_dirs = [Path(d.strip()) for d in env_dirs.split(",")]
|
|
51
|
+
|
|
52
|
+
return watch_dirs
|
|
53
|
+
|
|
54
|
+
def to_dict(self) -> dict:
|
|
55
|
+
"""Convert config to dictionary.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
Configuration as dictionary
|
|
59
|
+
|
|
60
|
+
"""
|
|
61
|
+
return {
|
|
62
|
+
"enabled": self.enabled,
|
|
63
|
+
"watch_dirs": [str(d) for d in self.watch_dirs],
|
|
64
|
+
"websocket_path": self.websocket_path,
|
|
65
|
+
"reload_delay": self.reload_delay,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
# Global config instance
|
|
70
|
+
_config: HotReloadConfig | None = None
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def get_hot_reload_config() -> HotReloadConfig:
|
|
74
|
+
"""Get the global hot-reload configuration.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Global HotReloadConfig instance
|
|
78
|
+
|
|
79
|
+
"""
|
|
80
|
+
global _config
|
|
81
|
+
if _config is None:
|
|
82
|
+
_config = HotReloadConfig()
|
|
83
|
+
return _config
|