empathy-framework 5.3.0__py3-none-any.whl → 5.4.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.
- empathy_framework-5.4.0.dist-info/METADATA +47 -0
- empathy_framework-5.4.0.dist-info/RECORD +8 -0
- {empathy_framework-5.3.0.dist-info → empathy_framework-5.4.0.dist-info}/top_level.txt +0 -1
- empathy_healthcare_plugin/__init__.py +12 -11
- empathy_llm_toolkit/__init__.py +12 -26
- empathy_os/__init__.py +12 -356
- empathy_software_plugin/__init__.py +12 -11
- empathy_framework-5.3.0.dist-info/METADATA +0 -1026
- empathy_framework-5.3.0.dist-info/RECORD +0 -456
- empathy_framework-5.3.0.dist-info/entry_points.txt +0 -26
- empathy_framework-5.3.0.dist-info/licenses/LICENSE +0 -201
- empathy_framework-5.3.0.dist-info/licenses/LICENSE_CHANGE_ANNOUNCEMENT.md +0 -101
- empathy_healthcare_plugin/monitors/__init__.py +0 -9
- empathy_healthcare_plugin/monitors/clinical_protocol_monitor.py +0 -315
- empathy_healthcare_plugin/monitors/monitoring/__init__.py +0 -44
- empathy_healthcare_plugin/monitors/monitoring/protocol_checker.py +0 -300
- empathy_healthcare_plugin/monitors/monitoring/protocol_loader.py +0 -214
- empathy_healthcare_plugin/monitors/monitoring/sensor_parsers.py +0 -306
- empathy_healthcare_plugin/monitors/monitoring/trajectory_analyzer.py +0 -389
- empathy_healthcare_plugin/protocols/cardiac.json +0 -93
- empathy_healthcare_plugin/protocols/post_operative.json +0 -92
- empathy_healthcare_plugin/protocols/respiratory.json +0 -92
- empathy_healthcare_plugin/protocols/sepsis.json +0 -141
- empathy_llm_toolkit/README.md +0 -553
- empathy_llm_toolkit/agent_factory/__init__.py +0 -53
- empathy_llm_toolkit/agent_factory/adapters/__init__.py +0 -85
- empathy_llm_toolkit/agent_factory/adapters/autogen_adapter.py +0 -312
- empathy_llm_toolkit/agent_factory/adapters/crewai_adapter.py +0 -483
- empathy_llm_toolkit/agent_factory/adapters/haystack_adapter.py +0 -298
- empathy_llm_toolkit/agent_factory/adapters/langchain_adapter.py +0 -362
- empathy_llm_toolkit/agent_factory/adapters/langgraph_adapter.py +0 -333
- empathy_llm_toolkit/agent_factory/adapters/native.py +0 -228
- empathy_llm_toolkit/agent_factory/adapters/wizard_adapter.py +0 -423
- empathy_llm_toolkit/agent_factory/base.py +0 -305
- empathy_llm_toolkit/agent_factory/crews/__init__.py +0 -67
- empathy_llm_toolkit/agent_factory/crews/code_review.py +0 -1113
- empathy_llm_toolkit/agent_factory/crews/health_check.py +0 -1262
- empathy_llm_toolkit/agent_factory/crews/refactoring.py +0 -1128
- empathy_llm_toolkit/agent_factory/crews/security_audit.py +0 -1018
- empathy_llm_toolkit/agent_factory/decorators.py +0 -287
- empathy_llm_toolkit/agent_factory/factory.py +0 -558
- empathy_llm_toolkit/agent_factory/framework.py +0 -193
- empathy_llm_toolkit/agent_factory/memory_integration.py +0 -328
- empathy_llm_toolkit/agent_factory/resilient.py +0 -320
- empathy_llm_toolkit/agents_md/__init__.py +0 -22
- empathy_llm_toolkit/agents_md/loader.py +0 -218
- empathy_llm_toolkit/agents_md/parser.py +0 -271
- empathy_llm_toolkit/agents_md/registry.py +0 -307
- empathy_llm_toolkit/claude_memory.py +0 -466
- empathy_llm_toolkit/cli/__init__.py +0 -8
- empathy_llm_toolkit/cli/sync_claude.py +0 -487
- empathy_llm_toolkit/code_health.py +0 -1313
- empathy_llm_toolkit/commands/__init__.py +0 -51
- empathy_llm_toolkit/commands/context.py +0 -375
- empathy_llm_toolkit/commands/loader.py +0 -301
- empathy_llm_toolkit/commands/models.py +0 -231
- empathy_llm_toolkit/commands/parser.py +0 -371
- empathy_llm_toolkit/commands/registry.py +0 -429
- empathy_llm_toolkit/config/__init__.py +0 -29
- empathy_llm_toolkit/config/unified.py +0 -291
- empathy_llm_toolkit/context/__init__.py +0 -22
- empathy_llm_toolkit/context/compaction.py +0 -455
- empathy_llm_toolkit/context/manager.py +0 -434
- empathy_llm_toolkit/contextual_patterns.py +0 -361
- empathy_llm_toolkit/core.py +0 -907
- empathy_llm_toolkit/git_pattern_extractor.py +0 -435
- empathy_llm_toolkit/hooks/__init__.py +0 -24
- empathy_llm_toolkit/hooks/config.py +0 -306
- empathy_llm_toolkit/hooks/executor.py +0 -289
- empathy_llm_toolkit/hooks/registry.py +0 -302
- empathy_llm_toolkit/hooks/scripts/__init__.py +0 -39
- empathy_llm_toolkit/hooks/scripts/evaluate_session.py +0 -201
- empathy_llm_toolkit/hooks/scripts/first_time_init.py +0 -285
- empathy_llm_toolkit/hooks/scripts/pre_compact.py +0 -207
- empathy_llm_toolkit/hooks/scripts/session_end.py +0 -183
- empathy_llm_toolkit/hooks/scripts/session_start.py +0 -163
- empathy_llm_toolkit/hooks/scripts/suggest_compact.py +0 -225
- empathy_llm_toolkit/learning/__init__.py +0 -30
- empathy_llm_toolkit/learning/evaluator.py +0 -438
- empathy_llm_toolkit/learning/extractor.py +0 -514
- empathy_llm_toolkit/learning/storage.py +0 -560
- empathy_llm_toolkit/levels.py +0 -227
- empathy_llm_toolkit/pattern_confidence.py +0 -414
- empathy_llm_toolkit/pattern_resolver.py +0 -272
- empathy_llm_toolkit/pattern_summary.py +0 -350
- empathy_llm_toolkit/providers.py +0 -967
- empathy_llm_toolkit/routing/__init__.py +0 -32
- empathy_llm_toolkit/routing/model_router.py +0 -362
- empathy_llm_toolkit/security/IMPLEMENTATION_SUMMARY.md +0 -413
- empathy_llm_toolkit/security/PHASE2_COMPLETE.md +0 -384
- empathy_llm_toolkit/security/PHASE2_SECRETS_DETECTOR_COMPLETE.md +0 -271
- empathy_llm_toolkit/security/QUICK_REFERENCE.md +0 -316
- empathy_llm_toolkit/security/README.md +0 -262
- empathy_llm_toolkit/security/__init__.py +0 -62
- empathy_llm_toolkit/security/audit_logger.py +0 -929
- empathy_llm_toolkit/security/audit_logger_example.py +0 -152
- empathy_llm_toolkit/security/pii_scrubber.py +0 -640
- empathy_llm_toolkit/security/secrets_detector.py +0 -678
- empathy_llm_toolkit/security/secrets_detector_example.py +0 -304
- empathy_llm_toolkit/security/secure_memdocs.py +0 -1192
- empathy_llm_toolkit/security/secure_memdocs_example.py +0 -278
- empathy_llm_toolkit/session_status.py +0 -745
- empathy_llm_toolkit/state.py +0 -246
- empathy_llm_toolkit/utils/__init__.py +0 -5
- empathy_llm_toolkit/utils/tokens.py +0 -349
- empathy_os/adaptive/__init__.py +0 -13
- empathy_os/adaptive/task_complexity.py +0 -127
- empathy_os/agent_monitoring.py +0 -414
- empathy_os/cache/__init__.py +0 -117
- empathy_os/cache/base.py +0 -166
- empathy_os/cache/dependency_manager.py +0 -256
- empathy_os/cache/hash_only.py +0 -251
- empathy_os/cache/hybrid.py +0 -457
- empathy_os/cache/storage.py +0 -285
- empathy_os/cache_monitor.py +0 -356
- empathy_os/cache_stats.py +0 -298
- empathy_os/cli/__init__.py +0 -152
- empathy_os/cli/__main__.py +0 -12
- empathy_os/cli/commands/__init__.py +0 -1
- empathy_os/cli/commands/batch.py +0 -264
- empathy_os/cli/commands/cache.py +0 -248
- empathy_os/cli/commands/help.py +0 -331
- empathy_os/cli/commands/info.py +0 -140
- empathy_os/cli/commands/inspect.py +0 -436
- empathy_os/cli/commands/inspection.py +0 -57
- empathy_os/cli/commands/memory.py +0 -48
- empathy_os/cli/commands/metrics.py +0 -92
- empathy_os/cli/commands/orchestrate.py +0 -184
- empathy_os/cli/commands/patterns.py +0 -207
- empathy_os/cli/commands/profiling.py +0 -202
- empathy_os/cli/commands/provider.py +0 -98
- empathy_os/cli/commands/routing.py +0 -285
- empathy_os/cli/commands/setup.py +0 -96
- empathy_os/cli/commands/status.py +0 -235
- empathy_os/cli/commands/sync.py +0 -166
- empathy_os/cli/commands/tier.py +0 -121
- empathy_os/cli/commands/utilities.py +0 -114
- empathy_os/cli/commands/workflow.py +0 -579
- empathy_os/cli/core.py +0 -32
- empathy_os/cli/parsers/__init__.py +0 -68
- empathy_os/cli/parsers/batch.py +0 -118
- empathy_os/cli/parsers/cache.py +0 -65
- empathy_os/cli/parsers/help.py +0 -41
- empathy_os/cli/parsers/info.py +0 -26
- empathy_os/cli/parsers/inspect.py +0 -66
- empathy_os/cli/parsers/metrics.py +0 -42
- empathy_os/cli/parsers/orchestrate.py +0 -61
- empathy_os/cli/parsers/patterns.py +0 -54
- empathy_os/cli/parsers/provider.py +0 -40
- empathy_os/cli/parsers/routing.py +0 -110
- empathy_os/cli/parsers/setup.py +0 -42
- empathy_os/cli/parsers/status.py +0 -47
- empathy_os/cli/parsers/sync.py +0 -31
- empathy_os/cli/parsers/tier.py +0 -33
- empathy_os/cli/parsers/workflow.py +0 -77
- empathy_os/cli/utils/__init__.py +0 -1
- empathy_os/cli/utils/data.py +0 -242
- empathy_os/cli/utils/helpers.py +0 -68
- empathy_os/cli_legacy.py +0 -3957
- empathy_os/cli_minimal.py +0 -1159
- empathy_os/cli_router.py +0 -437
- empathy_os/cli_unified.py +0 -814
- empathy_os/config/__init__.py +0 -66
- empathy_os/config/xml_config.py +0 -286
- empathy_os/config.py +0 -545
- empathy_os/coordination.py +0 -870
- empathy_os/core.py +0 -1511
- empathy_os/core_modules/__init__.py +0 -15
- empathy_os/cost_tracker.py +0 -626
- empathy_os/dashboard/__init__.py +0 -41
- empathy_os/dashboard/app.py +0 -512
- empathy_os/dashboard/simple_server.py +0 -435
- empathy_os/dashboard/standalone_server.py +0 -547
- empathy_os/discovery.py +0 -306
- empathy_os/emergence.py +0 -306
- empathy_os/exceptions.py +0 -123
- empathy_os/feedback_loops.py +0 -373
- empathy_os/hot_reload/README.md +0 -473
- empathy_os/hot_reload/__init__.py +0 -62
- empathy_os/hot_reload/config.py +0 -83
- empathy_os/hot_reload/integration.py +0 -229
- empathy_os/hot_reload/reloader.py +0 -298
- empathy_os/hot_reload/watcher.py +0 -183
- empathy_os/hot_reload/websocket.py +0 -177
- empathy_os/levels.py +0 -577
- empathy_os/leverage_points.py +0 -441
- empathy_os/logging_config.py +0 -261
- empathy_os/mcp/__init__.py +0 -10
- empathy_os/mcp/server.py +0 -506
- empathy_os/memory/__init__.py +0 -237
- empathy_os/memory/claude_memory.py +0 -469
- empathy_os/memory/config.py +0 -224
- empathy_os/memory/control_panel.py +0 -1290
- empathy_os/memory/control_panel_support.py +0 -145
- empathy_os/memory/cross_session.py +0 -845
- empathy_os/memory/edges.py +0 -179
- empathy_os/memory/encryption.py +0 -159
- empathy_os/memory/file_session.py +0 -770
- empathy_os/memory/graph.py +0 -570
- empathy_os/memory/long_term.py +0 -913
- empathy_os/memory/long_term_types.py +0 -99
- empathy_os/memory/mixins/__init__.py +0 -25
- empathy_os/memory/mixins/backend_init_mixin.py +0 -249
- empathy_os/memory/mixins/capabilities_mixin.py +0 -208
- empathy_os/memory/mixins/handoff_mixin.py +0 -208
- empathy_os/memory/mixins/lifecycle_mixin.py +0 -49
- empathy_os/memory/mixins/long_term_mixin.py +0 -352
- empathy_os/memory/mixins/promotion_mixin.py +0 -109
- empathy_os/memory/mixins/short_term_mixin.py +0 -182
- empathy_os/memory/nodes.py +0 -179
- empathy_os/memory/redis_bootstrap.py +0 -540
- empathy_os/memory/security/__init__.py +0 -31
- empathy_os/memory/security/audit_logger.py +0 -932
- empathy_os/memory/security/pii_scrubber.py +0 -640
- empathy_os/memory/security/secrets_detector.py +0 -678
- empathy_os/memory/short_term.py +0 -2192
- empathy_os/memory/simple_storage.py +0 -302
- empathy_os/memory/storage/__init__.py +0 -15
- empathy_os/memory/storage_backend.py +0 -167
- empathy_os/memory/summary_index.py +0 -583
- empathy_os/memory/types.py +0 -446
- empathy_os/memory/unified.py +0 -182
- empathy_os/meta_workflows/__init__.py +0 -74
- empathy_os/meta_workflows/agent_creator.py +0 -248
- empathy_os/meta_workflows/builtin_templates.py +0 -567
- empathy_os/meta_workflows/cli_commands/__init__.py +0 -56
- empathy_os/meta_workflows/cli_commands/agent_commands.py +0 -321
- empathy_os/meta_workflows/cli_commands/analytics_commands.py +0 -442
- empathy_os/meta_workflows/cli_commands/config_commands.py +0 -232
- empathy_os/meta_workflows/cli_commands/memory_commands.py +0 -182
- empathy_os/meta_workflows/cli_commands/template_commands.py +0 -354
- empathy_os/meta_workflows/cli_commands/workflow_commands.py +0 -382
- empathy_os/meta_workflows/cli_meta_workflows.py +0 -59
- empathy_os/meta_workflows/form_engine.py +0 -292
- empathy_os/meta_workflows/intent_detector.py +0 -409
- empathy_os/meta_workflows/models.py +0 -569
- empathy_os/meta_workflows/pattern_learner.py +0 -738
- empathy_os/meta_workflows/plan_generator.py +0 -384
- empathy_os/meta_workflows/session_context.py +0 -397
- empathy_os/meta_workflows/template_registry.py +0 -229
- empathy_os/meta_workflows/workflow.py +0 -984
- empathy_os/metrics/__init__.py +0 -12
- empathy_os/metrics/collector.py +0 -31
- empathy_os/metrics/prompt_metrics.py +0 -194
- empathy_os/models/__init__.py +0 -172
- empathy_os/models/__main__.py +0 -13
- empathy_os/models/adaptive_routing.py +0 -437
- empathy_os/models/auth_cli.py +0 -444
- empathy_os/models/auth_strategy.py +0 -450
- empathy_os/models/cli.py +0 -655
- empathy_os/models/empathy_executor.py +0 -354
- empathy_os/models/executor.py +0 -257
- empathy_os/models/fallback.py +0 -762
- empathy_os/models/provider_config.py +0 -282
- empathy_os/models/registry.py +0 -472
- empathy_os/models/tasks.py +0 -359
- empathy_os/models/telemetry/__init__.py +0 -71
- empathy_os/models/telemetry/analytics.py +0 -594
- empathy_os/models/telemetry/backend.py +0 -196
- empathy_os/models/telemetry/data_models.py +0 -431
- empathy_os/models/telemetry/storage.py +0 -489
- empathy_os/models/token_estimator.py +0 -420
- empathy_os/models/validation.py +0 -280
- empathy_os/monitoring/__init__.py +0 -52
- empathy_os/monitoring/alerts.py +0 -946
- empathy_os/monitoring/alerts_cli.py +0 -448
- empathy_os/monitoring/multi_backend.py +0 -271
- empathy_os/monitoring/otel_backend.py +0 -362
- empathy_os/optimization/__init__.py +0 -19
- empathy_os/optimization/context_optimizer.py +0 -272
- empathy_os/orchestration/__init__.py +0 -67
- empathy_os/orchestration/agent_templates.py +0 -707
- empathy_os/orchestration/config_store.py +0 -499
- empathy_os/orchestration/execution_strategies.py +0 -2111
- empathy_os/orchestration/meta_orchestrator.py +0 -1168
- empathy_os/orchestration/pattern_learner.py +0 -696
- empathy_os/orchestration/real_tools.py +0 -931
- empathy_os/pattern_cache.py +0 -187
- empathy_os/pattern_library.py +0 -542
- empathy_os/patterns/debugging/all_patterns.json +0 -81
- empathy_os/patterns/debugging/workflow_20260107_1770825e.json +0 -77
- empathy_os/patterns/refactoring_memory.json +0 -89
- empathy_os/persistence.py +0 -564
- empathy_os/platform_utils.py +0 -265
- empathy_os/plugins/__init__.py +0 -28
- empathy_os/plugins/base.py +0 -361
- empathy_os/plugins/registry.py +0 -268
- empathy_os/project_index/__init__.py +0 -32
- empathy_os/project_index/cli.py +0 -335
- empathy_os/project_index/index.py +0 -667
- empathy_os/project_index/models.py +0 -504
- empathy_os/project_index/reports.py +0 -474
- empathy_os/project_index/scanner.py +0 -777
- empathy_os/project_index/scanner_parallel.py +0 -291
- empathy_os/prompts/__init__.py +0 -61
- empathy_os/prompts/config.py +0 -77
- empathy_os/prompts/context.py +0 -177
- empathy_os/prompts/parser.py +0 -285
- empathy_os/prompts/registry.py +0 -313
- empathy_os/prompts/templates.py +0 -208
- empathy_os/redis_config.py +0 -302
- empathy_os/redis_memory.py +0 -799
- empathy_os/resilience/__init__.py +0 -56
- empathy_os/resilience/circuit_breaker.py +0 -256
- empathy_os/resilience/fallback.py +0 -179
- empathy_os/resilience/health.py +0 -300
- empathy_os/resilience/retry.py +0 -209
- empathy_os/resilience/timeout.py +0 -135
- empathy_os/routing/__init__.py +0 -43
- empathy_os/routing/chain_executor.py +0 -433
- empathy_os/routing/classifier.py +0 -217
- empathy_os/routing/smart_router.py +0 -234
- empathy_os/routing/workflow_registry.py +0 -343
- empathy_os/scaffolding/README.md +0 -589
- empathy_os/scaffolding/__init__.py +0 -35
- empathy_os/scaffolding/__main__.py +0 -14
- empathy_os/scaffolding/cli.py +0 -240
- empathy_os/socratic/__init__.py +0 -256
- empathy_os/socratic/ab_testing.py +0 -958
- empathy_os/socratic/blueprint.py +0 -533
- empathy_os/socratic/cli.py +0 -703
- empathy_os/socratic/collaboration.py +0 -1114
- empathy_os/socratic/domain_templates.py +0 -924
- empathy_os/socratic/embeddings.py +0 -738
- empathy_os/socratic/engine.py +0 -794
- empathy_os/socratic/explainer.py +0 -682
- empathy_os/socratic/feedback.py +0 -772
- empathy_os/socratic/forms.py +0 -629
- empathy_os/socratic/generator.py +0 -732
- empathy_os/socratic/llm_analyzer.py +0 -637
- empathy_os/socratic/mcp_server.py +0 -702
- empathy_os/socratic/session.py +0 -312
- empathy_os/socratic/storage.py +0 -667
- empathy_os/socratic/success.py +0 -730
- empathy_os/socratic/visual_editor.py +0 -860
- empathy_os/socratic/web_ui.py +0 -958
- empathy_os/telemetry/__init__.py +0 -39
- empathy_os/telemetry/agent_coordination.py +0 -475
- empathy_os/telemetry/agent_tracking.py +0 -367
- empathy_os/telemetry/approval_gates.py +0 -545
- empathy_os/telemetry/cli.py +0 -1231
- empathy_os/telemetry/commands/__init__.py +0 -14
- empathy_os/telemetry/commands/dashboard_commands.py +0 -696
- empathy_os/telemetry/event_streaming.py +0 -409
- empathy_os/telemetry/feedback_loop.py +0 -567
- empathy_os/telemetry/usage_tracker.py +0 -591
- empathy_os/templates.py +0 -754
- empathy_os/test_generator/__init__.py +0 -38
- empathy_os/test_generator/__main__.py +0 -14
- empathy_os/test_generator/cli.py +0 -234
- empathy_os/test_generator/generator.py +0 -355
- empathy_os/test_generator/risk_analyzer.py +0 -216
- empathy_os/tier_recommender.py +0 -384
- empathy_os/tools.py +0 -183
- empathy_os/trust/__init__.py +0 -28
- empathy_os/trust/circuit_breaker.py +0 -579
- empathy_os/trust_building.py +0 -527
- empathy_os/validation/__init__.py +0 -19
- empathy_os/validation/xml_validator.py +0 -281
- empathy_os/vscode_bridge.py +0 -173
- empathy_os/workflow_commands.py +0 -780
- empathy_os/workflow_patterns/__init__.py +0 -33
- empathy_os/workflow_patterns/behavior.py +0 -249
- empathy_os/workflow_patterns/core.py +0 -76
- empathy_os/workflow_patterns/output.py +0 -99
- empathy_os/workflow_patterns/registry.py +0 -255
- empathy_os/workflow_patterns/structural.py +0 -288
- empathy_os/workflows/__init__.py +0 -539
- empathy_os/workflows/autonomous_test_gen.py +0 -1268
- empathy_os/workflows/base.py +0 -2667
- empathy_os/workflows/batch_processing.py +0 -342
- empathy_os/workflows/bug_predict.py +0 -1084
- empathy_os/workflows/builder.py +0 -273
- empathy_os/workflows/caching.py +0 -253
- empathy_os/workflows/code_review.py +0 -1048
- empathy_os/workflows/code_review_adapters.py +0 -312
- empathy_os/workflows/code_review_pipeline.py +0 -722
- empathy_os/workflows/config.py +0 -645
- empathy_os/workflows/dependency_check.py +0 -644
- empathy_os/workflows/document_gen/__init__.py +0 -25
- empathy_os/workflows/document_gen/config.py +0 -30
- empathy_os/workflows/document_gen/report_formatter.py +0 -162
- empathy_os/workflows/document_gen/workflow.py +0 -1426
- empathy_os/workflows/document_manager.py +0 -216
- empathy_os/workflows/document_manager_README.md +0 -134
- empathy_os/workflows/documentation_orchestrator.py +0 -1205
- empathy_os/workflows/history.py +0 -510
- empathy_os/workflows/keyboard_shortcuts/__init__.py +0 -39
- empathy_os/workflows/keyboard_shortcuts/generators.py +0 -391
- empathy_os/workflows/keyboard_shortcuts/parsers.py +0 -416
- empathy_os/workflows/keyboard_shortcuts/prompts.py +0 -295
- empathy_os/workflows/keyboard_shortcuts/schema.py +0 -193
- empathy_os/workflows/keyboard_shortcuts/workflow.py +0 -509
- empathy_os/workflows/llm_base.py +0 -363
- empathy_os/workflows/manage_docs.py +0 -87
- empathy_os/workflows/manage_docs_README.md +0 -134
- empathy_os/workflows/manage_documentation.py +0 -821
- empathy_os/workflows/new_sample_workflow1.py +0 -149
- empathy_os/workflows/new_sample_workflow1_README.md +0 -150
- empathy_os/workflows/orchestrated_health_check.py +0 -849
- empathy_os/workflows/orchestrated_release_prep.py +0 -600
- empathy_os/workflows/output.py +0 -413
- empathy_os/workflows/perf_audit.py +0 -863
- empathy_os/workflows/pr_review.py +0 -762
- empathy_os/workflows/progress.py +0 -785
- empathy_os/workflows/progress_server.py +0 -322
- empathy_os/workflows/progressive/README 2.md +0 -454
- empathy_os/workflows/progressive/README.md +0 -454
- empathy_os/workflows/progressive/__init__.py +0 -82
- empathy_os/workflows/progressive/cli.py +0 -219
- empathy_os/workflows/progressive/core.py +0 -488
- empathy_os/workflows/progressive/orchestrator.py +0 -723
- empathy_os/workflows/progressive/reports.py +0 -520
- empathy_os/workflows/progressive/telemetry.py +0 -274
- empathy_os/workflows/progressive/test_gen.py +0 -495
- empathy_os/workflows/progressive/workflow.py +0 -589
- empathy_os/workflows/refactor_plan.py +0 -694
- empathy_os/workflows/release_prep.py +0 -895
- empathy_os/workflows/release_prep_crew.py +0 -969
- empathy_os/workflows/research_synthesis.py +0 -404
- empathy_os/workflows/routing.py +0 -168
- empathy_os/workflows/secure_release.py +0 -593
- empathy_os/workflows/security_adapters.py +0 -297
- empathy_os/workflows/security_audit.py +0 -1329
- empathy_os/workflows/security_audit_phase3.py +0 -355
- empathy_os/workflows/seo_optimization.py +0 -633
- empathy_os/workflows/step_config.py +0 -234
- empathy_os/workflows/telemetry_mixin.py +0 -269
- empathy_os/workflows/test5.py +0 -125
- empathy_os/workflows/test5_README.md +0 -158
- empathy_os/workflows/test_coverage_boost_crew.py +0 -849
- empathy_os/workflows/test_gen/__init__.py +0 -52
- empathy_os/workflows/test_gen/ast_analyzer.py +0 -249
- empathy_os/workflows/test_gen/config.py +0 -88
- empathy_os/workflows/test_gen/data_models.py +0 -38
- empathy_os/workflows/test_gen/report_formatter.py +0 -289
- empathy_os/workflows/test_gen/test_templates.py +0 -381
- empathy_os/workflows/test_gen/workflow.py +0 -655
- empathy_os/workflows/test_gen.py +0 -54
- empathy_os/workflows/test_gen_behavioral.py +0 -477
- empathy_os/workflows/test_gen_parallel.py +0 -341
- empathy_os/workflows/test_lifecycle.py +0 -526
- empathy_os/workflows/test_maintenance.py +0 -627
- empathy_os/workflows/test_maintenance_cli.py +0 -590
- empathy_os/workflows/test_maintenance_crew.py +0 -840
- empathy_os/workflows/test_runner.py +0 -622
- empathy_os/workflows/tier_tracking.py +0 -531
- empathy_os/workflows/xml_enhanced_crew.py +0 -285
- empathy_software_plugin/SOFTWARE_PLUGIN_README.md +0 -57
- empathy_software_plugin/cli/__init__.py +0 -120
- empathy_software_plugin/cli/inspect.py +0 -362
- empathy_software_plugin/cli.py +0 -574
- empathy_software_plugin/plugin.py +0 -188
- workflow_scaffolding/__init__.py +0 -11
- workflow_scaffolding/__main__.py +0 -12
- workflow_scaffolding/cli.py +0 -206
- workflow_scaffolding/generator.py +0 -265
- {empathy_framework-5.3.0.dist-info → empathy_framework-5.4.0.dist-info}/WHEEL +0 -0
empathy_os/workflows/progress.py
DELETED
|
@@ -1,785 +0,0 @@
|
|
|
1
|
-
"""Progress Tracking System
|
|
2
|
-
|
|
3
|
-
Real-time progress tracking for workflow execution with WebSocket support.
|
|
4
|
-
Enables live UI updates during workflow runs.
|
|
5
|
-
|
|
6
|
-
Copyright 2025 Smart AI Memory, LLC
|
|
7
|
-
Licensed under Fair Source 0.9
|
|
8
|
-
"""
|
|
9
|
-
|
|
10
|
-
from __future__ import annotations
|
|
11
|
-
|
|
12
|
-
import asyncio
|
|
13
|
-
import json
|
|
14
|
-
import logging
|
|
15
|
-
import sys
|
|
16
|
-
import uuid
|
|
17
|
-
from collections.abc import Callable, Coroutine, Generator
|
|
18
|
-
from contextlib import contextmanager
|
|
19
|
-
from dataclasses import dataclass, field
|
|
20
|
-
from datetime import datetime
|
|
21
|
-
from enum import Enum
|
|
22
|
-
from typing import Any, Protocol
|
|
23
|
-
|
|
24
|
-
logger = logging.getLogger(__name__)
|
|
25
|
-
|
|
26
|
-
# Rich imports with fallback
|
|
27
|
-
try:
|
|
28
|
-
from rich.console import Console, Group
|
|
29
|
-
from rich.live import Live
|
|
30
|
-
from rich.panel import Panel
|
|
31
|
-
from rich.progress import BarColumn, Progress, SpinnerColumn, TaskID, TextColumn
|
|
32
|
-
from rich.table import Table
|
|
33
|
-
from rich.text import Text
|
|
34
|
-
|
|
35
|
-
RICH_AVAILABLE = True
|
|
36
|
-
except ImportError:
|
|
37
|
-
RICH_AVAILABLE = False
|
|
38
|
-
Console = None # type: ignore
|
|
39
|
-
Live = None # type: ignore
|
|
40
|
-
Panel = None # type: ignore
|
|
41
|
-
Progress = None # type: ignore
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
class ProgressStatus(Enum):
|
|
45
|
-
"""Status of a workflow or stage."""
|
|
46
|
-
|
|
47
|
-
PENDING = "pending"
|
|
48
|
-
RUNNING = "running"
|
|
49
|
-
COMPLETED = "completed"
|
|
50
|
-
FAILED = "failed"
|
|
51
|
-
SKIPPED = "skipped"
|
|
52
|
-
FALLBACK = "fallback" # Using fallback model
|
|
53
|
-
RETRYING = "retrying" # Retrying after error
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
@dataclass
|
|
57
|
-
class StageProgress:
|
|
58
|
-
"""Progress information for a single stage."""
|
|
59
|
-
|
|
60
|
-
name: str
|
|
61
|
-
status: ProgressStatus
|
|
62
|
-
tier: str = "capable"
|
|
63
|
-
model: str = ""
|
|
64
|
-
started_at: datetime | None = None
|
|
65
|
-
completed_at: datetime | None = None
|
|
66
|
-
duration_ms: int = 0
|
|
67
|
-
cost: float = 0.0
|
|
68
|
-
tokens_in: int = 0
|
|
69
|
-
tokens_out: int = 0
|
|
70
|
-
error: str | None = None
|
|
71
|
-
fallback_info: str | None = None
|
|
72
|
-
retry_count: int = 0
|
|
73
|
-
|
|
74
|
-
def to_dict(self) -> dict[str, Any]:
|
|
75
|
-
"""Convert to dictionary for JSON serialization."""
|
|
76
|
-
return {
|
|
77
|
-
"name": self.name,
|
|
78
|
-
"status": self.status.value,
|
|
79
|
-
"tier": self.tier,
|
|
80
|
-
"model": self.model,
|
|
81
|
-
"started_at": self.started_at.isoformat() if self.started_at else None,
|
|
82
|
-
"completed_at": self.completed_at.isoformat() if self.completed_at else None,
|
|
83
|
-
"duration_ms": self.duration_ms,
|
|
84
|
-
"cost": self.cost,
|
|
85
|
-
"tokens_in": self.tokens_in,
|
|
86
|
-
"tokens_out": self.tokens_out,
|
|
87
|
-
"error": self.error,
|
|
88
|
-
"fallback_info": self.fallback_info,
|
|
89
|
-
"retry_count": self.retry_count,
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
@dataclass
|
|
94
|
-
class ProgressUpdate:
|
|
95
|
-
"""A progress update to be broadcast."""
|
|
96
|
-
|
|
97
|
-
workflow: str
|
|
98
|
-
workflow_id: str
|
|
99
|
-
current_stage: str
|
|
100
|
-
stage_index: int
|
|
101
|
-
total_stages: int
|
|
102
|
-
status: ProgressStatus
|
|
103
|
-
message: str
|
|
104
|
-
cost_so_far: float = 0.0
|
|
105
|
-
tokens_so_far: int = 0
|
|
106
|
-
percent_complete: float = 0.0
|
|
107
|
-
estimated_remaining_ms: int | None = None
|
|
108
|
-
stages: list[StageProgress] = field(default_factory=list)
|
|
109
|
-
fallback_info: str | None = None
|
|
110
|
-
error: str | None = None
|
|
111
|
-
timestamp: datetime = field(default_factory=datetime.now)
|
|
112
|
-
|
|
113
|
-
def to_dict(self) -> dict[str, Any]:
|
|
114
|
-
"""Convert to dictionary for JSON serialization."""
|
|
115
|
-
return {
|
|
116
|
-
"workflow": self.workflow,
|
|
117
|
-
"workflow_id": self.workflow_id,
|
|
118
|
-
"current_stage": self.current_stage,
|
|
119
|
-
"stage_index": self.stage_index,
|
|
120
|
-
"total_stages": self.total_stages,
|
|
121
|
-
"status": self.status.value,
|
|
122
|
-
"message": self.message,
|
|
123
|
-
"cost_so_far": self.cost_so_far,
|
|
124
|
-
"tokens_so_far": self.tokens_so_far,
|
|
125
|
-
"percent_complete": self.percent_complete,
|
|
126
|
-
"estimated_remaining_ms": self.estimated_remaining_ms,
|
|
127
|
-
"stages": [s.to_dict() for s in self.stages],
|
|
128
|
-
"fallback_info": self.fallback_info,
|
|
129
|
-
"error": self.error,
|
|
130
|
-
"timestamp": self.timestamp.isoformat(),
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
def to_json(self) -> str:
|
|
134
|
-
"""Convert to JSON string."""
|
|
135
|
-
return json.dumps(self.to_dict())
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
# Type for progress callbacks
|
|
139
|
-
ProgressCallback = Callable[[ProgressUpdate], None]
|
|
140
|
-
AsyncProgressCallback = Callable[[ProgressUpdate], Coroutine[Any, Any, None]]
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
class ProgressTracker:
|
|
144
|
-
"""Tracks and broadcasts workflow progress.
|
|
145
|
-
|
|
146
|
-
Maintains state for all stages and emits updates to registered callbacks.
|
|
147
|
-
Supports both sync and async callbacks for flexibility.
|
|
148
|
-
"""
|
|
149
|
-
|
|
150
|
-
def __init__(
|
|
151
|
-
self,
|
|
152
|
-
workflow_name: str,
|
|
153
|
-
workflow_id: str,
|
|
154
|
-
stage_names: list[str],
|
|
155
|
-
):
|
|
156
|
-
self.workflow = workflow_name
|
|
157
|
-
self.workflow_id = workflow_id
|
|
158
|
-
self.stage_names = stage_names
|
|
159
|
-
# Optimization: Index map for O(1) stage lookup (vs O(n) .index() call)
|
|
160
|
-
self._stage_index_map: dict[str, int] = {name: i for i, name in enumerate(stage_names)}
|
|
161
|
-
self.current_index = 0
|
|
162
|
-
self.cost_accumulated = 0.0
|
|
163
|
-
self.tokens_accumulated = 0
|
|
164
|
-
self._started_at = datetime.now()
|
|
165
|
-
self._stage_start_times: dict[str, datetime] = {}
|
|
166
|
-
self._stage_durations: list[int] = []
|
|
167
|
-
|
|
168
|
-
# Initialize stages
|
|
169
|
-
self.stages: list[StageProgress] = [
|
|
170
|
-
StageProgress(name=name, status=ProgressStatus.PENDING) for name in stage_names
|
|
171
|
-
]
|
|
172
|
-
|
|
173
|
-
# Callbacks
|
|
174
|
-
self._callbacks: list[ProgressCallback] = []
|
|
175
|
-
self._async_callbacks: list[AsyncProgressCallback] = []
|
|
176
|
-
|
|
177
|
-
def add_callback(self, callback: ProgressCallback) -> None:
|
|
178
|
-
"""Add a synchronous progress callback."""
|
|
179
|
-
self._callbacks.append(callback)
|
|
180
|
-
|
|
181
|
-
def add_async_callback(self, callback: AsyncProgressCallback) -> None:
|
|
182
|
-
"""Add an asynchronous progress callback."""
|
|
183
|
-
self._async_callbacks.append(callback)
|
|
184
|
-
|
|
185
|
-
def remove_callback(self, callback: ProgressCallback) -> None:
|
|
186
|
-
"""Remove a synchronous callback."""
|
|
187
|
-
if callback in self._callbacks:
|
|
188
|
-
self._callbacks.remove(callback)
|
|
189
|
-
|
|
190
|
-
def start_workflow(self) -> None:
|
|
191
|
-
"""Mark workflow as started."""
|
|
192
|
-
self._started_at = datetime.now()
|
|
193
|
-
self._emit(ProgressStatus.RUNNING, f"Starting {self.workflow}...")
|
|
194
|
-
|
|
195
|
-
def start_stage(self, stage_name: str, tier: str = "capable", model: str = "") -> None:
|
|
196
|
-
"""Mark a stage as started."""
|
|
197
|
-
stage = self._get_stage(stage_name)
|
|
198
|
-
if stage:
|
|
199
|
-
stage.status = ProgressStatus.RUNNING
|
|
200
|
-
stage.started_at = datetime.now()
|
|
201
|
-
stage.tier = tier
|
|
202
|
-
stage.model = model
|
|
203
|
-
self._stage_start_times[stage_name] = stage.started_at
|
|
204
|
-
# Optimization: O(1) lookup instead of O(n) .index() call
|
|
205
|
-
self.current_index = self._stage_index_map.get(stage_name, 0)
|
|
206
|
-
|
|
207
|
-
self._emit(ProgressStatus.RUNNING, f"Running {stage_name}...")
|
|
208
|
-
|
|
209
|
-
def complete_stage(
|
|
210
|
-
self,
|
|
211
|
-
stage_name: str,
|
|
212
|
-
cost: float = 0.0,
|
|
213
|
-
tokens_in: int = 0,
|
|
214
|
-
tokens_out: int = 0,
|
|
215
|
-
) -> None:
|
|
216
|
-
"""Mark a stage as completed."""
|
|
217
|
-
stage = self._get_stage(stage_name)
|
|
218
|
-
if stage:
|
|
219
|
-
stage.status = ProgressStatus.COMPLETED
|
|
220
|
-
stage.completed_at = datetime.now()
|
|
221
|
-
stage.cost = cost
|
|
222
|
-
stage.tokens_in = tokens_in
|
|
223
|
-
stage.tokens_out = tokens_out
|
|
224
|
-
|
|
225
|
-
if stage.started_at:
|
|
226
|
-
duration_ms = int((stage.completed_at - stage.started_at).total_seconds() * 1000)
|
|
227
|
-
stage.duration_ms = duration_ms
|
|
228
|
-
self._stage_durations.append(duration_ms)
|
|
229
|
-
|
|
230
|
-
self.cost_accumulated += cost
|
|
231
|
-
self.tokens_accumulated += tokens_in + tokens_out
|
|
232
|
-
# Optimization: O(1) lookup instead of O(n) .index() call
|
|
233
|
-
self.current_index = self._stage_index_map.get(stage_name, 0) + 1
|
|
234
|
-
|
|
235
|
-
self._emit(ProgressStatus.COMPLETED, f"Completed {stage_name}")
|
|
236
|
-
|
|
237
|
-
def fail_stage(self, stage_name: str, error: str) -> None:
|
|
238
|
-
"""Mark a stage as failed."""
|
|
239
|
-
stage = self._get_stage(stage_name)
|
|
240
|
-
if stage:
|
|
241
|
-
stage.status = ProgressStatus.FAILED
|
|
242
|
-
stage.completed_at = datetime.now()
|
|
243
|
-
stage.error = error
|
|
244
|
-
|
|
245
|
-
if stage.started_at:
|
|
246
|
-
stage.duration_ms = int(
|
|
247
|
-
(stage.completed_at - stage.started_at).total_seconds() * 1000,
|
|
248
|
-
)
|
|
249
|
-
|
|
250
|
-
self._emit(ProgressStatus.FAILED, f"Failed: {stage_name}", error=error)
|
|
251
|
-
|
|
252
|
-
def skip_stage(self, stage_name: str, reason: str = "") -> None:
|
|
253
|
-
"""Mark a stage as skipped."""
|
|
254
|
-
stage = self._get_stage(stage_name)
|
|
255
|
-
if stage:
|
|
256
|
-
stage.status = ProgressStatus.SKIPPED
|
|
257
|
-
|
|
258
|
-
message = f"Skipped {stage_name}"
|
|
259
|
-
if reason:
|
|
260
|
-
message += f": {reason}"
|
|
261
|
-
self._emit(ProgressStatus.SKIPPED, message)
|
|
262
|
-
|
|
263
|
-
def update_tier(self, stage_name: str, new_tier: str, reason: str = "") -> None:
|
|
264
|
-
"""Update the tier for a stage during tier fallback.
|
|
265
|
-
|
|
266
|
-
Args:
|
|
267
|
-
stage_name: Name of the stage
|
|
268
|
-
new_tier: New tier being attempted (CHEAP, CAPABLE, PREMIUM)
|
|
269
|
-
reason: Optional reason for tier change (e.g., "validation_failed")
|
|
270
|
-
|
|
271
|
-
"""
|
|
272
|
-
stage = self._get_stage(stage_name)
|
|
273
|
-
if stage:
|
|
274
|
-
old_tier = stage.tier
|
|
275
|
-
stage.tier = new_tier
|
|
276
|
-
|
|
277
|
-
message = f"Tier upgrade: {stage_name} [{old_tier.upper()} → {new_tier.upper()}]"
|
|
278
|
-
if reason:
|
|
279
|
-
message += f" ({reason})"
|
|
280
|
-
|
|
281
|
-
self._emit(ProgressStatus.RUNNING, message)
|
|
282
|
-
|
|
283
|
-
def fallback_occurred(
|
|
284
|
-
self,
|
|
285
|
-
stage_name: str,
|
|
286
|
-
original_model: str,
|
|
287
|
-
fallback_model: str,
|
|
288
|
-
reason: str,
|
|
289
|
-
) -> None:
|
|
290
|
-
"""Record that a fallback occurred."""
|
|
291
|
-
stage = self._get_stage(stage_name)
|
|
292
|
-
fallback_info = f"{original_model} → {fallback_model} ({reason})"
|
|
293
|
-
|
|
294
|
-
if stage:
|
|
295
|
-
stage.status = ProgressStatus.FALLBACK
|
|
296
|
-
stage.fallback_info = fallback_info
|
|
297
|
-
|
|
298
|
-
self._emit(
|
|
299
|
-
ProgressStatus.FALLBACK,
|
|
300
|
-
f"Falling back from {original_model} to {fallback_model}",
|
|
301
|
-
fallback_info=fallback_info,
|
|
302
|
-
)
|
|
303
|
-
|
|
304
|
-
def retry_occurred(self, stage_name: str, attempt: int, max_attempts: int) -> None:
|
|
305
|
-
"""Record that a retry is occurring."""
|
|
306
|
-
stage = self._get_stage(stage_name)
|
|
307
|
-
if stage:
|
|
308
|
-
stage.status = ProgressStatus.RETRYING
|
|
309
|
-
stage.retry_count = attempt
|
|
310
|
-
|
|
311
|
-
self._emit(
|
|
312
|
-
ProgressStatus.RETRYING,
|
|
313
|
-
f"Retrying {stage_name} (attempt {attempt}/{max_attempts})",
|
|
314
|
-
)
|
|
315
|
-
|
|
316
|
-
def complete_workflow(self) -> None:
|
|
317
|
-
"""Mark workflow as completed."""
|
|
318
|
-
self._emit(
|
|
319
|
-
ProgressStatus.COMPLETED,
|
|
320
|
-
f"Workflow {self.workflow} completed",
|
|
321
|
-
)
|
|
322
|
-
|
|
323
|
-
def fail_workflow(self, error: str) -> None:
|
|
324
|
-
"""Mark workflow as failed."""
|
|
325
|
-
self._emit(
|
|
326
|
-
ProgressStatus.FAILED,
|
|
327
|
-
f"Workflow {self.workflow} failed",
|
|
328
|
-
error=error,
|
|
329
|
-
)
|
|
330
|
-
|
|
331
|
-
def _get_stage(self, stage_name: str) -> StageProgress | None:
|
|
332
|
-
"""Get stage by name."""
|
|
333
|
-
for stage in self.stages:
|
|
334
|
-
if stage.name == stage_name:
|
|
335
|
-
return stage
|
|
336
|
-
return None
|
|
337
|
-
|
|
338
|
-
def _calculate_percent_complete(self) -> float:
|
|
339
|
-
"""Calculate completion percentage."""
|
|
340
|
-
completed = sum(1 for s in self.stages if s.status == ProgressStatus.COMPLETED)
|
|
341
|
-
return (completed / len(self.stages)) * 100 if self.stages else 0.0
|
|
342
|
-
|
|
343
|
-
def _estimate_remaining_ms(self) -> int | None:
|
|
344
|
-
"""Estimate remaining time based on average stage duration."""
|
|
345
|
-
if not self._stage_durations:
|
|
346
|
-
return None
|
|
347
|
-
|
|
348
|
-
avg_duration = sum(self._stage_durations) / len(self._stage_durations)
|
|
349
|
-
remaining_stages = len(self.stages) - self.current_index
|
|
350
|
-
return int(avg_duration * remaining_stages)
|
|
351
|
-
|
|
352
|
-
def _emit(
|
|
353
|
-
self,
|
|
354
|
-
status: ProgressStatus,
|
|
355
|
-
message: str,
|
|
356
|
-
fallback_info: str | None = None,
|
|
357
|
-
error: str | None = None,
|
|
358
|
-
) -> None:
|
|
359
|
-
"""Emit a progress update to all callbacks."""
|
|
360
|
-
current_stage = (
|
|
361
|
-
self.stage_names[min(self.current_index, len(self.stage_names) - 1)]
|
|
362
|
-
if self.stage_names
|
|
363
|
-
else ""
|
|
364
|
-
)
|
|
365
|
-
|
|
366
|
-
update = ProgressUpdate(
|
|
367
|
-
workflow=self.workflow,
|
|
368
|
-
workflow_id=self.workflow_id,
|
|
369
|
-
current_stage=current_stage,
|
|
370
|
-
stage_index=self.current_index,
|
|
371
|
-
total_stages=len(self.stages),
|
|
372
|
-
status=status,
|
|
373
|
-
message=message,
|
|
374
|
-
cost_so_far=self.cost_accumulated,
|
|
375
|
-
tokens_so_far=self.tokens_accumulated,
|
|
376
|
-
percent_complete=self._calculate_percent_complete(),
|
|
377
|
-
estimated_remaining_ms=self._estimate_remaining_ms(),
|
|
378
|
-
stages=list(self.stages),
|
|
379
|
-
fallback_info=fallback_info,
|
|
380
|
-
error=error,
|
|
381
|
-
)
|
|
382
|
-
|
|
383
|
-
# Call sync callbacks
|
|
384
|
-
for callback in self._callbacks:
|
|
385
|
-
try:
|
|
386
|
-
callback(update)
|
|
387
|
-
except Exception: # noqa: BLE001
|
|
388
|
-
# INTENTIONAL: Callbacks are optional - never fail workflow on callback error
|
|
389
|
-
logger.warning("Progress callback error", exc_info=True)
|
|
390
|
-
|
|
391
|
-
# Call async callbacks
|
|
392
|
-
for async_callback in self._async_callbacks:
|
|
393
|
-
try:
|
|
394
|
-
asyncio.create_task(async_callback(update))
|
|
395
|
-
except RuntimeError:
|
|
396
|
-
# No event loop running, skip async callbacks
|
|
397
|
-
pass
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
class ProgressReporter(Protocol):
|
|
401
|
-
"""Protocol for progress reporting implementations."""
|
|
402
|
-
|
|
403
|
-
def report(self, update: ProgressUpdate) -> None:
|
|
404
|
-
"""Report a progress update."""
|
|
405
|
-
...
|
|
406
|
-
|
|
407
|
-
async def report_async(self, update: ProgressUpdate) -> None:
|
|
408
|
-
"""Report a progress update asynchronously."""
|
|
409
|
-
...
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
class ConsoleProgressReporter:
|
|
413
|
-
"""Console-based progress reporter optimized for IDE environments.
|
|
414
|
-
|
|
415
|
-
Provides clear, readable progress output that works reliably in:
|
|
416
|
-
- VSCode integrated terminal
|
|
417
|
-
- VSCode output panel
|
|
418
|
-
- IDE debug consoles
|
|
419
|
-
- Standard terminals
|
|
420
|
-
|
|
421
|
-
Uses Unicode symbols that render correctly in most environments.
|
|
422
|
-
"""
|
|
423
|
-
|
|
424
|
-
def __init__(self, verbose: bool = False, show_tokens: bool = False):
|
|
425
|
-
"""Initialize console progress reporter.
|
|
426
|
-
|
|
427
|
-
Args:
|
|
428
|
-
verbose: Show additional details (fallback info, errors)
|
|
429
|
-
show_tokens: Include token counts in output
|
|
430
|
-
"""
|
|
431
|
-
self.verbose = verbose
|
|
432
|
-
self.show_tokens = show_tokens
|
|
433
|
-
self._start_time: datetime | None = None
|
|
434
|
-
self._stage_times: dict[str, int] = {}
|
|
435
|
-
|
|
436
|
-
def report(self, update: ProgressUpdate) -> None:
|
|
437
|
-
"""Print progress to console.
|
|
438
|
-
|
|
439
|
-
Args:
|
|
440
|
-
update: Progress update from the tracker
|
|
441
|
-
"""
|
|
442
|
-
# Track start time for elapsed calculation
|
|
443
|
-
if self._start_time is None:
|
|
444
|
-
self._start_time = datetime.now()
|
|
445
|
-
|
|
446
|
-
percent = f"{update.percent_complete:3.0f}%"
|
|
447
|
-
cost = f"${update.cost_so_far:.4f}"
|
|
448
|
-
|
|
449
|
-
# Status icons that work in most environments
|
|
450
|
-
status_icon = {
|
|
451
|
-
ProgressStatus.PENDING: "○",
|
|
452
|
-
ProgressStatus.RUNNING: "►",
|
|
453
|
-
ProgressStatus.COMPLETED: "✓",
|
|
454
|
-
ProgressStatus.FAILED: "✗",
|
|
455
|
-
ProgressStatus.SKIPPED: "–",
|
|
456
|
-
ProgressStatus.FALLBACK: "↻",
|
|
457
|
-
ProgressStatus.RETRYING: "↻",
|
|
458
|
-
}.get(update.status, "?")
|
|
459
|
-
|
|
460
|
-
# Get current tier from running stage
|
|
461
|
-
tier_info = ""
|
|
462
|
-
model_info = ""
|
|
463
|
-
if update.current_stage and update.stages:
|
|
464
|
-
for stage in update.stages:
|
|
465
|
-
if stage.name == update.current_stage:
|
|
466
|
-
if stage.status == ProgressStatus.RUNNING:
|
|
467
|
-
tier_info = f" [{stage.tier.upper()}]"
|
|
468
|
-
if stage.model:
|
|
469
|
-
model_info = f" ({stage.model})"
|
|
470
|
-
# Track stage duration
|
|
471
|
-
if stage.duration_ms > 0:
|
|
472
|
-
self._stage_times[stage.name] = stage.duration_ms
|
|
473
|
-
break
|
|
474
|
-
|
|
475
|
-
# Build output line
|
|
476
|
-
elapsed = ""
|
|
477
|
-
if self._start_time:
|
|
478
|
-
elapsed_sec = (datetime.now() - self._start_time).total_seconds()
|
|
479
|
-
if elapsed_sec >= 1:
|
|
480
|
-
elapsed = f" [{elapsed_sec:.1f}s]"
|
|
481
|
-
|
|
482
|
-
tokens_str = ""
|
|
483
|
-
if self.show_tokens and update.tokens_so_far > 0:
|
|
484
|
-
tokens_str = f" | {update.tokens_so_far:,} tokens"
|
|
485
|
-
|
|
486
|
-
# Format: [100%] ✓ Completed optimize [PREMIUM] ($0.0279) [12.3s]
|
|
487
|
-
output = f"[{percent}] {status_icon} {update.message}{tier_info} ({cost}{tokens_str}){elapsed}"
|
|
488
|
-
print(output)
|
|
489
|
-
|
|
490
|
-
# Verbose output
|
|
491
|
-
if self.verbose:
|
|
492
|
-
if update.fallback_info:
|
|
493
|
-
print(f" ↳ Fallback: {update.fallback_info}")
|
|
494
|
-
if update.error:
|
|
495
|
-
print(f" ↳ Error: {update.error}")
|
|
496
|
-
|
|
497
|
-
# Print summary only on final workflow completion (not stage completion)
|
|
498
|
-
if update.status == ProgressStatus.COMPLETED and "workflow" in update.message.lower():
|
|
499
|
-
self._print_summary(update)
|
|
500
|
-
|
|
501
|
-
def _print_summary(self, update: ProgressUpdate) -> None:
|
|
502
|
-
"""Print workflow completion summary."""
|
|
503
|
-
if not self._stage_times:
|
|
504
|
-
return
|
|
505
|
-
|
|
506
|
-
print("")
|
|
507
|
-
print("─" * 50)
|
|
508
|
-
print("Stage Summary:")
|
|
509
|
-
for stage in update.stages:
|
|
510
|
-
if stage.status == ProgressStatus.COMPLETED:
|
|
511
|
-
duration_ms = stage.duration_ms or self._stage_times.get(stage.name, 0)
|
|
512
|
-
duration_str = f"{duration_ms}ms" if duration_ms < 1000 else f"{duration_ms/1000:.1f}s"
|
|
513
|
-
cost_str = f"${stage.cost:.4f}" if stage.cost > 0 else "—"
|
|
514
|
-
print(f" {stage.name}: {duration_str} | {cost_str}")
|
|
515
|
-
elif stage.status == ProgressStatus.SKIPPED:
|
|
516
|
-
print(f" {stage.name}: skipped")
|
|
517
|
-
print("─" * 50)
|
|
518
|
-
|
|
519
|
-
async def report_async(self, update: ProgressUpdate) -> None:
|
|
520
|
-
"""Async version just calls sync."""
|
|
521
|
-
self.report(update)
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
class JsonLinesProgressReporter:
|
|
525
|
-
"""JSON Lines progress reporter for machine parsing."""
|
|
526
|
-
|
|
527
|
-
def __init__(self, output_file: str | None = None):
|
|
528
|
-
self.output_file = output_file
|
|
529
|
-
|
|
530
|
-
def report(self, update: ProgressUpdate) -> None:
|
|
531
|
-
"""Output progress as JSON line."""
|
|
532
|
-
json_line = update.to_json()
|
|
533
|
-
|
|
534
|
-
if self.output_file:
|
|
535
|
-
with open(self.output_file, "a") as f:
|
|
536
|
-
f.write(json_line + "\n")
|
|
537
|
-
else:
|
|
538
|
-
print(json_line)
|
|
539
|
-
|
|
540
|
-
async def report_async(self, update: ProgressUpdate) -> None:
|
|
541
|
-
"""Async version just calls sync."""
|
|
542
|
-
self.report(update)
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
def create_progress_tracker(
|
|
546
|
-
workflow_name: str,
|
|
547
|
-
stage_names: list[str],
|
|
548
|
-
reporter: ProgressReporter | None = None,
|
|
549
|
-
) -> ProgressTracker:
|
|
550
|
-
"""Factory function to create a progress tracker with optional reporter.
|
|
551
|
-
|
|
552
|
-
Args:
|
|
553
|
-
workflow_name: Name of the workflow
|
|
554
|
-
stage_names: List of stage names in order
|
|
555
|
-
reporter: Optional progress reporter
|
|
556
|
-
|
|
557
|
-
Returns:
|
|
558
|
-
Configured ProgressTracker instance
|
|
559
|
-
|
|
560
|
-
"""
|
|
561
|
-
tracker = ProgressTracker(
|
|
562
|
-
workflow_name=workflow_name,
|
|
563
|
-
workflow_id=uuid.uuid4().hex[:12],
|
|
564
|
-
stage_names=stage_names,
|
|
565
|
-
)
|
|
566
|
-
|
|
567
|
-
if reporter:
|
|
568
|
-
tracker.add_callback(reporter.report)
|
|
569
|
-
|
|
570
|
-
return tracker
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
class RichProgressReporter:
|
|
574
|
-
"""Rich-based live progress display with spinner, progress bar, and metrics.
|
|
575
|
-
|
|
576
|
-
Provides real-time visual feedback during workflow execution:
|
|
577
|
-
- Progress bar showing stage completion (1/3, 2/3, etc.)
|
|
578
|
-
- Spinner during active LLM API calls
|
|
579
|
-
- Real-time cost and token display
|
|
580
|
-
- In-place updates (no terminal scrolling)
|
|
581
|
-
|
|
582
|
-
Requires Rich library. Falls back gracefully if unavailable.
|
|
583
|
-
"""
|
|
584
|
-
|
|
585
|
-
def __init__(self, workflow_name: str, stage_names: list[str]) -> None:
|
|
586
|
-
"""Initialize the Rich progress reporter.
|
|
587
|
-
|
|
588
|
-
Args:
|
|
589
|
-
workflow_name: Name of the workflow for display
|
|
590
|
-
stage_names: List of stage names for progress tracking
|
|
591
|
-
"""
|
|
592
|
-
if not RICH_AVAILABLE:
|
|
593
|
-
raise RuntimeError(
|
|
594
|
-
"Rich library required for RichProgressReporter. "
|
|
595
|
-
"Install with: pip install rich"
|
|
596
|
-
)
|
|
597
|
-
|
|
598
|
-
self.workflow_name = workflow_name
|
|
599
|
-
self.stage_names = stage_names
|
|
600
|
-
self.console = Console()
|
|
601
|
-
self._live: Live | None = None
|
|
602
|
-
self._progress: Progress | None = None
|
|
603
|
-
self._task_id: TaskID | None = None
|
|
604
|
-
self._current_stage = ""
|
|
605
|
-
self._cost = 0.0
|
|
606
|
-
self._tokens = 0
|
|
607
|
-
self._status = ProgressStatus.PENDING
|
|
608
|
-
|
|
609
|
-
def start(self) -> None:
|
|
610
|
-
"""Start the live progress display."""
|
|
611
|
-
if not RICH_AVAILABLE or Progress is None or Live is None:
|
|
612
|
-
return
|
|
613
|
-
|
|
614
|
-
self._progress = Progress(
|
|
615
|
-
SpinnerColumn(),
|
|
616
|
-
TextColumn("[bold blue]{task.description}"),
|
|
617
|
-
BarColumn(bar_width=30),
|
|
618
|
-
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
|
619
|
-
TextColumn("({task.completed}/{task.total})"),
|
|
620
|
-
console=self.console,
|
|
621
|
-
transient=False,
|
|
622
|
-
)
|
|
623
|
-
|
|
624
|
-
self._task_id = self._progress.add_task(
|
|
625
|
-
self.workflow_name,
|
|
626
|
-
total=len(self.stage_names),
|
|
627
|
-
)
|
|
628
|
-
|
|
629
|
-
self._live = Live(
|
|
630
|
-
self._create_display(),
|
|
631
|
-
console=self.console,
|
|
632
|
-
refresh_per_second=4,
|
|
633
|
-
transient=False,
|
|
634
|
-
)
|
|
635
|
-
self._live.start()
|
|
636
|
-
|
|
637
|
-
def stop(self) -> None:
|
|
638
|
-
"""Stop the live progress display."""
|
|
639
|
-
if self._live:
|
|
640
|
-
self._live.stop()
|
|
641
|
-
self._live = None
|
|
642
|
-
|
|
643
|
-
def report(self, update: ProgressUpdate) -> None:
|
|
644
|
-
"""Handle a progress update.
|
|
645
|
-
|
|
646
|
-
Args:
|
|
647
|
-
update: Progress update from the tracker
|
|
648
|
-
"""
|
|
649
|
-
self._current_stage = update.current_stage
|
|
650
|
-
self._cost = update.cost_so_far
|
|
651
|
-
self._tokens = update.tokens_so_far
|
|
652
|
-
self._status = update.status
|
|
653
|
-
|
|
654
|
-
# Update progress bar
|
|
655
|
-
if self._progress is not None and self._task_id is not None:
|
|
656
|
-
completed = sum(
|
|
657
|
-
1 for s in update.stages if s.status == ProgressStatus.COMPLETED
|
|
658
|
-
)
|
|
659
|
-
self._progress.update(
|
|
660
|
-
self._task_id,
|
|
661
|
-
completed=completed,
|
|
662
|
-
description=f"{self.workflow_name}: {update.current_stage}",
|
|
663
|
-
)
|
|
664
|
-
|
|
665
|
-
# Refresh display
|
|
666
|
-
if self._live:
|
|
667
|
-
self._live.update(self._create_display())
|
|
668
|
-
|
|
669
|
-
async def report_async(self, update: ProgressUpdate) -> None:
|
|
670
|
-
"""Async version of report."""
|
|
671
|
-
self.report(update)
|
|
672
|
-
|
|
673
|
-
def _create_display(self) -> Panel:
|
|
674
|
-
"""Create the Rich display panel.
|
|
675
|
-
|
|
676
|
-
Returns:
|
|
677
|
-
Rich Panel containing progress information
|
|
678
|
-
"""
|
|
679
|
-
if not RICH_AVAILABLE or Panel is None or Table is None:
|
|
680
|
-
raise RuntimeError(
|
|
681
|
-
"Rich library not available. "
|
|
682
|
-
"Install with: pip install rich"
|
|
683
|
-
)
|
|
684
|
-
|
|
685
|
-
# Build metrics table
|
|
686
|
-
metrics = Table(show_header=False, box=None, padding=(0, 2))
|
|
687
|
-
metrics.add_column("Label", style="dim")
|
|
688
|
-
metrics.add_column("Value", style="bold")
|
|
689
|
-
|
|
690
|
-
metrics.add_row("Cost:", f"${self._cost:.4f}")
|
|
691
|
-
metrics.add_row("Tokens:", f"{self._tokens:,}")
|
|
692
|
-
metrics.add_row("Stage:", self._current_stage or "Starting...")
|
|
693
|
-
|
|
694
|
-
# Status indicator
|
|
695
|
-
status_style = {
|
|
696
|
-
ProgressStatus.PENDING: "dim",
|
|
697
|
-
ProgressStatus.RUNNING: "blue",
|
|
698
|
-
ProgressStatus.COMPLETED: "green",
|
|
699
|
-
ProgressStatus.FAILED: "red",
|
|
700
|
-
ProgressStatus.FALLBACK: "yellow",
|
|
701
|
-
ProgressStatus.RETRYING: "yellow",
|
|
702
|
-
}.get(self._status, "white")
|
|
703
|
-
|
|
704
|
-
status_text = Text(self._status.value.upper(), style=status_style)
|
|
705
|
-
|
|
706
|
-
# Combine into panel
|
|
707
|
-
if self._progress is not None:
|
|
708
|
-
content = Group(self._progress, metrics)
|
|
709
|
-
else:
|
|
710
|
-
content = metrics
|
|
711
|
-
|
|
712
|
-
return Panel(
|
|
713
|
-
content,
|
|
714
|
-
title=f"[bold]{self.workflow_name}[/bold]",
|
|
715
|
-
subtitle=status_text,
|
|
716
|
-
border_style=status_style,
|
|
717
|
-
)
|
|
718
|
-
|
|
719
|
-
def __enter__(self) -> RichProgressReporter:
|
|
720
|
-
"""Context manager entry."""
|
|
721
|
-
self.start()
|
|
722
|
-
return self
|
|
723
|
-
|
|
724
|
-
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
|
|
725
|
-
"""Context manager exit."""
|
|
726
|
-
self.stop()
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
@contextmanager
|
|
730
|
-
def live_progress(
|
|
731
|
-
workflow_name: str,
|
|
732
|
-
stage_names: list[str],
|
|
733
|
-
console: Console | None = None,
|
|
734
|
-
) -> Generator[tuple[ProgressTracker, RichProgressReporter | None], None, None]:
|
|
735
|
-
"""Context manager for live progress display during workflow execution.
|
|
736
|
-
|
|
737
|
-
Provides a ProgressTracker with optional Rich-based live display.
|
|
738
|
-
Falls back gracefully when Rich is unavailable or output is not a TTY.
|
|
739
|
-
|
|
740
|
-
Args:
|
|
741
|
-
workflow_name: Name of the workflow
|
|
742
|
-
stage_names: List of stage names in order
|
|
743
|
-
console: Optional Rich Console (creates new one if not provided)
|
|
744
|
-
|
|
745
|
-
Yields:
|
|
746
|
-
Tuple of (ProgressTracker, RichProgressReporter or None)
|
|
747
|
-
|
|
748
|
-
Example:
|
|
749
|
-
with live_progress("Code Review", ["analyze", "review", "summarize"]) as (tracker, _):
|
|
750
|
-
tracker.start_workflow()
|
|
751
|
-
for stage in stages:
|
|
752
|
-
tracker.start_stage(stage)
|
|
753
|
-
# ... do work ...
|
|
754
|
-
tracker.complete_stage(stage, cost=0.01, tokens_in=100, tokens_out=50)
|
|
755
|
-
tracker.complete_workflow()
|
|
756
|
-
"""
|
|
757
|
-
tracker = ProgressTracker(
|
|
758
|
-
workflow_name=workflow_name,
|
|
759
|
-
workflow_id=uuid.uuid4().hex[:12],
|
|
760
|
-
stage_names=stage_names,
|
|
761
|
-
)
|
|
762
|
-
|
|
763
|
-
reporter: RichProgressReporter | None = None
|
|
764
|
-
|
|
765
|
-
# Use Rich if available and output is a TTY
|
|
766
|
-
if RICH_AVAILABLE and sys.stdout.isatty():
|
|
767
|
-
try:
|
|
768
|
-
reporter = RichProgressReporter(workflow_name, stage_names)
|
|
769
|
-
tracker.add_callback(reporter.report)
|
|
770
|
-
reporter.start()
|
|
771
|
-
except Exception: # noqa: BLE001
|
|
772
|
-
# INTENTIONAL: Rich display is optional - fall back to console output
|
|
773
|
-
reporter = None
|
|
774
|
-
simple_reporter = ConsoleProgressReporter(verbose=False)
|
|
775
|
-
tracker.add_callback(simple_reporter.report)
|
|
776
|
-
else:
|
|
777
|
-
# No Rich or not a TTY - use simple console reporter
|
|
778
|
-
simple_reporter = ConsoleProgressReporter(verbose=False)
|
|
779
|
-
tracker.add_callback(simple_reporter.report)
|
|
780
|
-
|
|
781
|
-
try:
|
|
782
|
-
yield tracker, reporter
|
|
783
|
-
finally:
|
|
784
|
-
if reporter:
|
|
785
|
-
reporter.stop()
|