tapps-agents 3.5.40__py3-none-any.whl → 3.6.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.
- tapps_agents/__init__.py +2 -2
- tapps_agents/agents/__init__.py +22 -22
- tapps_agents/agents/analyst/__init__.py +5 -5
- tapps_agents/agents/architect/__init__.py +5 -5
- tapps_agents/agents/architect/agent.py +1033 -1033
- tapps_agents/agents/architect/pattern_detector.py +75 -75
- tapps_agents/agents/cleanup/__init__.py +7 -7
- tapps_agents/agents/cleanup/agent.py +445 -445
- tapps_agents/agents/debugger/__init__.py +7 -7
- tapps_agents/agents/debugger/agent.py +310 -310
- tapps_agents/agents/debugger/error_analyzer.py +437 -437
- tapps_agents/agents/designer/__init__.py +5 -5
- tapps_agents/agents/designer/agent.py +786 -786
- tapps_agents/agents/designer/visual_designer.py +638 -638
- tapps_agents/agents/documenter/__init__.py +7 -7
- tapps_agents/agents/documenter/agent.py +531 -531
- tapps_agents/agents/documenter/doc_generator.py +472 -472
- tapps_agents/agents/documenter/doc_validator.py +393 -393
- tapps_agents/agents/documenter/framework_doc_updater.py +493 -493
- tapps_agents/agents/enhancer/__init__.py +7 -7
- tapps_agents/agents/evaluator/__init__.py +7 -7
- tapps_agents/agents/evaluator/agent.py +443 -443
- tapps_agents/agents/evaluator/priority_evaluator.py +641 -641
- tapps_agents/agents/evaluator/quality_analyzer.py +147 -147
- tapps_agents/agents/evaluator/report_generator.py +344 -344
- tapps_agents/agents/evaluator/usage_analyzer.py +192 -192
- tapps_agents/agents/evaluator/workflow_analyzer.py +189 -189
- tapps_agents/agents/implementer/__init__.py +7 -7
- tapps_agents/agents/implementer/agent.py +798 -798
- tapps_agents/agents/implementer/auto_fix.py +1119 -1119
- tapps_agents/agents/implementer/code_generator.py +73 -73
- tapps_agents/agents/improver/__init__.py +1 -1
- tapps_agents/agents/improver/agent.py +753 -753
- tapps_agents/agents/ops/__init__.py +1 -1
- tapps_agents/agents/ops/agent.py +619 -619
- tapps_agents/agents/ops/dependency_analyzer.py +600 -600
- tapps_agents/agents/orchestrator/__init__.py +5 -5
- tapps_agents/agents/orchestrator/agent.py +522 -522
- tapps_agents/agents/planner/__init__.py +7 -7
- tapps_agents/agents/planner/agent.py +1127 -1127
- tapps_agents/agents/reviewer/__init__.py +24 -24
- tapps_agents/agents/reviewer/agent.py +3513 -3513
- tapps_agents/agents/reviewer/aggregator.py +213 -213
- tapps_agents/agents/reviewer/batch_review.py +448 -448
- tapps_agents/agents/reviewer/cache.py +443 -443
- tapps_agents/agents/reviewer/context7_enhancer.py +630 -630
- tapps_agents/agents/reviewer/context_detector.py +203 -203
- tapps_agents/agents/reviewer/docker_compose_validator.py +158 -158
- tapps_agents/agents/reviewer/dockerfile_validator.py +176 -176
- tapps_agents/agents/reviewer/error_handling.py +126 -126
- tapps_agents/agents/reviewer/feedback_generator.py +490 -490
- tapps_agents/agents/reviewer/influxdb_validator.py +316 -316
- tapps_agents/agents/reviewer/issue_tracking.py +169 -169
- tapps_agents/agents/reviewer/library_detector.py +295 -295
- tapps_agents/agents/reviewer/library_patterns.py +268 -268
- tapps_agents/agents/reviewer/maintainability_scorer.py +593 -593
- tapps_agents/agents/reviewer/metric_strategies.py +276 -276
- tapps_agents/agents/reviewer/mqtt_validator.py +160 -160
- tapps_agents/agents/reviewer/output_enhancer.py +105 -105
- tapps_agents/agents/reviewer/pattern_detector.py +241 -241
- tapps_agents/agents/reviewer/performance_scorer.py +357 -357
- tapps_agents/agents/reviewer/phased_review.py +516 -516
- tapps_agents/agents/reviewer/progressive_review.py +435 -435
- tapps_agents/agents/reviewer/react_scorer.py +331 -331
- tapps_agents/agents/reviewer/score_constants.py +228 -228
- tapps_agents/agents/reviewer/score_validator.py +507 -507
- tapps_agents/agents/reviewer/scorer_registry.py +373 -373
- tapps_agents/agents/reviewer/scoring.py +1566 -1566
- tapps_agents/agents/reviewer/service_discovery.py +534 -534
- tapps_agents/agents/reviewer/tools/__init__.py +41 -41
- tapps_agents/agents/reviewer/tools/parallel_executor.py +581 -581
- tapps_agents/agents/reviewer/tools/ruff_grouping.py +250 -250
- tapps_agents/agents/reviewer/tools/scoped_mypy.py +284 -284
- tapps_agents/agents/reviewer/typescript_scorer.py +1142 -1142
- tapps_agents/agents/reviewer/validation.py +208 -208
- tapps_agents/agents/reviewer/websocket_validator.py +132 -132
- tapps_agents/agents/tester/__init__.py +7 -7
- tapps_agents/agents/tester/accessibility_auditor.py +309 -309
- tapps_agents/agents/tester/agent.py +1080 -1080
- tapps_agents/agents/tester/batch_generator.py +54 -54
- tapps_agents/agents/tester/context_learner.py +51 -51
- tapps_agents/agents/tester/coverage_analyzer.py +386 -386
- tapps_agents/agents/tester/coverage_test_generator.py +290 -290
- tapps_agents/agents/tester/debug_enhancer.py +238 -238
- tapps_agents/agents/tester/device_emulator.py +241 -241
- tapps_agents/agents/tester/integration_generator.py +62 -62
- tapps_agents/agents/tester/network_recorder.py +300 -300
- tapps_agents/agents/tester/performance_monitor.py +320 -320
- tapps_agents/agents/tester/test_fixer.py +316 -316
- tapps_agents/agents/tester/test_generator.py +632 -632
- tapps_agents/agents/tester/trace_manager.py +234 -234
- tapps_agents/agents/tester/visual_regression.py +291 -291
- tapps_agents/analysis/pattern_detector.py +36 -36
- tapps_agents/beads/hydration.py +213 -213
- tapps_agents/beads/parse.py +32 -32
- tapps_agents/beads/specs.py +206 -206
- tapps_agents/cli/__init__.py +9 -9
- tapps_agents/cli/__main__.py +8 -8
- tapps_agents/cli/base.py +478 -478
- tapps_agents/cli/command_classifier.py +72 -72
- tapps_agents/cli/commands/__init__.py +2 -2
- tapps_agents/cli/commands/analyst.py +173 -173
- tapps_agents/cli/commands/architect.py +109 -109
- tapps_agents/cli/commands/cleanup_agent.py +92 -92
- tapps_agents/cli/commands/common.py +126 -126
- tapps_agents/cli/commands/debugger.py +90 -90
- tapps_agents/cli/commands/designer.py +112 -112
- tapps_agents/cli/commands/documenter.py +136 -136
- tapps_agents/cli/commands/enhancer.py +110 -110
- tapps_agents/cli/commands/evaluator.py +255 -255
- tapps_agents/cli/commands/health.py +665 -665
- tapps_agents/cli/commands/implementer.py +301 -301
- tapps_agents/cli/commands/improver.py +91 -91
- tapps_agents/cli/commands/knowledge.py +111 -111
- tapps_agents/cli/commands/learning.py +172 -172
- tapps_agents/cli/commands/observability.py +283 -283
- tapps_agents/cli/commands/ops.py +135 -135
- tapps_agents/cli/commands/orchestrator.py +116 -116
- tapps_agents/cli/commands/planner.py +237 -237
- tapps_agents/cli/commands/reviewer.py +1872 -1872
- tapps_agents/cli/commands/status.py +285 -285
- tapps_agents/cli/commands/task.py +227 -219
- tapps_agents/cli/commands/tester.py +191 -191
- tapps_agents/cli/commands/top_level.py +3586 -3586
- tapps_agents/cli/feedback.py +936 -936
- tapps_agents/cli/formatters.py +608 -608
- tapps_agents/cli/help/__init__.py +7 -7
- tapps_agents/cli/help/static_help.py +425 -425
- tapps_agents/cli/network_detection.py +110 -110
- tapps_agents/cli/output_compactor.py +274 -274
- tapps_agents/cli/parsers/__init__.py +2 -2
- tapps_agents/cli/parsers/analyst.py +186 -186
- tapps_agents/cli/parsers/architect.py +167 -167
- tapps_agents/cli/parsers/cleanup_agent.py +228 -228
- tapps_agents/cli/parsers/debugger.py +116 -116
- tapps_agents/cli/parsers/designer.py +182 -182
- tapps_agents/cli/parsers/documenter.py +134 -134
- tapps_agents/cli/parsers/enhancer.py +113 -113
- tapps_agents/cli/parsers/evaluator.py +213 -213
- tapps_agents/cli/parsers/implementer.py +168 -168
- tapps_agents/cli/parsers/improver.py +132 -132
- tapps_agents/cli/parsers/ops.py +159 -159
- tapps_agents/cli/parsers/orchestrator.py +98 -98
- tapps_agents/cli/parsers/planner.py +145 -145
- tapps_agents/cli/parsers/reviewer.py +462 -462
- tapps_agents/cli/parsers/tester.py +124 -124
- tapps_agents/cli/progress_heartbeat.py +254 -254
- tapps_agents/cli/streaming_progress.py +336 -336
- tapps_agents/cli/utils/__init__.py +6 -6
- tapps_agents/cli/utils/agent_lifecycle.py +48 -48
- tapps_agents/cli/utils/error_formatter.py +82 -82
- tapps_agents/cli/utils/error_recovery.py +188 -188
- tapps_agents/cli/utils/output_handler.py +59 -59
- tapps_agents/cli/utils/prompt_enhancer.py +319 -319
- tapps_agents/cli/validators/__init__.py +9 -9
- tapps_agents/cli/validators/command_validator.py +81 -81
- tapps_agents/context7/__init__.py +112 -112
- tapps_agents/context7/agent_integration.py +869 -869
- tapps_agents/context7/analytics.py +382 -382
- tapps_agents/context7/analytics_dashboard.py +299 -299
- tapps_agents/context7/async_cache.py +681 -681
- tapps_agents/context7/backup_client.py +958 -958
- tapps_agents/context7/cache_locking.py +194 -194
- tapps_agents/context7/cache_metadata.py +214 -214
- tapps_agents/context7/cache_prewarm.py +488 -488
- tapps_agents/context7/cache_structure.py +168 -168
- tapps_agents/context7/cache_warming.py +604 -604
- tapps_agents/context7/circuit_breaker.py +376 -376
- tapps_agents/context7/cleanup.py +461 -461
- tapps_agents/context7/commands.py +858 -858
- tapps_agents/context7/credential_validation.py +276 -276
- tapps_agents/context7/cross_reference_resolver.py +168 -168
- tapps_agents/context7/cross_references.py +424 -424
- tapps_agents/context7/doc_manager.py +225 -225
- tapps_agents/context7/fuzzy_matcher.py +369 -369
- tapps_agents/context7/kb_cache.py +404 -404
- tapps_agents/context7/language_detector.py +219 -219
- tapps_agents/context7/library_detector.py +725 -725
- tapps_agents/context7/lookup.py +738 -738
- tapps_agents/context7/metadata.py +258 -258
- tapps_agents/context7/refresh_queue.py +300 -300
- tapps_agents/context7/security.py +373 -373
- tapps_agents/context7/staleness_policies.py +278 -278
- tapps_agents/context7/tiles_integration.py +47 -47
- tapps_agents/continuous_bug_fix/__init__.py +20 -20
- tapps_agents/continuous_bug_fix/bug_finder.py +306 -306
- tapps_agents/continuous_bug_fix/bug_fix_coordinator.py +177 -177
- tapps_agents/continuous_bug_fix/commit_manager.py +178 -178
- tapps_agents/continuous_bug_fix/continuous_bug_fixer.py +322 -322
- tapps_agents/continuous_bug_fix/proactive_bug_finder.py +285 -285
- tapps_agents/core/__init__.py +298 -298
- tapps_agents/core/adaptive_cache_config.py +432 -432
- tapps_agents/core/agent_base.py +647 -647
- tapps_agents/core/agent_cache.py +466 -466
- tapps_agents/core/agent_learning.py +1865 -1865
- tapps_agents/core/analytics_dashboard.py +563 -563
- tapps_agents/core/analytics_enhancements.py +597 -597
- tapps_agents/core/anonymization.py +274 -274
- tapps_agents/core/artifact_context_builder.py +293 -0
- tapps_agents/core/ast_parser.py +228 -228
- tapps_agents/core/async_file_ops.py +402 -402
- tapps_agents/core/best_practice_consultant.py +299 -299
- tapps_agents/core/brownfield_analyzer.py +299 -299
- tapps_agents/core/brownfield_review.py +541 -541
- tapps_agents/core/browser_controller.py +513 -513
- tapps_agents/core/capability_registry.py +418 -418
- tapps_agents/core/change_impact_analyzer.py +190 -190
- tapps_agents/core/checkpoint_manager.py +377 -377
- tapps_agents/core/code_generator.py +329 -329
- tapps_agents/core/code_validator.py +276 -276
- tapps_agents/core/command_registry.py +327 -327
- tapps_agents/core/config.py +33 -0
- tapps_agents/core/context_gathering/__init__.py +2 -2
- tapps_agents/core/context_gathering/repository_explorer.py +28 -28
- tapps_agents/core/context_intelligence/__init__.py +2 -2
- tapps_agents/core/context_intelligence/relevance_scorer.py +24 -24
- tapps_agents/core/context_intelligence/token_budget_manager.py +27 -27
- tapps_agents/core/context_manager.py +240 -240
- tapps_agents/core/cursor_feedback_monitor.py +146 -146
- tapps_agents/core/cursor_verification.py +290 -290
- tapps_agents/core/customization_loader.py +280 -280
- tapps_agents/core/customization_schema.py +260 -260
- tapps_agents/core/customization_template.py +238 -238
- tapps_agents/core/debug_logger.py +124 -124
- tapps_agents/core/design_validator.py +298 -298
- tapps_agents/core/diagram_generator.py +226 -226
- tapps_agents/core/docker_utils.py +232 -232
- tapps_agents/core/document_generator.py +617 -617
- tapps_agents/core/domain_detector.py +30 -30
- tapps_agents/core/error_envelope.py +454 -454
- tapps_agents/core/error_handler.py +270 -270
- tapps_agents/core/estimation_tracker.py +189 -189
- tapps_agents/core/eval_prompt_engine.py +116 -116
- tapps_agents/core/evaluation_base.py +119 -119
- tapps_agents/core/evaluation_models.py +320 -320
- tapps_agents/core/evaluation_orchestrator.py +225 -225
- tapps_agents/core/evaluators/__init__.py +7 -7
- tapps_agents/core/evaluators/architectural_evaluator.py +205 -205
- tapps_agents/core/evaluators/behavioral_evaluator.py +160 -160
- tapps_agents/core/evaluators/performance_profile_evaluator.py +160 -160
- tapps_agents/core/evaluators/security_posture_evaluator.py +148 -148
- tapps_agents/core/evaluators/spec_compliance_evaluator.py +181 -181
- tapps_agents/core/exceptions.py +107 -107
- tapps_agents/core/expert_config_generator.py +293 -293
- tapps_agents/core/export_schema.py +202 -202
- tapps_agents/core/external_feedback_models.py +102 -102
- tapps_agents/core/external_feedback_storage.py +213 -213
- tapps_agents/core/fallback_strategy.py +314 -314
- tapps_agents/core/feedback_analyzer.py +162 -162
- tapps_agents/core/feedback_collector.py +178 -178
- tapps_agents/core/git_operations.py +445 -445
- tapps_agents/core/hardware_profiler.py +151 -151
- tapps_agents/core/instructions.py +324 -324
- tapps_agents/core/io_guardrails.py +69 -69
- tapps_agents/core/issue_manifest.py +249 -249
- tapps_agents/core/issue_schema.py +139 -139
- tapps_agents/core/json_utils.py +128 -128
- tapps_agents/core/knowledge_graph.py +446 -446
- tapps_agents/core/language_detector.py +296 -296
- tapps_agents/core/learning_confidence.py +242 -242
- tapps_agents/core/learning_dashboard.py +246 -246
- tapps_agents/core/learning_decision.py +384 -384
- tapps_agents/core/learning_explainability.py +578 -578
- tapps_agents/core/learning_export.py +287 -287
- tapps_agents/core/learning_integration.py +228 -228
- tapps_agents/core/llm_behavior.py +232 -232
- tapps_agents/core/long_duration_support.py +786 -786
- tapps_agents/core/mcp_setup.py +106 -106
- tapps_agents/core/memory_integration.py +396 -396
- tapps_agents/core/meta_learning.py +666 -666
- tapps_agents/core/module_path_sanitizer.py +199 -199
- tapps_agents/core/multi_agent_orchestrator.py +382 -382
- tapps_agents/core/network_errors.py +125 -125
- tapps_agents/core/nfr_validator.py +336 -336
- tapps_agents/core/offline_mode.py +158 -158
- tapps_agents/core/output_contracts.py +300 -300
- tapps_agents/core/output_formatter.py +300 -300
- tapps_agents/core/path_normalizer.py +174 -174
- tapps_agents/core/path_validator.py +322 -322
- tapps_agents/core/pattern_library.py +250 -250
- tapps_agents/core/performance_benchmark.py +301 -301
- tapps_agents/core/performance_monitor.py +184 -184
- tapps_agents/core/playwright_mcp_controller.py +771 -771
- tapps_agents/core/policy_loader.py +135 -135
- tapps_agents/core/progress.py +166 -166
- tapps_agents/core/project_profile.py +354 -354
- tapps_agents/core/project_type_detector.py +454 -454
- tapps_agents/core/prompt_base.py +223 -223
- tapps_agents/core/prompt_learning/__init__.py +2 -2
- tapps_agents/core/prompt_learning/learning_loop.py +24 -24
- tapps_agents/core/prompt_learning/project_prompt_store.py +25 -25
- tapps_agents/core/prompt_learning/skills_prompt_analyzer.py +35 -35
- tapps_agents/core/prompt_optimization/__init__.py +6 -6
- tapps_agents/core/prompt_optimization/ab_tester.py +114 -114
- tapps_agents/core/prompt_optimization/correlation_analyzer.py +160 -160
- tapps_agents/core/prompt_optimization/progressive_refiner.py +129 -129
- tapps_agents/core/prompt_optimization/prompt_library.py +37 -37
- tapps_agents/core/requirements_evaluator.py +431 -431
- tapps_agents/core/resource_aware_executor.py +449 -449
- tapps_agents/core/resource_monitor.py +343 -343
- tapps_agents/core/resume_handler.py +298 -298
- tapps_agents/core/retry_handler.py +197 -197
- tapps_agents/core/review_checklists.py +479 -479
- tapps_agents/core/role_loader.py +201 -201
- tapps_agents/core/role_template_loader.py +201 -201
- tapps_agents/core/runtime_mode.py +60 -60
- tapps_agents/core/security_scanner.py +342 -342
- tapps_agents/core/skill_agent_registry.py +194 -194
- tapps_agents/core/skill_integration.py +208 -208
- tapps_agents/core/skill_loader.py +492 -492
- tapps_agents/core/skill_template.py +341 -341
- tapps_agents/core/skill_validator.py +478 -478
- tapps_agents/core/stack_analyzer.py +35 -35
- tapps_agents/core/startup.py +174 -174
- tapps_agents/core/storage_manager.py +397 -397
- tapps_agents/core/storage_models.py +166 -166
- tapps_agents/core/story_evaluator.py +410 -410
- tapps_agents/core/subprocess_utils.py +170 -170
- tapps_agents/core/task_duration.py +296 -296
- tapps_agents/core/task_memory.py +582 -582
- tapps_agents/core/task_state.py +226 -226
- tapps_agents/core/tech_stack_priorities.py +208 -208
- tapps_agents/core/temp_directory.py +194 -194
- tapps_agents/core/template_merger.py +600 -600
- tapps_agents/core/template_selector.py +280 -280
- tapps_agents/core/test_generator.py +286 -286
- tapps_agents/core/tiered_context.py +253 -253
- tapps_agents/core/token_monitor.py +345 -345
- tapps_agents/core/traceability.py +254 -254
- tapps_agents/core/trajectory_tracker.py +50 -50
- tapps_agents/core/unicode_safe.py +143 -143
- tapps_agents/core/unified_cache_config.py +170 -170
- tapps_agents/core/unified_state.py +324 -324
- tapps_agents/core/validate_cursor_setup.py +237 -237
- tapps_agents/core/validation_registry.py +136 -136
- tapps_agents/core/validators/__init__.py +4 -4
- tapps_agents/core/validators/python_validator.py +87 -87
- tapps_agents/core/verification_agent.py +90 -90
- tapps_agents/core/visual_feedback.py +644 -644
- tapps_agents/core/workflow_validator.py +197 -197
- tapps_agents/core/worktree.py +367 -367
- tapps_agents/docker/__init__.py +10 -10
- tapps_agents/docker/analyzer.py +186 -186
- tapps_agents/docker/debugger.py +229 -229
- tapps_agents/docker/error_patterns.py +216 -216
- tapps_agents/epic/__init__.py +22 -22
- tapps_agents/epic/beads_sync.py +115 -115
- tapps_agents/epic/markdown_sync.py +105 -105
- tapps_agents/epic/models.py +96 -96
- tapps_agents/experts/__init__.py +163 -163
- tapps_agents/experts/agent_integration.py +243 -243
- tapps_agents/experts/auto_generator.py +331 -331
- tapps_agents/experts/base_expert.py +536 -536
- tapps_agents/experts/builtin_registry.py +261 -261
- tapps_agents/experts/business_metrics.py +565 -565
- tapps_agents/experts/cache.py +266 -266
- tapps_agents/experts/confidence_breakdown.py +306 -306
- tapps_agents/experts/confidence_calculator.py +336 -336
- tapps_agents/experts/confidence_metrics.py +236 -236
- tapps_agents/experts/domain_config.py +311 -311
- tapps_agents/experts/domain_detector.py +550 -550
- tapps_agents/experts/domain_utils.py +84 -84
- tapps_agents/experts/expert_config.py +113 -113
- tapps_agents/experts/expert_engine.py +465 -465
- tapps_agents/experts/expert_registry.py +744 -744
- tapps_agents/experts/expert_synthesizer.py +70 -70
- tapps_agents/experts/governance.py +197 -197
- tapps_agents/experts/history_logger.py +312 -312
- tapps_agents/experts/knowledge/README.md +180 -180
- tapps_agents/experts/knowledge/accessibility/accessible-forms.md +331 -331
- tapps_agents/experts/knowledge/accessibility/aria-patterns.md +344 -344
- tapps_agents/experts/knowledge/accessibility/color-contrast.md +285 -285
- tapps_agents/experts/knowledge/accessibility/keyboard-navigation.md +332 -332
- tapps_agents/experts/knowledge/accessibility/screen-readers.md +282 -282
- tapps_agents/experts/knowledge/accessibility/semantic-html.md +355 -355
- tapps_agents/experts/knowledge/accessibility/testing-accessibility.md +369 -369
- tapps_agents/experts/knowledge/accessibility/wcag-2.1.md +296 -296
- tapps_agents/experts/knowledge/accessibility/wcag-2.2.md +211 -211
- tapps_agents/experts/knowledge/agent-learning/best-practices.md +715 -715
- tapps_agents/experts/knowledge/agent-learning/pattern-extraction.md +282 -282
- tapps_agents/experts/knowledge/agent-learning/prompt-optimization.md +320 -320
- tapps_agents/experts/knowledge/ai-frameworks/model-optimization.md +90 -90
- tapps_agents/experts/knowledge/ai-frameworks/openvino-patterns.md +260 -260
- tapps_agents/experts/knowledge/api-design-integration/api-gateway-patterns.md +309 -309
- tapps_agents/experts/knowledge/api-design-integration/api-security-patterns.md +521 -521
- tapps_agents/experts/knowledge/api-design-integration/api-versioning.md +421 -421
- tapps_agents/experts/knowledge/api-design-integration/async-protocol-patterns.md +61 -61
- tapps_agents/experts/knowledge/api-design-integration/contract-testing.md +221 -221
- tapps_agents/experts/knowledge/api-design-integration/external-api-integration.md +489 -489
- tapps_agents/experts/knowledge/api-design-integration/fastapi-patterns.md +360 -360
- tapps_agents/experts/knowledge/api-design-integration/fastapi-testing.md +262 -262
- tapps_agents/experts/knowledge/api-design-integration/graphql-patterns.md +582 -582
- tapps_agents/experts/knowledge/api-design-integration/grpc-best-practices.md +499 -499
- tapps_agents/experts/knowledge/api-design-integration/mqtt-patterns.md +455 -455
- tapps_agents/experts/knowledge/api-design-integration/rate-limiting.md +507 -507
- tapps_agents/experts/knowledge/api-design-integration/restful-api-design.md +618 -618
- tapps_agents/experts/knowledge/api-design-integration/websocket-patterns.md +480 -480
- tapps_agents/experts/knowledge/cloud-infrastructure/cloud-native-patterns.md +175 -175
- tapps_agents/experts/knowledge/cloud-infrastructure/container-health-checks.md +261 -261
- tapps_agents/experts/knowledge/cloud-infrastructure/containerization.md +222 -222
- tapps_agents/experts/knowledge/cloud-infrastructure/cost-optimization.md +122 -122
- tapps_agents/experts/knowledge/cloud-infrastructure/disaster-recovery.md +153 -153
- tapps_agents/experts/knowledge/cloud-infrastructure/dockerfile-patterns.md +285 -285
- tapps_agents/experts/knowledge/cloud-infrastructure/infrastructure-as-code.md +187 -187
- tapps_agents/experts/knowledge/cloud-infrastructure/kubernetes-patterns.md +253 -253
- tapps_agents/experts/knowledge/cloud-infrastructure/multi-cloud-strategies.md +155 -155
- tapps_agents/experts/knowledge/cloud-infrastructure/serverless-architecture.md +200 -200
- tapps_agents/experts/knowledge/code-quality-analysis/README.md +16 -16
- tapps_agents/experts/knowledge/code-quality-analysis/code-metrics.md +137 -137
- tapps_agents/experts/knowledge/code-quality-analysis/complexity-analysis.md +181 -181
- tapps_agents/experts/knowledge/code-quality-analysis/technical-debt-patterns.md +191 -191
- tapps_agents/experts/knowledge/data-privacy-compliance/anonymization.md +313 -313
- tapps_agents/experts/knowledge/data-privacy-compliance/ccpa.md +255 -255
- tapps_agents/experts/knowledge/data-privacy-compliance/consent-management.md +282 -282
- tapps_agents/experts/knowledge/data-privacy-compliance/data-minimization.md +275 -275
- tapps_agents/experts/knowledge/data-privacy-compliance/data-retention.md +297 -297
- tapps_agents/experts/knowledge/data-privacy-compliance/data-subject-rights.md +383 -383
- tapps_agents/experts/knowledge/data-privacy-compliance/encryption-privacy.md +285 -285
- tapps_agents/experts/knowledge/data-privacy-compliance/gdpr.md +344 -344
- tapps_agents/experts/knowledge/data-privacy-compliance/hipaa.md +385 -385
- tapps_agents/experts/knowledge/data-privacy-compliance/privacy-by-design.md +280 -280
- tapps_agents/experts/knowledge/database-data-management/acid-vs-cap.md +164 -164
- tapps_agents/experts/knowledge/database-data-management/backup-and-recovery.md +182 -182
- tapps_agents/experts/knowledge/database-data-management/data-modeling.md +172 -172
- tapps_agents/experts/knowledge/database-data-management/database-design.md +187 -187
- tapps_agents/experts/knowledge/database-data-management/flux-query-optimization.md +342 -342
- tapps_agents/experts/knowledge/database-data-management/influxdb-connection-patterns.md +432 -432
- tapps_agents/experts/knowledge/database-data-management/influxdb-patterns.md +442 -442
- tapps_agents/experts/knowledge/database-data-management/migration-strategies.md +216 -216
- tapps_agents/experts/knowledge/database-data-management/nosql-patterns.md +259 -259
- tapps_agents/experts/knowledge/database-data-management/scalability-patterns.md +184 -184
- tapps_agents/experts/knowledge/database-data-management/sql-optimization.md +175 -175
- tapps_agents/experts/knowledge/database-data-management/time-series-modeling.md +444 -444
- tapps_agents/experts/knowledge/development-workflow/README.md +16 -16
- tapps_agents/experts/knowledge/development-workflow/automation-best-practices.md +216 -216
- tapps_agents/experts/knowledge/development-workflow/build-strategies.md +198 -198
- tapps_agents/experts/knowledge/development-workflow/deployment-patterns.md +205 -205
- tapps_agents/experts/knowledge/development-workflow/git-workflows.md +205 -205
- tapps_agents/experts/knowledge/documentation-knowledge-management/README.md +16 -16
- tapps_agents/experts/knowledge/documentation-knowledge-management/api-documentation-patterns.md +231 -231
- tapps_agents/experts/knowledge/documentation-knowledge-management/documentation-standards.md +191 -191
- tapps_agents/experts/knowledge/documentation-knowledge-management/knowledge-management.md +171 -171
- tapps_agents/experts/knowledge/documentation-knowledge-management/technical-writing-guide.md +192 -192
- tapps_agents/experts/knowledge/observability-monitoring/alerting-patterns.md +461 -461
- tapps_agents/experts/knowledge/observability-monitoring/apm-tools.md +459 -459
- tapps_agents/experts/knowledge/observability-monitoring/distributed-tracing.md +367 -367
- tapps_agents/experts/knowledge/observability-monitoring/logging-strategies.md +478 -478
- tapps_agents/experts/knowledge/observability-monitoring/metrics-and-monitoring.md +510 -510
- tapps_agents/experts/knowledge/observability-monitoring/observability-best-practices.md +492 -492
- tapps_agents/experts/knowledge/observability-monitoring/open-telemetry.md +573 -573
- tapps_agents/experts/knowledge/observability-monitoring/slo-sli-sla.md +419 -419
- tapps_agents/experts/knowledge/performance/anti-patterns.md +284 -284
- tapps_agents/experts/knowledge/performance/api-performance.md +256 -256
- tapps_agents/experts/knowledge/performance/caching.md +327 -327
- tapps_agents/experts/knowledge/performance/database-performance.md +252 -252
- tapps_agents/experts/knowledge/performance/optimization-patterns.md +327 -327
- tapps_agents/experts/knowledge/performance/profiling.md +297 -297
- tapps_agents/experts/knowledge/performance/resource-management.md +293 -293
- tapps_agents/experts/knowledge/performance/scalability.md +306 -306
- tapps_agents/experts/knowledge/security/owasp-top10.md +209 -209
- tapps_agents/experts/knowledge/security/secure-coding-practices.md +207 -207
- tapps_agents/experts/knowledge/security/threat-modeling.md +220 -220
- tapps_agents/experts/knowledge/security/vulnerability-patterns.md +342 -342
- tapps_agents/experts/knowledge/software-architecture/docker-compose-patterns.md +314 -314
- tapps_agents/experts/knowledge/software-architecture/microservices-patterns.md +379 -379
- tapps_agents/experts/knowledge/software-architecture/service-communication.md +316 -316
- tapps_agents/experts/knowledge/testing/best-practices.md +310 -310
- tapps_agents/experts/knowledge/testing/coverage-analysis.md +293 -293
- tapps_agents/experts/knowledge/testing/mocking.md +256 -256
- tapps_agents/experts/knowledge/testing/test-automation.md +276 -276
- tapps_agents/experts/knowledge/testing/test-data.md +271 -271
- tapps_agents/experts/knowledge/testing/test-design-patterns.md +280 -280
- tapps_agents/experts/knowledge/testing/test-maintenance.md +236 -236
- tapps_agents/experts/knowledge/testing/test-strategies.md +311 -311
- tapps_agents/experts/knowledge/user-experience/information-architecture.md +325 -325
- tapps_agents/experts/knowledge/user-experience/interaction-design.md +363 -363
- tapps_agents/experts/knowledge/user-experience/prototyping.md +293 -293
- tapps_agents/experts/knowledge/user-experience/usability-heuristics.md +337 -337
- tapps_agents/experts/knowledge/user-experience/usability-testing.md +311 -311
- tapps_agents/experts/knowledge/user-experience/user-journeys.md +296 -296
- tapps_agents/experts/knowledge/user-experience/user-research.md +373 -373
- tapps_agents/experts/knowledge/user-experience/ux-principles.md +340 -340
- tapps_agents/experts/knowledge_freshness.py +321 -321
- tapps_agents/experts/knowledge_ingestion.py +438 -438
- tapps_agents/experts/knowledge_need_detector.py +93 -93
- tapps_agents/experts/knowledge_validator.py +382 -382
- tapps_agents/experts/observability.py +440 -440
- tapps_agents/experts/passive_notifier.py +238 -238
- tapps_agents/experts/proactive_orchestrator.py +32 -32
- tapps_agents/experts/rag_chunker.py +205 -205
- tapps_agents/experts/rag_embedder.py +152 -152
- tapps_agents/experts/rag_evaluation.py +299 -299
- tapps_agents/experts/rag_index.py +303 -303
- tapps_agents/experts/rag_metrics.py +293 -293
- tapps_agents/experts/rag_safety.py +263 -263
- tapps_agents/experts/report_generator.py +296 -296
- tapps_agents/experts/setup_wizard.py +441 -441
- tapps_agents/experts/simple_rag.py +431 -431
- tapps_agents/experts/vector_rag.py +354 -354
- tapps_agents/experts/weight_distributor.py +304 -304
- tapps_agents/health/__init__.py +24 -24
- tapps_agents/health/base.py +75 -75
- tapps_agents/health/checks/__init__.py +22 -22
- tapps_agents/health/checks/automation.py +127 -127
- tapps_agents/health/checks/context7_cache.py +210 -210
- tapps_agents/health/checks/environment.py +116 -116
- tapps_agents/health/checks/execution.py +170 -170
- tapps_agents/health/checks/knowledge_base.py +187 -187
- tapps_agents/health/checks/outcomes.py +324 -324
- tapps_agents/health/collector.py +280 -280
- tapps_agents/health/dashboard.py +137 -137
- tapps_agents/health/metrics.py +151 -151
- tapps_agents/health/orchestrator.py +271 -271
- tapps_agents/health/registry.py +166 -166
- tapps_agents/hooks/__init__.py +33 -33
- tapps_agents/hooks/config.py +140 -140
- tapps_agents/hooks/events.py +135 -135
- tapps_agents/hooks/executor.py +128 -128
- tapps_agents/hooks/manager.py +143 -143
- tapps_agents/integration/__init__.py +8 -8
- tapps_agents/integration/service_integrator.py +121 -121
- tapps_agents/integrations/__init__.py +10 -10
- tapps_agents/integrations/clawdbot.py +525 -525
- tapps_agents/integrations/memory_bridge.py +356 -356
- tapps_agents/mcp/__init__.py +18 -18
- tapps_agents/mcp/gateway.py +112 -112
- tapps_agents/mcp/servers/__init__.py +13 -13
- tapps_agents/mcp/servers/analysis.py +204 -204
- tapps_agents/mcp/servers/context7.py +198 -198
- tapps_agents/mcp/servers/filesystem.py +218 -218
- tapps_agents/mcp/servers/git.py +201 -201
- tapps_agents/mcp/tool_registry.py +115 -115
- tapps_agents/quality/__init__.py +54 -54
- tapps_agents/quality/coverage_analyzer.py +379 -379
- tapps_agents/quality/enforcement.py +82 -82
- tapps_agents/quality/gates/__init__.py +37 -37
- tapps_agents/quality/gates/approval_gate.py +255 -255
- tapps_agents/quality/gates/base.py +84 -84
- tapps_agents/quality/gates/exceptions.py +43 -43
- tapps_agents/quality/gates/policy_gate.py +195 -195
- tapps_agents/quality/gates/registry.py +239 -239
- tapps_agents/quality/gates/security_gate.py +156 -156
- tapps_agents/quality/quality_gates.py +369 -369
- tapps_agents/quality/secret_scanner.py +335 -335
- tapps_agents/session/__init__.py +19 -19
- tapps_agents/session/manager.py +256 -256
- tapps_agents/simple_mode/__init__.py +66 -66
- tapps_agents/simple_mode/agent_contracts.py +357 -357
- tapps_agents/simple_mode/beads_hooks.py +151 -151
- tapps_agents/simple_mode/code_snippet_handler.py +382 -382
- tapps_agents/simple_mode/documentation_manager.py +395 -395
- tapps_agents/simple_mode/documentation_reader.py +187 -187
- tapps_agents/simple_mode/file_inference.py +292 -292
- tapps_agents/simple_mode/framework_change_detector.py +268 -268
- tapps_agents/simple_mode/intent_parser.py +510 -510
- tapps_agents/simple_mode/learning_progression.py +358 -358
- tapps_agents/simple_mode/nl_handler.py +700 -700
- tapps_agents/simple_mode/onboarding.py +253 -253
- tapps_agents/simple_mode/orchestrators/__init__.py +38 -38
- tapps_agents/simple_mode/orchestrators/base.py +185 -185
- tapps_agents/simple_mode/orchestrators/breakdown_orchestrator.py +49 -49
- tapps_agents/simple_mode/orchestrators/brownfield_orchestrator.py +135 -135
- tapps_agents/simple_mode/orchestrators/build_orchestrator.py +2700 -2667
- tapps_agents/simple_mode/orchestrators/deliverable_checklist.py +349 -349
- tapps_agents/simple_mode/orchestrators/enhance_orchestrator.py +53 -53
- tapps_agents/simple_mode/orchestrators/epic_orchestrator.py +122 -122
- tapps_agents/simple_mode/orchestrators/explore_orchestrator.py +184 -184
- tapps_agents/simple_mode/orchestrators/fix_orchestrator.py +723 -723
- tapps_agents/simple_mode/orchestrators/plan_analysis_orchestrator.py +206 -206
- tapps_agents/simple_mode/orchestrators/pr_orchestrator.py +237 -237
- tapps_agents/simple_mode/orchestrators/refactor_orchestrator.py +222 -222
- tapps_agents/simple_mode/orchestrators/requirements_tracer.py +262 -262
- tapps_agents/simple_mode/orchestrators/resume_orchestrator.py +210 -210
- tapps_agents/simple_mode/orchestrators/review_orchestrator.py +161 -161
- tapps_agents/simple_mode/orchestrators/test_orchestrator.py +82 -82
- tapps_agents/simple_mode/output_aggregator.py +340 -340
- tapps_agents/simple_mode/result_formatters.py +598 -598
- tapps_agents/simple_mode/step_dependencies.py +382 -382
- tapps_agents/simple_mode/step_results.py +276 -276
- tapps_agents/simple_mode/streaming.py +388 -388
- tapps_agents/simple_mode/variations.py +129 -129
- tapps_agents/simple_mode/visual_feedback.py +238 -238
- tapps_agents/simple_mode/zero_config.py +274 -274
- tapps_agents/suggestions/__init__.py +8 -8
- tapps_agents/suggestions/inline_suggester.py +52 -52
- tapps_agents/templates/__init__.py +8 -8
- tapps_agents/templates/microservice_generator.py +274 -274
- tapps_agents/utils/env_validator.py +291 -291
- tapps_agents/workflow/__init__.py +171 -171
- tapps_agents/workflow/acceptance_verifier.py +132 -132
- tapps_agents/workflow/agent_handlers/__init__.py +41 -41
- tapps_agents/workflow/agent_handlers/analyst_handler.py +75 -75
- tapps_agents/workflow/agent_handlers/architect_handler.py +107 -107
- tapps_agents/workflow/agent_handlers/base.py +84 -84
- tapps_agents/workflow/agent_handlers/debugger_handler.py +100 -100
- tapps_agents/workflow/agent_handlers/designer_handler.py +110 -110
- tapps_agents/workflow/agent_handlers/documenter_handler.py +94 -94
- tapps_agents/workflow/agent_handlers/implementer_handler.py +235 -235
- tapps_agents/workflow/agent_handlers/ops_handler.py +62 -62
- tapps_agents/workflow/agent_handlers/orchestrator_handler.py +43 -43
- tapps_agents/workflow/agent_handlers/planner_handler.py +98 -98
- tapps_agents/workflow/agent_handlers/registry.py +119 -119
- tapps_agents/workflow/agent_handlers/reviewer_handler.py +119 -119
- tapps_agents/workflow/agent_handlers/tester_handler.py +69 -69
- tapps_agents/workflow/analytics_accessor.py +337 -337
- tapps_agents/workflow/analytics_alerts.py +416 -416
- tapps_agents/workflow/analytics_dashboard_cursor.py +281 -281
- tapps_agents/workflow/analytics_dual_write.py +103 -103
- tapps_agents/workflow/analytics_integration.py +119 -119
- tapps_agents/workflow/analytics_query_parser.py +278 -278
- tapps_agents/workflow/analytics_visualizer.py +259 -259
- tapps_agents/workflow/artifact_helper.py +204 -204
- tapps_agents/workflow/audit_logger.py +263 -263
- tapps_agents/workflow/auto_execution_config.py +340 -340
- tapps_agents/workflow/auto_progression.py +586 -586
- tapps_agents/workflow/branch_cleanup.py +349 -349
- tapps_agents/workflow/checkpoint.py +256 -256
- tapps_agents/workflow/checkpoint_manager.py +178 -178
- tapps_agents/workflow/code_artifact.py +179 -179
- tapps_agents/workflow/common_enums.py +96 -96
- tapps_agents/workflow/confirmation_handler.py +130 -130
- tapps_agents/workflow/context_analyzer.py +222 -222
- tapps_agents/workflow/context_artifact.py +230 -230
- tapps_agents/workflow/cursor_chat.py +94 -94
- tapps_agents/workflow/cursor_executor.py +2337 -2196
- tapps_agents/workflow/cursor_skill_helper.py +516 -516
- tapps_agents/workflow/dependency_resolver.py +244 -244
- tapps_agents/workflow/design_artifact.py +156 -156
- tapps_agents/workflow/detector.py +751 -751
- tapps_agents/workflow/direct_execution_fallback.py +301 -301
- tapps_agents/workflow/docs_artifact.py +168 -168
- tapps_agents/workflow/enforcer.py +389 -389
- tapps_agents/workflow/enhancement_artifact.py +142 -142
- tapps_agents/workflow/error_recovery.py +806 -806
- tapps_agents/workflow/event_bus.py +183 -183
- tapps_agents/workflow/event_log.py +612 -612
- tapps_agents/workflow/events.py +63 -63
- tapps_agents/workflow/exceptions.py +43 -43
- tapps_agents/workflow/execution_graph.py +498 -498
- tapps_agents/workflow/execution_plan.py +126 -126
- tapps_agents/workflow/file_utils.py +186 -186
- tapps_agents/workflow/gate_evaluator.py +182 -182
- tapps_agents/workflow/gate_integration.py +200 -200
- tapps_agents/workflow/graph_visualizer.py +130 -130
- tapps_agents/workflow/health_checker.py +206 -206
- tapps_agents/workflow/logging_helper.py +243 -243
- tapps_agents/workflow/manifest.py +582 -582
- tapps_agents/workflow/marker_writer.py +250 -250
- tapps_agents/workflow/message_formatter.py +188 -188
- tapps_agents/workflow/messaging.py +325 -325
- tapps_agents/workflow/metadata_models.py +91 -91
- tapps_agents/workflow/metrics_integration.py +226 -226
- tapps_agents/workflow/migration_utils.py +116 -116
- tapps_agents/workflow/models.py +148 -111
- tapps_agents/workflow/nlp_config.py +198 -198
- tapps_agents/workflow/nlp_error_handler.py +207 -207
- tapps_agents/workflow/nlp_executor.py +163 -163
- tapps_agents/workflow/nlp_parser.py +528 -528
- tapps_agents/workflow/observability_dashboard.py +451 -451
- tapps_agents/workflow/observer.py +170 -170
- tapps_agents/workflow/ops_artifact.py +257 -257
- tapps_agents/workflow/output_passing.py +214 -214
- tapps_agents/workflow/parallel_executor.py +463 -463
- tapps_agents/workflow/planning_artifact.py +179 -179
- tapps_agents/workflow/preset_loader.py +285 -285
- tapps_agents/workflow/preset_recommender.py +270 -270
- tapps_agents/workflow/progress_logger.py +145 -145
- tapps_agents/workflow/progress_manager.py +303 -303
- tapps_agents/workflow/progress_monitor.py +186 -186
- tapps_agents/workflow/progress_updates.py +423 -423
- tapps_agents/workflow/quality_artifact.py +158 -158
- tapps_agents/workflow/quality_loopback.py +101 -101
- tapps_agents/workflow/recommender.py +387 -387
- tapps_agents/workflow/remediation_loop.py +166 -166
- tapps_agents/workflow/result_aggregator.py +300 -300
- tapps_agents/workflow/review_artifact.py +185 -185
- tapps_agents/workflow/schema_validator.py +522 -522
- tapps_agents/workflow/session_handoff.py +178 -178
- tapps_agents/workflow/skill_invoker.py +648 -648
- tapps_agents/workflow/state_manager.py +756 -756
- tapps_agents/workflow/state_persistence_config.py +331 -331
- tapps_agents/workflow/status_monitor.py +449 -449
- tapps_agents/workflow/step_checkpoint.py +314 -314
- tapps_agents/workflow/step_details.py +201 -201
- tapps_agents/workflow/story_models.py +147 -147
- tapps_agents/workflow/streaming.py +416 -416
- tapps_agents/workflow/suggestion_engine.py +552 -552
- tapps_agents/workflow/testing_artifact.py +186 -186
- tapps_agents/workflow/timeline.py +158 -158
- tapps_agents/workflow/token_integration.py +209 -209
- tapps_agents/workflow/validation.py +217 -217
- tapps_agents/workflow/visual_feedback.py +391 -391
- tapps_agents/workflow/workflow_chain.py +95 -95
- tapps_agents/workflow/workflow_summary.py +219 -219
- tapps_agents/workflow/worktree_manager.py +724 -724
- {tapps_agents-3.5.40.dist-info → tapps_agents-3.6.0.dist-info}/METADATA +672 -672
- tapps_agents-3.6.0.dist-info/RECORD +758 -0
- {tapps_agents-3.5.40.dist-info → tapps_agents-3.6.0.dist-info}/licenses/LICENSE +22 -22
- tapps_agents/health/checks/outcomes.backup_20260204_064058.py +0 -324
- tapps_agents/health/checks/outcomes.backup_20260204_064256.py +0 -324
- tapps_agents/health/checks/outcomes.backup_20260204_064600.py +0 -324
- tapps_agents-3.5.40.dist-info/RECORD +0 -760
- {tapps_agents-3.5.40.dist-info → tapps_agents-3.6.0.dist-info}/WHEEL +0 -0
- {tapps_agents-3.5.40.dist-info → tapps_agents-3.6.0.dist-info}/entry_points.txt +0 -0
- {tapps_agents-3.5.40.dist-info → tapps_agents-3.6.0.dist-info}/top_level.txt +0 -0
|
@@ -1,376 +1,376 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Circuit Breaker Pattern for Context7 Operations.
|
|
3
|
-
|
|
4
|
-
Prevents cascading failures by "opening" the circuit when failures exceed a threshold.
|
|
5
|
-
2025 Architecture Pattern: Resilient distributed systems with fail-fast semantics.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from __future__ import annotations
|
|
9
|
-
|
|
10
|
-
import asyncio
|
|
11
|
-
import logging
|
|
12
|
-
import time
|
|
13
|
-
from dataclasses import dataclass, field
|
|
14
|
-
from enum import Enum
|
|
15
|
-
from typing import Any, Callable, TypeVar
|
|
16
|
-
|
|
17
|
-
logger = logging.getLogger(__name__)
|
|
18
|
-
|
|
19
|
-
T = TypeVar("T")
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class CircuitState(Enum):
|
|
23
|
-
"""Circuit breaker states."""
|
|
24
|
-
|
|
25
|
-
CLOSED = "closed" # Normal operation, requests flow through
|
|
26
|
-
OPEN = "open" # Circuit tripped, requests fail fast
|
|
27
|
-
HALF_OPEN = "half_open" # Testing if service recovered
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
@dataclass
|
|
31
|
-
class CircuitBreakerConfig:
|
|
32
|
-
"""Circuit breaker configuration."""
|
|
33
|
-
|
|
34
|
-
failure_threshold: int = 3 # Failures before opening circuit
|
|
35
|
-
success_threshold: int = 2 # Successes in half-open to close circuit
|
|
36
|
-
timeout_seconds: float = 5.0 # Request timeout
|
|
37
|
-
reset_timeout_seconds: float = 30.0 # Time before half-open from open
|
|
38
|
-
name: str = "context7"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
@dataclass
|
|
42
|
-
class CircuitBreakerStats:
|
|
43
|
-
"""Circuit breaker statistics."""
|
|
44
|
-
|
|
45
|
-
total_requests: int = 0
|
|
46
|
-
successful_requests: int = 0
|
|
47
|
-
failed_requests: int = 0
|
|
48
|
-
rejected_requests: int = 0 # Requests rejected due to open circuit
|
|
49
|
-
consecutive_failures: int = 0
|
|
50
|
-
consecutive_successes: int = 0
|
|
51
|
-
state: CircuitState = CircuitState.CLOSED
|
|
52
|
-
last_failure_time: float | None = None
|
|
53
|
-
last_state_change: float | None = None
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
class CircuitBreaker:
|
|
57
|
-
"""
|
|
58
|
-
Circuit Breaker for resilient Context7 operations.
|
|
59
|
-
|
|
60
|
-
States:
|
|
61
|
-
- CLOSED: Normal operation, requests flow through
|
|
62
|
-
- OPEN: Circuit tripped, requests fail fast (no actual call)
|
|
63
|
-
- HALF_OPEN: Testing recovery, limited requests allowed
|
|
64
|
-
|
|
65
|
-
Transitions:
|
|
66
|
-
- CLOSED -> OPEN: When failures >= failure_threshold
|
|
67
|
-
- OPEN -> HALF_OPEN: After reset_timeout_seconds
|
|
68
|
-
- HALF_OPEN -> CLOSED: When successes >= success_threshold
|
|
69
|
-
- HALF_OPEN -> OPEN: On any failure
|
|
70
|
-
"""
|
|
71
|
-
|
|
72
|
-
def __init__(self, config: CircuitBreakerConfig | None = None):
|
|
73
|
-
self.config = config or CircuitBreakerConfig()
|
|
74
|
-
self._stats = CircuitBreakerStats()
|
|
75
|
-
self._lock = asyncio.Lock()
|
|
76
|
-
|
|
77
|
-
@property
|
|
78
|
-
def state(self) -> CircuitState:
|
|
79
|
-
"""Get current circuit state."""
|
|
80
|
-
return self._stats.state
|
|
81
|
-
|
|
82
|
-
@property
|
|
83
|
-
def is_closed(self) -> bool:
|
|
84
|
-
"""Check if circuit is closed (normal operation)."""
|
|
85
|
-
return self._stats.state == CircuitState.CLOSED
|
|
86
|
-
|
|
87
|
-
@property
|
|
88
|
-
def is_open(self) -> bool:
|
|
89
|
-
"""Check if circuit is open (failing fast)."""
|
|
90
|
-
return self._stats.state == CircuitState.OPEN
|
|
91
|
-
|
|
92
|
-
@property
|
|
93
|
-
def stats(self) -> CircuitBreakerStats:
|
|
94
|
-
"""Get circuit breaker statistics."""
|
|
95
|
-
return self._stats
|
|
96
|
-
|
|
97
|
-
async def _check_state_transition(self) -> None:
|
|
98
|
-
"""Check if state should transition (OPEN -> HALF_OPEN)."""
|
|
99
|
-
if self._stats.state == CircuitState.OPEN:
|
|
100
|
-
if self._stats.last_failure_time is not None:
|
|
101
|
-
elapsed = time.time() - self._stats.last_failure_time
|
|
102
|
-
if elapsed >= self.config.reset_timeout_seconds:
|
|
103
|
-
self._stats.state = CircuitState.HALF_OPEN
|
|
104
|
-
self._stats.consecutive_failures = 0
|
|
105
|
-
self._stats.consecutive_successes = 0
|
|
106
|
-
self._stats.last_state_change = time.time()
|
|
107
|
-
logger.info(
|
|
108
|
-
f"Circuit breaker [{self.config.name}] transitioned to HALF_OPEN "
|
|
109
|
-
f"after {elapsed:.1f}s"
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
async def _record_success(self) -> None:
|
|
113
|
-
"""Record successful request."""
|
|
114
|
-
self._stats.total_requests += 1
|
|
115
|
-
self._stats.successful_requests += 1
|
|
116
|
-
self._stats.consecutive_successes += 1
|
|
117
|
-
self._stats.consecutive_failures = 0
|
|
118
|
-
|
|
119
|
-
if self._stats.state == CircuitState.HALF_OPEN:
|
|
120
|
-
if self._stats.consecutive_successes >= self.config.success_threshold:
|
|
121
|
-
self._stats.state = CircuitState.CLOSED
|
|
122
|
-
self._stats.last_state_change = time.time()
|
|
123
|
-
logger.info(
|
|
124
|
-
f"Circuit breaker [{self.config.name}] CLOSED after "
|
|
125
|
-
f"{self._stats.consecutive_successes} consecutive successes"
|
|
126
|
-
)
|
|
127
|
-
|
|
128
|
-
async def _record_failure(self) -> None:
|
|
129
|
-
"""Record failed request."""
|
|
130
|
-
self._stats.total_requests += 1
|
|
131
|
-
self._stats.failed_requests += 1
|
|
132
|
-
self._stats.consecutive_failures += 1
|
|
133
|
-
self._stats.consecutive_successes = 0
|
|
134
|
-
self._stats.last_failure_time = time.time()
|
|
135
|
-
|
|
136
|
-
if self._stats.state == CircuitState.HALF_OPEN:
|
|
137
|
-
# Any failure in half-open reopens the circuit
|
|
138
|
-
self._stats.state = CircuitState.OPEN
|
|
139
|
-
self._stats.last_state_change = time.time()
|
|
140
|
-
logger.warning(
|
|
141
|
-
f"Circuit breaker [{self.config.name}] OPENED from HALF_OPEN due to failure"
|
|
142
|
-
)
|
|
143
|
-
elif self._stats.state == CircuitState.CLOSED:
|
|
144
|
-
if self._stats.consecutive_failures >= self.config.failure_threshold:
|
|
145
|
-
self._stats.state = CircuitState.OPEN
|
|
146
|
-
self._stats.last_state_change = time.time()
|
|
147
|
-
logger.warning(
|
|
148
|
-
f"Circuit breaker [{self.config.name}] OPENED after "
|
|
149
|
-
f"{self._stats.consecutive_failures} consecutive failures"
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
async def _record_rejection(self) -> None:
|
|
153
|
-
"""Record rejected request (circuit open)."""
|
|
154
|
-
self._stats.rejected_requests += 1
|
|
155
|
-
|
|
156
|
-
async def call(
|
|
157
|
-
self,
|
|
158
|
-
func: Callable[..., Any],
|
|
159
|
-
*args: Any,
|
|
160
|
-
fallback: Any = None,
|
|
161
|
-
**kwargs: Any,
|
|
162
|
-
) -> Any:
|
|
163
|
-
"""
|
|
164
|
-
Execute function with circuit breaker protection.
|
|
165
|
-
|
|
166
|
-
Args:
|
|
167
|
-
func: Async function to execute
|
|
168
|
-
*args: Positional arguments for func
|
|
169
|
-
fallback: Value to return if circuit is open or call fails
|
|
170
|
-
**kwargs: Keyword arguments for func
|
|
171
|
-
|
|
172
|
-
Returns:
|
|
173
|
-
Function result or fallback value
|
|
174
|
-
|
|
175
|
-
Raises:
|
|
176
|
-
CircuitBreakerOpen: If circuit is open and no fallback provided
|
|
177
|
-
"""
|
|
178
|
-
async with self._lock:
|
|
179
|
-
await self._check_state_transition()
|
|
180
|
-
|
|
181
|
-
if self._stats.state == CircuitState.OPEN:
|
|
182
|
-
await self._record_rejection()
|
|
183
|
-
logger.debug(
|
|
184
|
-
f"Circuit breaker [{self.config.name}] OPEN - fast failing"
|
|
185
|
-
)
|
|
186
|
-
if fallback is not None:
|
|
187
|
-
return fallback
|
|
188
|
-
raise CircuitBreakerOpen(
|
|
189
|
-
f"Circuit breaker [{self.config.name}] is OPEN"
|
|
190
|
-
)
|
|
191
|
-
|
|
192
|
-
# Execute with timeout
|
|
193
|
-
try:
|
|
194
|
-
result = await asyncio.wait_for(
|
|
195
|
-
func(*args, **kwargs),
|
|
196
|
-
timeout=self.config.timeout_seconds,
|
|
197
|
-
)
|
|
198
|
-
async with self._lock:
|
|
199
|
-
await self._record_success()
|
|
200
|
-
return result
|
|
201
|
-
|
|
202
|
-
except asyncio.TimeoutError:
|
|
203
|
-
async with self._lock:
|
|
204
|
-
await self._record_failure()
|
|
205
|
-
logger.debug(
|
|
206
|
-
f"Circuit breaker [{self.config.name}] timeout after "
|
|
207
|
-
f"{self.config.timeout_seconds}s"
|
|
208
|
-
)
|
|
209
|
-
if fallback is not None:
|
|
210
|
-
return fallback
|
|
211
|
-
raise
|
|
212
|
-
|
|
213
|
-
except Exception as e:
|
|
214
|
-
async with self._lock:
|
|
215
|
-
# CRITICAL FIX: Detect quota errors and open circuit immediately
|
|
216
|
-
error_msg = str(e).lower()
|
|
217
|
-
error_type = type(e).__name__
|
|
218
|
-
|
|
219
|
-
# Check if this is a quota error (429 status, quota exceeded message, etc.)
|
|
220
|
-
is_quota_error = (
|
|
221
|
-
"quota exceeded" in error_msg
|
|
222
|
-
or "429" in error_msg
|
|
223
|
-
or error_type == "CircuitBreakerOpen" and "quota" in error_msg
|
|
224
|
-
)
|
|
225
|
-
|
|
226
|
-
if is_quota_error:
|
|
227
|
-
# Immediately open circuit on quota error - no need to wait for threshold
|
|
228
|
-
if self._stats.state == CircuitState.CLOSED:
|
|
229
|
-
self._stats.state = CircuitState.OPEN
|
|
230
|
-
self._stats.last_state_change = time.time()
|
|
231
|
-
logger.warning(
|
|
232
|
-
f"Circuit breaker [{self.config.name}] OPENED immediately due to quota error. "
|
|
233
|
-
f"Subsequent requests will be rejected without API calls."
|
|
234
|
-
)
|
|
235
|
-
# Mark quota as exceeded globally
|
|
236
|
-
try:
|
|
237
|
-
from .backup_client import _mark_context7_quota_exceeded
|
|
238
|
-
quota_msg = str(e) if "quota" in error_msg.lower() else "Quota exceeded"
|
|
239
|
-
_mark_context7_quota_exceeded(quota_msg)
|
|
240
|
-
except Exception:
|
|
241
|
-
pass # If quota marking fails, continue
|
|
242
|
-
|
|
243
|
-
await self._record_failure()
|
|
244
|
-
|
|
245
|
-
logger.debug(
|
|
246
|
-
f"Circuit breaker [{self.config.name}] call failed: {e}"
|
|
247
|
-
)
|
|
248
|
-
if fallback is not None:
|
|
249
|
-
return fallback
|
|
250
|
-
raise
|
|
251
|
-
|
|
252
|
-
def reset(self) -> None:
|
|
253
|
-
"""Reset circuit breaker to initial state."""
|
|
254
|
-
self._stats = CircuitBreakerStats()
|
|
255
|
-
logger.info(f"Circuit breaker [{self.config.name}] reset to CLOSED")
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
class CircuitBreakerOpen(Exception):
|
|
259
|
-
"""Exception raised when circuit breaker is open."""
|
|
260
|
-
|
|
261
|
-
pass
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
class ParallelExecutor:
|
|
265
|
-
"""
|
|
266
|
-
Parallel executor with circuit breaker and bounded concurrency.
|
|
267
|
-
|
|
268
|
-
2025 Pattern: Bounded parallelism with fail-fast semantics.
|
|
269
|
-
"""
|
|
270
|
-
|
|
271
|
-
def __init__(
|
|
272
|
-
self,
|
|
273
|
-
max_concurrency: int = 5,
|
|
274
|
-
circuit_breaker: CircuitBreaker | None = None,
|
|
275
|
-
):
|
|
276
|
-
"""
|
|
277
|
-
Initialize parallel executor.
|
|
278
|
-
|
|
279
|
-
Args:
|
|
280
|
-
max_concurrency: Maximum concurrent operations
|
|
281
|
-
circuit_breaker: Optional shared circuit breaker
|
|
282
|
-
"""
|
|
283
|
-
self.max_concurrency = max_concurrency
|
|
284
|
-
self._semaphore = asyncio.Semaphore(max_concurrency)
|
|
285
|
-
self.circuit_breaker = circuit_breaker or CircuitBreaker()
|
|
286
|
-
|
|
287
|
-
async def execute_all(
|
|
288
|
-
self,
|
|
289
|
-
items: list[Any],
|
|
290
|
-
func: Callable[[Any], Any],
|
|
291
|
-
fallback: Any = None,
|
|
292
|
-
) -> list[Any]:
|
|
293
|
-
"""
|
|
294
|
-
Execute function for all items in parallel with circuit breaker.
|
|
295
|
-
|
|
296
|
-
Args:
|
|
297
|
-
items: List of items to process
|
|
298
|
-
func: Async function to apply to each item
|
|
299
|
-
fallback: Fallback value for failed items
|
|
300
|
-
|
|
301
|
-
Returns:
|
|
302
|
-
List of results (in same order as items)
|
|
303
|
-
"""
|
|
304
|
-
|
|
305
|
-
async def execute_one(item: Any) -> Any:
|
|
306
|
-
async with self._semaphore:
|
|
307
|
-
return await self.circuit_breaker.call(
|
|
308
|
-
func, item, fallback=fallback
|
|
309
|
-
)
|
|
310
|
-
|
|
311
|
-
tasks = [execute_one(item) for item in items]
|
|
312
|
-
results = await asyncio.gather(*tasks, return_exceptions=True)
|
|
313
|
-
|
|
314
|
-
# Replace exceptions with fallback
|
|
315
|
-
return [
|
|
316
|
-
fallback if isinstance(r, Exception) else r
|
|
317
|
-
for r in results
|
|
318
|
-
]
|
|
319
|
-
|
|
320
|
-
@property
|
|
321
|
-
def stats(self) -> dict[str, Any]:
|
|
322
|
-
"""Get executor statistics."""
|
|
323
|
-
return {
|
|
324
|
-
"max_concurrency": self.max_concurrency,
|
|
325
|
-
"circuit_breaker": {
|
|
326
|
-
"state": self.circuit_breaker.state.value,
|
|
327
|
-
"stats": {
|
|
328
|
-
"total_requests": self.circuit_breaker.stats.total_requests,
|
|
329
|
-
"successful_requests": self.circuit_breaker.stats.successful_requests,
|
|
330
|
-
"failed_requests": self.circuit_breaker.stats.failed_requests,
|
|
331
|
-
"rejected_requests": self.circuit_breaker.stats.rejected_requests,
|
|
332
|
-
},
|
|
333
|
-
},
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
# Global circuit breaker instance for Context7 operations
|
|
338
|
-
_context7_circuit_breaker: CircuitBreaker | None = None
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
def get_context7_circuit_breaker() -> CircuitBreaker:
|
|
342
|
-
"""Get the global Context7 circuit breaker."""
|
|
343
|
-
global _context7_circuit_breaker
|
|
344
|
-
if _context7_circuit_breaker is None:
|
|
345
|
-
_context7_circuit_breaker = CircuitBreaker(
|
|
346
|
-
CircuitBreakerConfig(
|
|
347
|
-
name="context7",
|
|
348
|
-
failure_threshold=3,
|
|
349
|
-
success_threshold=2,
|
|
350
|
-
timeout_seconds=5.0,
|
|
351
|
-
reset_timeout_seconds=30.0,
|
|
352
|
-
)
|
|
353
|
-
)
|
|
354
|
-
return _context7_circuit_breaker
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
def get_parallel_executor(
|
|
358
|
-
max_concurrency: int = 5,
|
|
359
|
-
circuit_breaker: CircuitBreaker | None = None,
|
|
360
|
-
) -> ParallelExecutor:
|
|
361
|
-
"""
|
|
362
|
-
Get a parallel executor for Context7 operations.
|
|
363
|
-
|
|
364
|
-
Args:
|
|
365
|
-
max_concurrency: Maximum concurrent operations (default: 5)
|
|
366
|
-
circuit_breaker: Optional circuit breaker (uses global if not provided)
|
|
367
|
-
|
|
368
|
-
Returns:
|
|
369
|
-
ParallelExecutor instance
|
|
370
|
-
"""
|
|
371
|
-
if circuit_breaker is None:
|
|
372
|
-
circuit_breaker = get_context7_circuit_breaker()
|
|
373
|
-
return ParallelExecutor(
|
|
374
|
-
max_concurrency=max_concurrency,
|
|
375
|
-
circuit_breaker=circuit_breaker,
|
|
376
|
-
)
|
|
1
|
+
"""
|
|
2
|
+
Circuit Breaker Pattern for Context7 Operations.
|
|
3
|
+
|
|
4
|
+
Prevents cascading failures by "opening" the circuit when failures exceed a threshold.
|
|
5
|
+
2025 Architecture Pattern: Resilient distributed systems with fail-fast semantics.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import asyncio
|
|
11
|
+
import logging
|
|
12
|
+
import time
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
from enum import Enum
|
|
15
|
+
from typing import Any, Callable, TypeVar
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
T = TypeVar("T")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class CircuitState(Enum):
|
|
23
|
+
"""Circuit breaker states."""
|
|
24
|
+
|
|
25
|
+
CLOSED = "closed" # Normal operation, requests flow through
|
|
26
|
+
OPEN = "open" # Circuit tripped, requests fail fast
|
|
27
|
+
HALF_OPEN = "half_open" # Testing if service recovered
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class CircuitBreakerConfig:
|
|
32
|
+
"""Circuit breaker configuration."""
|
|
33
|
+
|
|
34
|
+
failure_threshold: int = 3 # Failures before opening circuit
|
|
35
|
+
success_threshold: int = 2 # Successes in half-open to close circuit
|
|
36
|
+
timeout_seconds: float = 5.0 # Request timeout
|
|
37
|
+
reset_timeout_seconds: float = 30.0 # Time before half-open from open
|
|
38
|
+
name: str = "context7"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class CircuitBreakerStats:
|
|
43
|
+
"""Circuit breaker statistics."""
|
|
44
|
+
|
|
45
|
+
total_requests: int = 0
|
|
46
|
+
successful_requests: int = 0
|
|
47
|
+
failed_requests: int = 0
|
|
48
|
+
rejected_requests: int = 0 # Requests rejected due to open circuit
|
|
49
|
+
consecutive_failures: int = 0
|
|
50
|
+
consecutive_successes: int = 0
|
|
51
|
+
state: CircuitState = CircuitState.CLOSED
|
|
52
|
+
last_failure_time: float | None = None
|
|
53
|
+
last_state_change: float | None = None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class CircuitBreaker:
|
|
57
|
+
"""
|
|
58
|
+
Circuit Breaker for resilient Context7 operations.
|
|
59
|
+
|
|
60
|
+
States:
|
|
61
|
+
- CLOSED: Normal operation, requests flow through
|
|
62
|
+
- OPEN: Circuit tripped, requests fail fast (no actual call)
|
|
63
|
+
- HALF_OPEN: Testing recovery, limited requests allowed
|
|
64
|
+
|
|
65
|
+
Transitions:
|
|
66
|
+
- CLOSED -> OPEN: When failures >= failure_threshold
|
|
67
|
+
- OPEN -> HALF_OPEN: After reset_timeout_seconds
|
|
68
|
+
- HALF_OPEN -> CLOSED: When successes >= success_threshold
|
|
69
|
+
- HALF_OPEN -> OPEN: On any failure
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
def __init__(self, config: CircuitBreakerConfig | None = None):
|
|
73
|
+
self.config = config or CircuitBreakerConfig()
|
|
74
|
+
self._stats = CircuitBreakerStats()
|
|
75
|
+
self._lock = asyncio.Lock()
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def state(self) -> CircuitState:
|
|
79
|
+
"""Get current circuit state."""
|
|
80
|
+
return self._stats.state
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def is_closed(self) -> bool:
|
|
84
|
+
"""Check if circuit is closed (normal operation)."""
|
|
85
|
+
return self._stats.state == CircuitState.CLOSED
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def is_open(self) -> bool:
|
|
89
|
+
"""Check if circuit is open (failing fast)."""
|
|
90
|
+
return self._stats.state == CircuitState.OPEN
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def stats(self) -> CircuitBreakerStats:
|
|
94
|
+
"""Get circuit breaker statistics."""
|
|
95
|
+
return self._stats
|
|
96
|
+
|
|
97
|
+
async def _check_state_transition(self) -> None:
|
|
98
|
+
"""Check if state should transition (OPEN -> HALF_OPEN)."""
|
|
99
|
+
if self._stats.state == CircuitState.OPEN:
|
|
100
|
+
if self._stats.last_failure_time is not None:
|
|
101
|
+
elapsed = time.time() - self._stats.last_failure_time
|
|
102
|
+
if elapsed >= self.config.reset_timeout_seconds:
|
|
103
|
+
self._stats.state = CircuitState.HALF_OPEN
|
|
104
|
+
self._stats.consecutive_failures = 0
|
|
105
|
+
self._stats.consecutive_successes = 0
|
|
106
|
+
self._stats.last_state_change = time.time()
|
|
107
|
+
logger.info(
|
|
108
|
+
f"Circuit breaker [{self.config.name}] transitioned to HALF_OPEN "
|
|
109
|
+
f"after {elapsed:.1f}s"
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
async def _record_success(self) -> None:
|
|
113
|
+
"""Record successful request."""
|
|
114
|
+
self._stats.total_requests += 1
|
|
115
|
+
self._stats.successful_requests += 1
|
|
116
|
+
self._stats.consecutive_successes += 1
|
|
117
|
+
self._stats.consecutive_failures = 0
|
|
118
|
+
|
|
119
|
+
if self._stats.state == CircuitState.HALF_OPEN:
|
|
120
|
+
if self._stats.consecutive_successes >= self.config.success_threshold:
|
|
121
|
+
self._stats.state = CircuitState.CLOSED
|
|
122
|
+
self._stats.last_state_change = time.time()
|
|
123
|
+
logger.info(
|
|
124
|
+
f"Circuit breaker [{self.config.name}] CLOSED after "
|
|
125
|
+
f"{self._stats.consecutive_successes} consecutive successes"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
async def _record_failure(self) -> None:
|
|
129
|
+
"""Record failed request."""
|
|
130
|
+
self._stats.total_requests += 1
|
|
131
|
+
self._stats.failed_requests += 1
|
|
132
|
+
self._stats.consecutive_failures += 1
|
|
133
|
+
self._stats.consecutive_successes = 0
|
|
134
|
+
self._stats.last_failure_time = time.time()
|
|
135
|
+
|
|
136
|
+
if self._stats.state == CircuitState.HALF_OPEN:
|
|
137
|
+
# Any failure in half-open reopens the circuit
|
|
138
|
+
self._stats.state = CircuitState.OPEN
|
|
139
|
+
self._stats.last_state_change = time.time()
|
|
140
|
+
logger.warning(
|
|
141
|
+
f"Circuit breaker [{self.config.name}] OPENED from HALF_OPEN due to failure"
|
|
142
|
+
)
|
|
143
|
+
elif self._stats.state == CircuitState.CLOSED:
|
|
144
|
+
if self._stats.consecutive_failures >= self.config.failure_threshold:
|
|
145
|
+
self._stats.state = CircuitState.OPEN
|
|
146
|
+
self._stats.last_state_change = time.time()
|
|
147
|
+
logger.warning(
|
|
148
|
+
f"Circuit breaker [{self.config.name}] OPENED after "
|
|
149
|
+
f"{self._stats.consecutive_failures} consecutive failures"
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
async def _record_rejection(self) -> None:
|
|
153
|
+
"""Record rejected request (circuit open)."""
|
|
154
|
+
self._stats.rejected_requests += 1
|
|
155
|
+
|
|
156
|
+
async def call(
|
|
157
|
+
self,
|
|
158
|
+
func: Callable[..., Any],
|
|
159
|
+
*args: Any,
|
|
160
|
+
fallback: Any = None,
|
|
161
|
+
**kwargs: Any,
|
|
162
|
+
) -> Any:
|
|
163
|
+
"""
|
|
164
|
+
Execute function with circuit breaker protection.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
func: Async function to execute
|
|
168
|
+
*args: Positional arguments for func
|
|
169
|
+
fallback: Value to return if circuit is open or call fails
|
|
170
|
+
**kwargs: Keyword arguments for func
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
Function result or fallback value
|
|
174
|
+
|
|
175
|
+
Raises:
|
|
176
|
+
CircuitBreakerOpen: If circuit is open and no fallback provided
|
|
177
|
+
"""
|
|
178
|
+
async with self._lock:
|
|
179
|
+
await self._check_state_transition()
|
|
180
|
+
|
|
181
|
+
if self._stats.state == CircuitState.OPEN:
|
|
182
|
+
await self._record_rejection()
|
|
183
|
+
logger.debug(
|
|
184
|
+
f"Circuit breaker [{self.config.name}] OPEN - fast failing"
|
|
185
|
+
)
|
|
186
|
+
if fallback is not None:
|
|
187
|
+
return fallback
|
|
188
|
+
raise CircuitBreakerOpen(
|
|
189
|
+
f"Circuit breaker [{self.config.name}] is OPEN"
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
# Execute with timeout
|
|
193
|
+
try:
|
|
194
|
+
result = await asyncio.wait_for(
|
|
195
|
+
func(*args, **kwargs),
|
|
196
|
+
timeout=self.config.timeout_seconds,
|
|
197
|
+
)
|
|
198
|
+
async with self._lock:
|
|
199
|
+
await self._record_success()
|
|
200
|
+
return result
|
|
201
|
+
|
|
202
|
+
except asyncio.TimeoutError:
|
|
203
|
+
async with self._lock:
|
|
204
|
+
await self._record_failure()
|
|
205
|
+
logger.debug(
|
|
206
|
+
f"Circuit breaker [{self.config.name}] timeout after "
|
|
207
|
+
f"{self.config.timeout_seconds}s"
|
|
208
|
+
)
|
|
209
|
+
if fallback is not None:
|
|
210
|
+
return fallback
|
|
211
|
+
raise
|
|
212
|
+
|
|
213
|
+
except Exception as e:
|
|
214
|
+
async with self._lock:
|
|
215
|
+
# CRITICAL FIX: Detect quota errors and open circuit immediately
|
|
216
|
+
error_msg = str(e).lower()
|
|
217
|
+
error_type = type(e).__name__
|
|
218
|
+
|
|
219
|
+
# Check if this is a quota error (429 status, quota exceeded message, etc.)
|
|
220
|
+
is_quota_error = (
|
|
221
|
+
"quota exceeded" in error_msg
|
|
222
|
+
or "429" in error_msg
|
|
223
|
+
or error_type == "CircuitBreakerOpen" and "quota" in error_msg
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
if is_quota_error:
|
|
227
|
+
# Immediately open circuit on quota error - no need to wait for threshold
|
|
228
|
+
if self._stats.state == CircuitState.CLOSED:
|
|
229
|
+
self._stats.state = CircuitState.OPEN
|
|
230
|
+
self._stats.last_state_change = time.time()
|
|
231
|
+
logger.warning(
|
|
232
|
+
f"Circuit breaker [{self.config.name}] OPENED immediately due to quota error. "
|
|
233
|
+
f"Subsequent requests will be rejected without API calls."
|
|
234
|
+
)
|
|
235
|
+
# Mark quota as exceeded globally
|
|
236
|
+
try:
|
|
237
|
+
from .backup_client import _mark_context7_quota_exceeded
|
|
238
|
+
quota_msg = str(e) if "quota" in error_msg.lower() else "Quota exceeded"
|
|
239
|
+
_mark_context7_quota_exceeded(quota_msg)
|
|
240
|
+
except Exception:
|
|
241
|
+
pass # If quota marking fails, continue
|
|
242
|
+
|
|
243
|
+
await self._record_failure()
|
|
244
|
+
|
|
245
|
+
logger.debug(
|
|
246
|
+
f"Circuit breaker [{self.config.name}] call failed: {e}"
|
|
247
|
+
)
|
|
248
|
+
if fallback is not None:
|
|
249
|
+
return fallback
|
|
250
|
+
raise
|
|
251
|
+
|
|
252
|
+
def reset(self) -> None:
|
|
253
|
+
"""Reset circuit breaker to initial state."""
|
|
254
|
+
self._stats = CircuitBreakerStats()
|
|
255
|
+
logger.info(f"Circuit breaker [{self.config.name}] reset to CLOSED")
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
class CircuitBreakerOpen(Exception):
|
|
259
|
+
"""Exception raised when circuit breaker is open."""
|
|
260
|
+
|
|
261
|
+
pass
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
class ParallelExecutor:
|
|
265
|
+
"""
|
|
266
|
+
Parallel executor with circuit breaker and bounded concurrency.
|
|
267
|
+
|
|
268
|
+
2025 Pattern: Bounded parallelism with fail-fast semantics.
|
|
269
|
+
"""
|
|
270
|
+
|
|
271
|
+
def __init__(
|
|
272
|
+
self,
|
|
273
|
+
max_concurrency: int = 5,
|
|
274
|
+
circuit_breaker: CircuitBreaker | None = None,
|
|
275
|
+
):
|
|
276
|
+
"""
|
|
277
|
+
Initialize parallel executor.
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
max_concurrency: Maximum concurrent operations
|
|
281
|
+
circuit_breaker: Optional shared circuit breaker
|
|
282
|
+
"""
|
|
283
|
+
self.max_concurrency = max_concurrency
|
|
284
|
+
self._semaphore = asyncio.Semaphore(max_concurrency)
|
|
285
|
+
self.circuit_breaker = circuit_breaker or CircuitBreaker()
|
|
286
|
+
|
|
287
|
+
async def execute_all(
|
|
288
|
+
self,
|
|
289
|
+
items: list[Any],
|
|
290
|
+
func: Callable[[Any], Any],
|
|
291
|
+
fallback: Any = None,
|
|
292
|
+
) -> list[Any]:
|
|
293
|
+
"""
|
|
294
|
+
Execute function for all items in parallel with circuit breaker.
|
|
295
|
+
|
|
296
|
+
Args:
|
|
297
|
+
items: List of items to process
|
|
298
|
+
func: Async function to apply to each item
|
|
299
|
+
fallback: Fallback value for failed items
|
|
300
|
+
|
|
301
|
+
Returns:
|
|
302
|
+
List of results (in same order as items)
|
|
303
|
+
"""
|
|
304
|
+
|
|
305
|
+
async def execute_one(item: Any) -> Any:
|
|
306
|
+
async with self._semaphore:
|
|
307
|
+
return await self.circuit_breaker.call(
|
|
308
|
+
func, item, fallback=fallback
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
tasks = [execute_one(item) for item in items]
|
|
312
|
+
results = await asyncio.gather(*tasks, return_exceptions=True)
|
|
313
|
+
|
|
314
|
+
# Replace exceptions with fallback
|
|
315
|
+
return [
|
|
316
|
+
fallback if isinstance(r, Exception) else r
|
|
317
|
+
for r in results
|
|
318
|
+
]
|
|
319
|
+
|
|
320
|
+
@property
|
|
321
|
+
def stats(self) -> dict[str, Any]:
|
|
322
|
+
"""Get executor statistics."""
|
|
323
|
+
return {
|
|
324
|
+
"max_concurrency": self.max_concurrency,
|
|
325
|
+
"circuit_breaker": {
|
|
326
|
+
"state": self.circuit_breaker.state.value,
|
|
327
|
+
"stats": {
|
|
328
|
+
"total_requests": self.circuit_breaker.stats.total_requests,
|
|
329
|
+
"successful_requests": self.circuit_breaker.stats.successful_requests,
|
|
330
|
+
"failed_requests": self.circuit_breaker.stats.failed_requests,
|
|
331
|
+
"rejected_requests": self.circuit_breaker.stats.rejected_requests,
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
# Global circuit breaker instance for Context7 operations
|
|
338
|
+
_context7_circuit_breaker: CircuitBreaker | None = None
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def get_context7_circuit_breaker() -> CircuitBreaker:
|
|
342
|
+
"""Get the global Context7 circuit breaker."""
|
|
343
|
+
global _context7_circuit_breaker
|
|
344
|
+
if _context7_circuit_breaker is None:
|
|
345
|
+
_context7_circuit_breaker = CircuitBreaker(
|
|
346
|
+
CircuitBreakerConfig(
|
|
347
|
+
name="context7",
|
|
348
|
+
failure_threshold=3,
|
|
349
|
+
success_threshold=2,
|
|
350
|
+
timeout_seconds=5.0,
|
|
351
|
+
reset_timeout_seconds=30.0,
|
|
352
|
+
)
|
|
353
|
+
)
|
|
354
|
+
return _context7_circuit_breaker
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def get_parallel_executor(
|
|
358
|
+
max_concurrency: int = 5,
|
|
359
|
+
circuit_breaker: CircuitBreaker | None = None,
|
|
360
|
+
) -> ParallelExecutor:
|
|
361
|
+
"""
|
|
362
|
+
Get a parallel executor for Context7 operations.
|
|
363
|
+
|
|
364
|
+
Args:
|
|
365
|
+
max_concurrency: Maximum concurrent operations (default: 5)
|
|
366
|
+
circuit_breaker: Optional circuit breaker (uses global if not provided)
|
|
367
|
+
|
|
368
|
+
Returns:
|
|
369
|
+
ParallelExecutor instance
|
|
370
|
+
"""
|
|
371
|
+
if circuit_breaker is None:
|
|
372
|
+
circuit_breaker = get_context7_circuit_breaker()
|
|
373
|
+
return ParallelExecutor(
|
|
374
|
+
max_concurrency=max_concurrency,
|
|
375
|
+
circuit_breaker=circuit_breaker,
|
|
376
|
+
)
|