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,869 +1,869 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Context7 Agent Integration - Helper functions for agents to use Context7 KB.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
import logging
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
from typing import Any
|
|
8
|
-
|
|
9
|
-
from ..core.config import ProjectConfig
|
|
10
|
-
from ..mcp.gateway import MCPGateway
|
|
11
|
-
from .analytics import Analytics
|
|
12
|
-
from .cache_structure import CacheStructure
|
|
13
|
-
from .credential_validation import validate_context7_credentials
|
|
14
|
-
from .doc_manager import Context7DocManager
|
|
15
|
-
from .fuzzy_matcher import FuzzyMatcher
|
|
16
|
-
from .kb_cache import KBCache
|
|
17
|
-
from .library_detector import LibraryDetector
|
|
18
|
-
from .lookup import KBLookup
|
|
19
|
-
from .metadata import MetadataManager
|
|
20
|
-
|
|
21
|
-
logger = logging.getLogger(__name__)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class Context7AgentHelper:
|
|
25
|
-
"""
|
|
26
|
-
Helper class for agents to easily access Context7 KB.
|
|
27
|
-
Provides simplified interface for common operations.
|
|
28
|
-
"""
|
|
29
|
-
|
|
30
|
-
def __init__(
|
|
31
|
-
self,
|
|
32
|
-
config: ProjectConfig,
|
|
33
|
-
mcp_gateway: MCPGateway | None = None,
|
|
34
|
-
project_root: Path | None = None,
|
|
35
|
-
):
|
|
36
|
-
"""
|
|
37
|
-
Initialize Context7 helper for an agent.
|
|
38
|
-
|
|
39
|
-
Args:
|
|
40
|
-
config: ProjectConfig instance
|
|
41
|
-
mcp_gateway: Optional MCPGateway instance
|
|
42
|
-
project_root: Optional project root path
|
|
43
|
-
"""
|
|
44
|
-
if project_root is None:
|
|
45
|
-
project_root = Path.cwd()
|
|
46
|
-
|
|
47
|
-
# #region agent log
|
|
48
|
-
from ..core.debug_logger import write_debug_log
|
|
49
|
-
# Extract values before JSON serialization to handle MagicMock objects in tests
|
|
50
|
-
config_exists = config is not None
|
|
51
|
-
context7_config_exists = False
|
|
52
|
-
context7_enabled = False
|
|
53
|
-
if config and hasattr(config, 'context7'):
|
|
54
|
-
try:
|
|
55
|
-
context7_config = config.context7
|
|
56
|
-
context7_config_exists = context7_config is not None
|
|
57
|
-
if context7_config_exists and hasattr(context7_config, 'enabled'):
|
|
58
|
-
context7_enabled = bool(context7_config.enabled)
|
|
59
|
-
except (AttributeError, TypeError):
|
|
60
|
-
pass
|
|
61
|
-
|
|
62
|
-
write_debug_log(
|
|
63
|
-
{
|
|
64
|
-
"sessionId": "debug-session",
|
|
65
|
-
"runId": "run1",
|
|
66
|
-
"hypothesisId": "C",
|
|
67
|
-
"message": "Context7AgentHelper __init__ called",
|
|
68
|
-
"data": {
|
|
69
|
-
"config_exists": config_exists,
|
|
70
|
-
"context7_config_exists": context7_config_exists,
|
|
71
|
-
"context7_enabled": context7_enabled
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
project_root=project_root,
|
|
75
|
-
location="context7/agent_integration.py:__init__:entry",
|
|
76
|
-
)
|
|
77
|
-
# #endregion
|
|
78
|
-
|
|
79
|
-
# Check if Context7 is enabled
|
|
80
|
-
context7_config = config.context7
|
|
81
|
-
if not context7_config or not context7_config.enabled:
|
|
82
|
-
self.enabled = False
|
|
83
|
-
# #region agent log
|
|
84
|
-
write_debug_log(
|
|
85
|
-
{
|
|
86
|
-
"sessionId": "debug-session",
|
|
87
|
-
"runId": "run1",
|
|
88
|
-
"hypothesisId": "C",
|
|
89
|
-
"message": "Context7 disabled in config",
|
|
90
|
-
"data": {"context7_config": context7_config is not None, "enabled": context7_config.enabled if context7_config else False},
|
|
91
|
-
},
|
|
92
|
-
project_root=project_root,
|
|
93
|
-
location="context7/agent_integration.py:__init__:disabled",
|
|
94
|
-
)
|
|
95
|
-
# #endregion
|
|
96
|
-
return
|
|
97
|
-
|
|
98
|
-
# Ensure API key is available (loads from encrypted storage if needed)
|
|
99
|
-
# This ensures agents don't need to manually pass the API key
|
|
100
|
-
try:
|
|
101
|
-
from .backup_client import _ensure_context7_api_key
|
|
102
|
-
api_key_result = _ensure_context7_api_key()
|
|
103
|
-
# #region agent log
|
|
104
|
-
write_debug_log(
|
|
105
|
-
{
|
|
106
|
-
"sessionId": "debug-session",
|
|
107
|
-
"runId": "run1",
|
|
108
|
-
"hypothesisId": "A",
|
|
109
|
-
"message": "API key ensured",
|
|
110
|
-
"data": {"api_key_available": api_key_result is not None, "key_length": len(api_key_result) if api_key_result else 0},
|
|
111
|
-
},
|
|
112
|
-
project_root=project_root,
|
|
113
|
-
location="context7/agent_integration.py:__init__:api_key_ensured",
|
|
114
|
-
)
|
|
115
|
-
# #endregion
|
|
116
|
-
except Exception as e:
|
|
117
|
-
logger.debug(f"Could not ensure Context7 API key availability: {e}")
|
|
118
|
-
# #region agent log
|
|
119
|
-
write_debug_log(
|
|
120
|
-
{
|
|
121
|
-
"sessionId": "debug-session",
|
|
122
|
-
"runId": "run1",
|
|
123
|
-
"hypothesisId": "A",
|
|
124
|
-
"message": "API key ensure failed",
|
|
125
|
-
"data": {"error": str(e)},
|
|
126
|
-
},
|
|
127
|
-
project_root=project_root,
|
|
128
|
-
location="context7/agent_integration.py:__init__:api_key_error",
|
|
129
|
-
)
|
|
130
|
-
# #endregion
|
|
131
|
-
|
|
132
|
-
# Validate credentials (non-blocking, only warn if Context7 is actually needed)
|
|
133
|
-
# Context7 is optional - only warn if it's explicitly required for the operation
|
|
134
|
-
try:
|
|
135
|
-
cred_result = validate_context7_credentials(mcp_gateway=mcp_gateway)
|
|
136
|
-
# Only log as debug if MCP is available (Context7 works via MCP)
|
|
137
|
-
# Only warn if both MCP and API key are unavailable
|
|
138
|
-
if not cred_result.valid:
|
|
139
|
-
if mcp_gateway:
|
|
140
|
-
# MCP is available, so Context7 should work via MCP
|
|
141
|
-
# Only log as debug to avoid noise
|
|
142
|
-
logger.debug(
|
|
143
|
-
f"Context7 API key not configured, but MCP gateway is available. "
|
|
144
|
-
f"Context7 will work via MCP: {cred_result.error}"
|
|
145
|
-
)
|
|
146
|
-
else:
|
|
147
|
-
# No MCP and no API key - warn only if Context7 is actually needed
|
|
148
|
-
# For now, we'll still warn but at debug level to reduce noise
|
|
149
|
-
logger.debug(
|
|
150
|
-
f"Context7 credentials not configured: {cred_result.error}. "
|
|
151
|
-
f"Context7 features will be limited. {cred_result.actionable_message}"
|
|
152
|
-
)
|
|
153
|
-
except Exception as e:
|
|
154
|
-
logger.debug(f"Context7 credential validation error: {e}", exc_info=True)
|
|
155
|
-
|
|
156
|
-
# Wrap initialization in try-except to prevent Context7 failures from breaking agents
|
|
157
|
-
try:
|
|
158
|
-
self.enabled = True
|
|
159
|
-
self.config = context7_config
|
|
160
|
-
self.project_root = project_root
|
|
161
|
-
# #region agent log
|
|
162
|
-
write_debug_log(
|
|
163
|
-
{
|
|
164
|
-
"sessionId": "debug-session",
|
|
165
|
-
"runId": "run1",
|
|
166
|
-
"hypothesisId": "C",
|
|
167
|
-
"message": "Context7AgentHelper enabled=True",
|
|
168
|
-
"data": {"enabled": True},
|
|
169
|
-
},
|
|
170
|
-
project_root=project_root,
|
|
171
|
-
location="context7/agent_integration.py:__init__:enabled",
|
|
172
|
-
)
|
|
173
|
-
# #endregion
|
|
174
|
-
|
|
175
|
-
# Initialize cache structure
|
|
176
|
-
# Defensive: if knowledge_base.location is not a string (e.g. MagicMock in tests),
|
|
177
|
-
# use default to avoid creating directories with mock reprs (e.g. "MagicMock/").
|
|
178
|
-
kb = getattr(context7_config, "knowledge_base", None)
|
|
179
|
-
loc = getattr(kb, "location", None) if kb is not None else None
|
|
180
|
-
if not isinstance(loc, str):
|
|
181
|
-
loc = ".tapps-agents/kb/context7-cache"
|
|
182
|
-
cache_root = project_root / loc
|
|
183
|
-
self.cache_structure = CacheStructure(cache_root)
|
|
184
|
-
self.cache_structure.initialize()
|
|
185
|
-
|
|
186
|
-
# Initialize components
|
|
187
|
-
self.metadata_manager = MetadataManager(self.cache_structure)
|
|
188
|
-
self.kb_cache = KBCache(self.cache_structure.cache_root, self.metadata_manager)
|
|
189
|
-
self.fuzzy_matcher = FuzzyMatcher(threshold=0.7)
|
|
190
|
-
self.analytics = Analytics(self.cache_structure, self.metadata_manager)
|
|
191
|
-
|
|
192
|
-
# Initialize KB lookup with MCP Gateway
|
|
193
|
-
self.mcp_gateway = mcp_gateway
|
|
194
|
-
self.kb_lookup = KBLookup(
|
|
195
|
-
kb_cache=self.kb_cache, mcp_gateway=mcp_gateway, fuzzy_threshold=0.7
|
|
196
|
-
)
|
|
197
|
-
|
|
198
|
-
# Initialize library detector for Option 3 quality uplift
|
|
199
|
-
self.library_detector = LibraryDetector(project_root=project_root)
|
|
200
|
-
|
|
201
|
-
# Initialize documentation manager for auto-save and offline access (Phase 7.1)
|
|
202
|
-
self.doc_manager = Context7DocManager(
|
|
203
|
-
cache_root=self.cache_structure.cache_root,
|
|
204
|
-
project_root=project_root,
|
|
205
|
-
)
|
|
206
|
-
|
|
207
|
-
# #region agent log
|
|
208
|
-
write_debug_log(
|
|
209
|
-
{
|
|
210
|
-
"sessionId": "debug-session",
|
|
211
|
-
"runId": "run1",
|
|
212
|
-
"hypothesisId": "C",
|
|
213
|
-
"message": "Library detector initialized",
|
|
214
|
-
"data": {"library_detector_created": self.library_detector is not None},
|
|
215
|
-
},
|
|
216
|
-
project_root=project_root,
|
|
217
|
-
location="context7/agent_integration.py:__init__:library_detector_init",
|
|
218
|
-
)
|
|
219
|
-
# #endregion
|
|
220
|
-
except Exception as e:
|
|
221
|
-
# If Context7 initialization fails, disable it gracefully
|
|
222
|
-
# This prevents Context7 failures from breaking agent initialization
|
|
223
|
-
logger.warning(
|
|
224
|
-
f"Context7 initialization failed, disabling Context7 features: {e}. "
|
|
225
|
-
f"Agents will continue to work without Context7."
|
|
226
|
-
)
|
|
227
|
-
self.enabled = False
|
|
228
|
-
# Set minimal attributes to prevent AttributeError
|
|
229
|
-
self.config = context7_config
|
|
230
|
-
self.project_root = project_root
|
|
231
|
-
self.cache_structure = None
|
|
232
|
-
self.metadata_manager = None
|
|
233
|
-
self.kb_cache = None
|
|
234
|
-
self.fuzzy_matcher = None
|
|
235
|
-
self.analytics = None
|
|
236
|
-
self.mcp_gateway = mcp_gateway
|
|
237
|
-
self.kb_lookup = None
|
|
238
|
-
self.library_detector = None
|
|
239
|
-
self.doc_manager = None # Phase 7.1: Doc manager disabled on init failure
|
|
240
|
-
# #region agent log
|
|
241
|
-
write_debug_log(
|
|
242
|
-
{
|
|
243
|
-
"sessionId": "debug-session",
|
|
244
|
-
"runId": "run1",
|
|
245
|
-
"hypothesisId": "C",
|
|
246
|
-
"message": "Context7 initialization failed, disabled",
|
|
247
|
-
"data": {"error": str(e), "enabled": False},
|
|
248
|
-
},
|
|
249
|
-
project_root=project_root,
|
|
250
|
-
location="context7/agent_integration.py:__init__:init_failed",
|
|
251
|
-
)
|
|
252
|
-
# #endregion
|
|
253
|
-
|
|
254
|
-
async def get_documentation(
|
|
255
|
-
self, library: str, topic: str | None = None, use_fuzzy_match: bool = True
|
|
256
|
-
) -> dict[str, Any] | None:
|
|
257
|
-
"""
|
|
258
|
-
Get documentation for a library/topic.
|
|
259
|
-
|
|
260
|
-
Args:
|
|
261
|
-
library: Library name (e.g., "react", "fastapi")
|
|
262
|
-
topic: Optional topic name (e.g., "hooks", "routing")
|
|
263
|
-
use_fuzzy_match: Whether to use fuzzy matching if exact match not found
|
|
264
|
-
|
|
265
|
-
Returns:
|
|
266
|
-
Dictionary with documentation content, or None if not found
|
|
267
|
-
"""
|
|
268
|
-
# #region agent log
|
|
269
|
-
from ..core.debug_logger import write_debug_log
|
|
270
|
-
write_debug_log(
|
|
271
|
-
{
|
|
272
|
-
"sessionId": "debug-session",
|
|
273
|
-
"runId": "run1",
|
|
274
|
-
"hypothesisId": "E",
|
|
275
|
-
"message": "get_documentation called",
|
|
276
|
-
"data": {"library": library, "topic": topic, "enabled": self.enabled},
|
|
277
|
-
},
|
|
278
|
-
project_root=self.project_root if hasattr(self, 'project_root') else None,
|
|
279
|
-
location="context7/agent_integration.py:get_documentation:entry",
|
|
280
|
-
)
|
|
281
|
-
# #endregion
|
|
282
|
-
if not self.enabled or self.kb_lookup is None:
|
|
283
|
-
return None
|
|
284
|
-
|
|
285
|
-
try:
|
|
286
|
-
# #region agent log
|
|
287
|
-
write_debug_log(
|
|
288
|
-
{
|
|
289
|
-
"sessionId": "debug-session",
|
|
290
|
-
"runId": "run1",
|
|
291
|
-
"hypothesisId": "E",
|
|
292
|
-
"message": "About to call kb_lookup.lookup",
|
|
293
|
-
"data": {"library": library, "topic": topic},
|
|
294
|
-
},
|
|
295
|
-
project_root=self.project_root if hasattr(self, 'project_root') else None,
|
|
296
|
-
location="context7/agent_integration.py:get_documentation:before_lookup",
|
|
297
|
-
)
|
|
298
|
-
# #endregion
|
|
299
|
-
|
|
300
|
-
# Check if we have saved documentation first (offline access)
|
|
301
|
-
if hasattr(self, 'doc_manager') and self.doc_manager:
|
|
302
|
-
saved_doc = self.doc_manager.get_saved_documentation(library, topic)
|
|
303
|
-
if saved_doc:
|
|
304
|
-
logger.debug(f"Using saved documentation for {library} ({topic})")
|
|
305
|
-
return saved_doc
|
|
306
|
-
|
|
307
|
-
result = await self.kb_lookup.lookup(
|
|
308
|
-
library=library, topic=topic, use_fuzzy_match=use_fuzzy_match
|
|
309
|
-
)
|
|
310
|
-
|
|
311
|
-
# Auto-save documentation if available
|
|
312
|
-
if result and result.success and hasattr(self, 'doc_manager') and self.doc_manager:
|
|
313
|
-
try:
|
|
314
|
-
doc_result = {
|
|
315
|
-
"content": result.content,
|
|
316
|
-
"library": result.library,
|
|
317
|
-
"topic": result.topic,
|
|
318
|
-
"source": result.source,
|
|
319
|
-
}
|
|
320
|
-
self.doc_manager.save_documentation(
|
|
321
|
-
library=library,
|
|
322
|
-
topic=topic,
|
|
323
|
-
documentation=doc_result,
|
|
324
|
-
source=result.source,
|
|
325
|
-
)
|
|
326
|
-
except Exception as e:
|
|
327
|
-
logger.debug(f"Failed to auto-save documentation: {e}")
|
|
328
|
-
|
|
329
|
-
# #region agent log
|
|
330
|
-
write_debug_log(
|
|
331
|
-
{
|
|
332
|
-
"sessionId": "debug-session",
|
|
333
|
-
"runId": "run1",
|
|
334
|
-
"hypothesisId": "E",
|
|
335
|
-
"message": "kb_lookup.lookup returned",
|
|
336
|
-
"data": {"library": library, "success": result.success if hasattr(result, 'success') else None},
|
|
337
|
-
},
|
|
338
|
-
project_root=self.project_root if hasattr(self, 'project_root') else None,
|
|
339
|
-
location="context7/agent_integration.py:get_documentation:after_lookup",
|
|
340
|
-
)
|
|
341
|
-
# #endregion
|
|
342
|
-
|
|
343
|
-
if result.success:
|
|
344
|
-
return {
|
|
345
|
-
"content": result.content,
|
|
346
|
-
"library": result.library,
|
|
347
|
-
"topic": result.topic,
|
|
348
|
-
"source": result.source, # "cache", "api", "fuzzy_match"
|
|
349
|
-
"fuzzy_score": result.fuzzy_score,
|
|
350
|
-
"matched_topic": result.matched_topic,
|
|
351
|
-
"response_time_ms": result.response_time_ms,
|
|
352
|
-
}
|
|
353
|
-
elif result.error:
|
|
354
|
-
# Log Context7 unavailability but continue (use debug for common cases)
|
|
355
|
-
# Only log at info level if it's a known library or network error
|
|
356
|
-
error_lower = result.error.lower()
|
|
357
|
-
if "network" in error_lower or "connection" in error_lower:
|
|
358
|
-
logger.warning(
|
|
359
|
-
f"Context7 network error for library '{library}' "
|
|
360
|
-
f"(topic: {topic}): {result.error}. Continuing without Context7 documentation."
|
|
361
|
-
)
|
|
362
|
-
elif "quota" in error_lower:
|
|
363
|
-
# Avoid log spam: once quota is exceeded, subsequent calls are expected to fail.
|
|
364
|
-
try:
|
|
365
|
-
from .backup_client import is_context7_quota_exceeded
|
|
366
|
-
already_exceeded = is_context7_quota_exceeded()
|
|
367
|
-
except Exception:
|
|
368
|
-
already_exceeded = False
|
|
369
|
-
|
|
370
|
-
log_fn = logger.debug if already_exceeded else logger.warning
|
|
371
|
-
log_fn(
|
|
372
|
-
f"Context7 quota exceeded for library '{library}' "
|
|
373
|
-
f"(topic: {topic}): {result.error}. Continuing without Context7 documentation."
|
|
374
|
-
)
|
|
375
|
-
else:
|
|
376
|
-
# Common case: library not found - use debug level
|
|
377
|
-
logger.debug(
|
|
378
|
-
f"Context7 lookup unavailable for library '{library}' "
|
|
379
|
-
f"(topic: {topic}): {result.error}. Continuing without Context7 documentation."
|
|
380
|
-
)
|
|
381
|
-
except (RuntimeError, OSError, PermissionError) as e:
|
|
382
|
-
# Cache lock or file operation failed - log but don't fail the agent
|
|
383
|
-
# These are non-critical errors that shouldn't break agent functionality
|
|
384
|
-
error_msg = str(e)
|
|
385
|
-
if "cache lock" in error_msg.lower() or "lock" in error_msg.lower():
|
|
386
|
-
# Cache lock failures are expected in high-concurrency scenarios
|
|
387
|
-
logger.debug(
|
|
388
|
-
f"Context7 cache lock unavailable for library '{library}' (topic: {topic}): {e}. "
|
|
389
|
-
f"Continuing without Context7 documentation."
|
|
390
|
-
)
|
|
391
|
-
else:
|
|
392
|
-
logger.warning(
|
|
393
|
-
f"Context7 lookup error for library '{library}' (topic: {topic}): {e}. "
|
|
394
|
-
f"Continuing without Context7 documentation.",
|
|
395
|
-
exc_info=True
|
|
396
|
-
)
|
|
397
|
-
except Exception as e:
|
|
398
|
-
# Other unexpected errors - log but don't fail the agent
|
|
399
|
-
logger.warning(
|
|
400
|
-
f"Context7 lookup error for library '{library}' (topic: {topic}): {e}. "
|
|
401
|
-
f"Continuing without Context7 documentation.",
|
|
402
|
-
exc_info=True
|
|
403
|
-
)
|
|
404
|
-
|
|
405
|
-
return None
|
|
406
|
-
|
|
407
|
-
async def search_libraries(
|
|
408
|
-
self, query: str, limit: int = 5
|
|
409
|
-
) -> list[dict[str, str]]:
|
|
410
|
-
"""
|
|
411
|
-
Search for libraries matching a query.
|
|
412
|
-
|
|
413
|
-
Args:
|
|
414
|
-
query: Search query
|
|
415
|
-
limit: Maximum number of results
|
|
416
|
-
|
|
417
|
-
Returns:
|
|
418
|
-
List of dictionaries with library information
|
|
419
|
-
"""
|
|
420
|
-
if not self.enabled or not self.mcp_gateway:
|
|
421
|
-
return []
|
|
422
|
-
|
|
423
|
-
try:
|
|
424
|
-
# Use backup client with automatic fallback (MCP Gateway -> HTTP)
|
|
425
|
-
from .backup_client import call_context7_resolve_with_fallback
|
|
426
|
-
|
|
427
|
-
result = await call_context7_resolve_with_fallback(query, self.mcp_gateway)
|
|
428
|
-
|
|
429
|
-
if result.get("success"):
|
|
430
|
-
matches = result.get("result", {}).get("matches", [])
|
|
431
|
-
return matches[:limit]
|
|
432
|
-
else:
|
|
433
|
-
# Log Context7 unavailability but continue
|
|
434
|
-
error_msg = result.get("error", "Unknown error")
|
|
435
|
-
logger.info(
|
|
436
|
-
f"Context7 search unavailable for query '{query}': {error_msg}. "
|
|
437
|
-
f"Continuing without Context7 library search."
|
|
438
|
-
)
|
|
439
|
-
except Exception as e:
|
|
440
|
-
logger.warning(
|
|
441
|
-
f"Context7 search error for query '{query}': {e}. "
|
|
442
|
-
f"Continuing without Context7 library search.",
|
|
443
|
-
exc_info=True
|
|
444
|
-
)
|
|
445
|
-
|
|
446
|
-
return []
|
|
447
|
-
|
|
448
|
-
def is_library_cached(self, library: str, topic: str | None = None) -> bool:
|
|
449
|
-
"""
|
|
450
|
-
Check if a library/topic is cached.
|
|
451
|
-
|
|
452
|
-
Args:
|
|
453
|
-
library: Library name
|
|
454
|
-
topic: Optional topic name
|
|
455
|
-
|
|
456
|
-
Returns:
|
|
457
|
-
True if cached, False otherwise
|
|
458
|
-
"""
|
|
459
|
-
if not self.enabled or self.kb_cache is None:
|
|
460
|
-
return False
|
|
461
|
-
|
|
462
|
-
if topic is None:
|
|
463
|
-
topic = "overview"
|
|
464
|
-
|
|
465
|
-
return self.kb_cache.exists(library, topic)
|
|
466
|
-
|
|
467
|
-
def get_cache_statistics(self) -> dict[str, Any]:
|
|
468
|
-
"""
|
|
469
|
-
Get cache statistics.
|
|
470
|
-
|
|
471
|
-
Returns:
|
|
472
|
-
Dictionary with cache statistics
|
|
473
|
-
"""
|
|
474
|
-
if not self.enabled or self.analytics is None:
|
|
475
|
-
return {"enabled": False}
|
|
476
|
-
|
|
477
|
-
try:
|
|
478
|
-
metrics = self.analytics.get_cache_metrics()
|
|
479
|
-
return {
|
|
480
|
-
"enabled": True,
|
|
481
|
-
"total_entries": metrics.total_entries,
|
|
482
|
-
"total_libraries": metrics.total_libraries,
|
|
483
|
-
"cache_hits": metrics.cache_hits,
|
|
484
|
-
"cache_misses": metrics.cache_misses,
|
|
485
|
-
"hit_rate": metrics.hit_rate,
|
|
486
|
-
"avg_response_time_ms": metrics.avg_response_time_ms,
|
|
487
|
-
}
|
|
488
|
-
except Exception:
|
|
489
|
-
return {"enabled": True, "error": "Failed to get statistics"}
|
|
490
|
-
|
|
491
|
-
def is_well_known_library(self, lib_name: str) -> bool:
|
|
492
|
-
"""
|
|
493
|
-
Check if a library name is a well-known library (likely to be useful for Context7).
|
|
494
|
-
|
|
495
|
-
This helps filter out local directory names vs. actual libraries that Context7
|
|
496
|
-
might have documentation for.
|
|
497
|
-
|
|
498
|
-
Args:
|
|
499
|
-
lib_name: Library name to check
|
|
500
|
-
|
|
501
|
-
Returns:
|
|
502
|
-
True if it's a well-known library that Context7 likely has docs for
|
|
503
|
-
"""
|
|
504
|
-
well_known = {
|
|
505
|
-
# Python
|
|
506
|
-
"fastapi", "django", "flask", "pydantic", "sqlalchemy", "pytest",
|
|
507
|
-
"requests", "httpx", "aiohttp", "click", "typer", "numpy", "pandas",
|
|
508
|
-
"openai", "anthropic", "yaml", "pyyaml", "marshmallow", "celery",
|
|
509
|
-
# JavaScript/TypeScript
|
|
510
|
-
"react", "vue", "angular", "express", "nextjs", "nuxt", "svelte",
|
|
511
|
-
"typescript", "jest", "vitest", "playwright", "cypress", "selenium",
|
|
512
|
-
"axios", "lodash", "moment", "dayjs", "webpack", "vite",
|
|
513
|
-
# Node.js
|
|
514
|
-
"node", "npm", "yarn", "pnpm",
|
|
515
|
-
# Testing
|
|
516
|
-
"playwright", "puppeteer", "selenium", "cypress", "jest", "mocha",
|
|
517
|
-
# Config/Infra (valid libraries in Context7)
|
|
518
|
-
"config", "dotenv", "env",
|
|
519
|
-
}
|
|
520
|
-
return lib_name.lower() in well_known
|
|
521
|
-
|
|
522
|
-
def should_use_context7(self, user_message: str) -> bool:
|
|
523
|
-
"""
|
|
524
|
-
Heuristic check if Context7 should be used based on user message.
|
|
525
|
-
|
|
526
|
-
Args:
|
|
527
|
-
user_message: User's message/query
|
|
528
|
-
|
|
529
|
-
Returns:
|
|
530
|
-
True if Context7 might be helpful
|
|
531
|
-
"""
|
|
532
|
-
if not self.enabled:
|
|
533
|
-
return False
|
|
534
|
-
|
|
535
|
-
message_lower = user_message.lower()
|
|
536
|
-
|
|
537
|
-
# Common library/framework mentions
|
|
538
|
-
library_keywords = [
|
|
539
|
-
"library",
|
|
540
|
-
"framework",
|
|
541
|
-
"package",
|
|
542
|
-
"npm",
|
|
543
|
-
"pip",
|
|
544
|
-
"import",
|
|
545
|
-
"react",
|
|
546
|
-
"vue",
|
|
547
|
-
"angular",
|
|
548
|
-
"fastapi",
|
|
549
|
-
"django",
|
|
550
|
-
"flask",
|
|
551
|
-
"pytest",
|
|
552
|
-
"jest",
|
|
553
|
-
"vitest",
|
|
554
|
-
"typescript",
|
|
555
|
-
"javascript",
|
|
556
|
-
"documentation",
|
|
557
|
-
"docs",
|
|
558
|
-
"api",
|
|
559
|
-
"sdk",
|
|
560
|
-
]
|
|
561
|
-
|
|
562
|
-
return any(keyword in message_lower for keyword in library_keywords)
|
|
563
|
-
|
|
564
|
-
def detect_libraries(
|
|
565
|
-
self,
|
|
566
|
-
code: str | None = None,
|
|
567
|
-
prompt: str | None = None,
|
|
568
|
-
error_message: str | None = None,
|
|
569
|
-
language: str = "python",
|
|
570
|
-
) -> list[str]:
|
|
571
|
-
"""
|
|
572
|
-
Detect libraries from code, prompt, error messages, or project files.
|
|
573
|
-
|
|
574
|
-
Option 3 (C1) Enhancement: Enhanced library detection for prompt analysis.
|
|
575
|
-
Enhancement 5: Added error message detection.
|
|
576
|
-
|
|
577
|
-
Args:
|
|
578
|
-
code: Optional code content to analyze
|
|
579
|
-
prompt: Optional prompt text to analyze
|
|
580
|
-
error_message: Optional error message or stack trace to analyze
|
|
581
|
-
language: Programming language ("python", "typescript", "javascript")
|
|
582
|
-
|
|
583
|
-
Returns:
|
|
584
|
-
List of detected library names
|
|
585
|
-
"""
|
|
586
|
-
if not self.enabled:
|
|
587
|
-
return []
|
|
588
|
-
|
|
589
|
-
return self.library_detector.detect_all(
|
|
590
|
-
code=code, prompt=prompt, error_message=error_message, language=language
|
|
591
|
-
)
|
|
592
|
-
|
|
593
|
-
async def get_documentation_for_libraries(
|
|
594
|
-
self,
|
|
595
|
-
libraries: list[str],
|
|
596
|
-
topic: str | None = None,
|
|
597
|
-
use_fuzzy_match: bool = True,
|
|
598
|
-
max_concurrency: int = 5,
|
|
599
|
-
per_library_timeout: float = 5.0,
|
|
600
|
-
) -> dict[str, dict[str, Any] | None]:
|
|
601
|
-
"""
|
|
602
|
-
Get documentation for multiple libraries in parallel with circuit breaker.
|
|
603
|
-
|
|
604
|
-
2025 Architecture: Bounded parallelism + circuit breaker for resilience.
|
|
605
|
-
- Max 5 concurrent requests (prevents resource exhaustion)
|
|
606
|
-
- 5s timeout per library (prevents cascading delays)
|
|
607
|
-
- Circuit breaker opens after 3 failures (fast-fails subsequent requests)
|
|
608
|
-
- Early quota detection prevents unnecessary API calls
|
|
609
|
-
|
|
610
|
-
Args:
|
|
611
|
-
libraries: List of library names
|
|
612
|
-
topic: Optional topic name (e.g., "hooks", "routing")
|
|
613
|
-
use_fuzzy_match: Whether to use fuzzy matching
|
|
614
|
-
max_concurrency: Maximum concurrent library lookups (default: 5)
|
|
615
|
-
per_library_timeout: Timeout per library in seconds (default: 5.0)
|
|
616
|
-
|
|
617
|
-
Returns:
|
|
618
|
-
Dictionary mapping library names to their documentation (or None if not found)
|
|
619
|
-
"""
|
|
620
|
-
if not self.enabled:
|
|
621
|
-
return {lib: None for lib in libraries}
|
|
622
|
-
|
|
623
|
-
# Plan 3.2: apply max_chunks_per_step to limit Context7 injection
|
|
624
|
-
max_chunks = 50
|
|
625
|
-
try:
|
|
626
|
-
cb = getattr(self.config, "context_budget", None)
|
|
627
|
-
if cb is not None:
|
|
628
|
-
max_chunks = max(1, getattr(cb, "max_chunks_per_step", 50) or 50)
|
|
629
|
-
except Exception: # pylint: disable=broad-except
|
|
630
|
-
pass
|
|
631
|
-
libraries = list(libraries)[:max_chunks]
|
|
632
|
-
|
|
633
|
-
# CRITICAL FIX: Check quota BEFORE starting parallel execution
|
|
634
|
-
# This prevents making multiple API calls when quota is already exceeded
|
|
635
|
-
try:
|
|
636
|
-
from .backup_client import is_context7_quota_exceeded, get_context7_quota_message
|
|
637
|
-
if is_context7_quota_exceeded():
|
|
638
|
-
quota_msg = get_context7_quota_message() or "Monthly quota exceeded"
|
|
639
|
-
logger.warning(
|
|
640
|
-
f"Context7 API quota exceeded ({quota_msg}). "
|
|
641
|
-
f"Skipping documentation lookup for {len(libraries)} libraries. "
|
|
642
|
-
f"Consider upgrading your Context7 plan or waiting for quota reset."
|
|
643
|
-
)
|
|
644
|
-
# Return empty results for all libraries without making API calls
|
|
645
|
-
return {lib: None for lib in libraries}
|
|
646
|
-
except Exception as e:
|
|
647
|
-
# If quota check fails, log but continue (graceful degradation)
|
|
648
|
-
logger.debug(f"Error checking Context7 quota status: {e}. Continuing with lookups.")
|
|
649
|
-
|
|
650
|
-
import asyncio
|
|
651
|
-
|
|
652
|
-
from .circuit_breaker import get_parallel_executor
|
|
653
|
-
|
|
654
|
-
# Get parallel executor with circuit breaker
|
|
655
|
-
executor = get_parallel_executor(max_concurrency=max_concurrency)
|
|
656
|
-
|
|
657
|
-
# Define the lookup function for each library
|
|
658
|
-
async def lookup_library(lib: str) -> tuple[str, dict[str, Any] | None]:
|
|
659
|
-
# Check quota again before each individual lookup (defense in depth)
|
|
660
|
-
try:
|
|
661
|
-
from .backup_client import is_context7_quota_exceeded
|
|
662
|
-
if is_context7_quota_exceeded():
|
|
663
|
-
return (lib, None) # Fast-fail without API call
|
|
664
|
-
except Exception:
|
|
665
|
-
pass # Continue if quota check fails
|
|
666
|
-
|
|
667
|
-
try:
|
|
668
|
-
result = await asyncio.wait_for(
|
|
669
|
-
self.get_documentation(
|
|
670
|
-
library=lib, topic=topic, use_fuzzy_match=use_fuzzy_match
|
|
671
|
-
),
|
|
672
|
-
timeout=per_library_timeout,
|
|
673
|
-
)
|
|
674
|
-
return (lib, result)
|
|
675
|
-
except asyncio.TimeoutError:
|
|
676
|
-
logger.debug(f"Context7 lookup timeout for {lib} ({per_library_timeout}s)")
|
|
677
|
-
return (lib, None)
|
|
678
|
-
except Exception as e:
|
|
679
|
-
logger.debug(f"Context7 lookup error for {lib}: {e}")
|
|
680
|
-
return (lib, None)
|
|
681
|
-
|
|
682
|
-
# Execute all lookups in parallel with circuit breaker
|
|
683
|
-
results = await executor.execute_all(
|
|
684
|
-
items=libraries,
|
|
685
|
-
func=lookup_library,
|
|
686
|
-
fallback=None,
|
|
687
|
-
)
|
|
688
|
-
|
|
689
|
-
# Map results to libraries
|
|
690
|
-
library_docs = {}
|
|
691
|
-
for result in results:
|
|
692
|
-
if result is None:
|
|
693
|
-
continue
|
|
694
|
-
if isinstance(result, tuple) and len(result) == 2:
|
|
695
|
-
lib, doc = result
|
|
696
|
-
library_docs[lib] = doc
|
|
697
|
-
else:
|
|
698
|
-
logger.debug(f"Unexpected result format: {result}")
|
|
699
|
-
|
|
700
|
-
# Ensure all libraries are in the result (even if lookup failed)
|
|
701
|
-
for lib in libraries:
|
|
702
|
-
if lib not in library_docs:
|
|
703
|
-
library_docs[lib] = None
|
|
704
|
-
|
|
705
|
-
# Log circuit breaker status if any failures
|
|
706
|
-
cb_stats = executor.stats.get("circuit_breaker", {}).get("stats", {})
|
|
707
|
-
if cb_stats.get("failed_requests", 0) > 0:
|
|
708
|
-
logger.debug(
|
|
709
|
-
f"Context7 parallel lookup completed. "
|
|
710
|
-
f"Success: {cb_stats.get('successful_requests', 0)}, "
|
|
711
|
-
f"Failed: {cb_stats.get('failed_requests', 0)}, "
|
|
712
|
-
f"Rejected: {cb_stats.get('rejected_requests', 0)}"
|
|
713
|
-
)
|
|
714
|
-
|
|
715
|
-
return library_docs
|
|
716
|
-
|
|
717
|
-
async def resolve_library_ids(self, libraries: list[str]) -> dict[str, str | None]:
|
|
718
|
-
"""
|
|
719
|
-
Resolve library names to Context7-compatible library IDs.
|
|
720
|
-
|
|
721
|
-
Option 3 Enhancement: Batch library ID resolution.
|
|
722
|
-
|
|
723
|
-
Args:
|
|
724
|
-
libraries: List of library names
|
|
725
|
-
|
|
726
|
-
Returns:
|
|
727
|
-
Dictionary mapping library names to Context7 library IDs (or None if not found)
|
|
728
|
-
"""
|
|
729
|
-
if not self.enabled or not self.mcp_gateway:
|
|
730
|
-
return {lib: None for lib in libraries}
|
|
731
|
-
|
|
732
|
-
import asyncio
|
|
733
|
-
|
|
734
|
-
async def resolve_one(lib: str) -> tuple[str, str | None]:
|
|
735
|
-
try:
|
|
736
|
-
from .backup_client import call_context7_resolve_with_fallback
|
|
737
|
-
|
|
738
|
-
result = await call_context7_resolve_with_fallback(lib, self.mcp_gateway)
|
|
739
|
-
if result.get("success"):
|
|
740
|
-
matches = result.get("result", {}).get("matches", [])
|
|
741
|
-
if matches:
|
|
742
|
-
# Return the first match's library ID
|
|
743
|
-
return (lib, matches[0].get("library_id"))
|
|
744
|
-
except Exception as e:
|
|
745
|
-
logger.debug(f"Error resolving library ID for {lib}: {e}")
|
|
746
|
-
return (lib, None)
|
|
747
|
-
|
|
748
|
-
# Resolve all libraries in parallel
|
|
749
|
-
tasks = [resolve_one(lib) for lib in libraries]
|
|
750
|
-
results = await asyncio.gather(*tasks, return_exceptions=True)
|
|
751
|
-
|
|
752
|
-
# Map results
|
|
753
|
-
library_ids = {}
|
|
754
|
-
for result in results:
|
|
755
|
-
if isinstance(result, Exception):
|
|
756
|
-
logger.warning(f"Error in library resolution: {result}")
|
|
757
|
-
else:
|
|
758
|
-
lib, lib_id = result
|
|
759
|
-
library_ids[lib] = lib_id
|
|
760
|
-
|
|
761
|
-
return library_ids
|
|
762
|
-
|
|
763
|
-
def detect_topics(self, code: str, library: str) -> list[str]:
|
|
764
|
-
"""
|
|
765
|
-
Detect relevant Context7 topics from code context.
|
|
766
|
-
|
|
767
|
-
Enhancement 7: Automatic topic detection from code patterns.
|
|
768
|
-
|
|
769
|
-
Examples:
|
|
770
|
-
- FastAPI code with @router.get() → ["routing", "path-parameters"]
|
|
771
|
-
- React code with useState() → ["hooks", "state-management"]
|
|
772
|
-
- pytest code with @pytest.fixture → ["fixtures", "testing"]
|
|
773
|
-
|
|
774
|
-
Args:
|
|
775
|
-
code: Code content to analyze
|
|
776
|
-
library: Library name (e.g., "fastapi", "react", "pytest")
|
|
777
|
-
|
|
778
|
-
Returns:
|
|
779
|
-
List of detected topic names
|
|
780
|
-
"""
|
|
781
|
-
if not self.enabled:
|
|
782
|
-
return []
|
|
783
|
-
|
|
784
|
-
topics = []
|
|
785
|
-
code_lower = code.lower()
|
|
786
|
-
|
|
787
|
-
# Library-specific topic mappings
|
|
788
|
-
topic_mappings = {
|
|
789
|
-
"fastapi": {
|
|
790
|
-
"routing": ["@router.get", "@router.post", "@router.put", "@router.delete", "apirouter", "route"],
|
|
791
|
-
"path-parameters": ["/{", "{id}", "path parameter", "pathparam"],
|
|
792
|
-
"query-parameters": ["query(", "query parameter", "queryparam"],
|
|
793
|
-
"dependencies": ["depends(", "dependency injection", "inject"],
|
|
794
|
-
"middleware": ["middleware", "@app.middleware", "starlette.middleware"],
|
|
795
|
-
"authentication": ["oauth2", "jwt", "security", "httponly", "authorization"],
|
|
796
|
-
"validation": ["pydantic", "basemodel", "validator", "field"],
|
|
797
|
-
},
|
|
798
|
-
"react": {
|
|
799
|
-
"hooks": ["usestate", "useeffect", "usecallback", "usememo", "useref"],
|
|
800
|
-
"state-management": ["usestate", "usereducer", "context", "redux", "zustand"],
|
|
801
|
-
"routing": ["router", "route", "link", "usenavigate", "browserrouter"],
|
|
802
|
-
"components": ["component", "jsx", "props", "children"],
|
|
803
|
-
"lifecycle": ["useeffect", "componentdidmount", "componentwillunmount"],
|
|
804
|
-
},
|
|
805
|
-
"pytest": {
|
|
806
|
-
"fixtures": ["@pytest.fixture", "fixture", "conftest"],
|
|
807
|
-
"parametrization": ["@pytest.mark.parametrize", "parametrize"],
|
|
808
|
-
"mocking": ["mock", "patch", "magicmock", "mock.patch"],
|
|
809
|
-
"async": ["pytest.mark.asyncio", "async def", "await"],
|
|
810
|
-
},
|
|
811
|
-
"django": {
|
|
812
|
-
"models": ["models.model", "models.charfield", "models.foreignkey"],
|
|
813
|
-
"views": ["view", "class view", "function view", "generic view"],
|
|
814
|
-
"urls": ["urlpatterns", "path(", "re_path("],
|
|
815
|
-
"admin": ["admin.site.register", "admin.modeladmin"],
|
|
816
|
-
"orm": ["objects.filter", "objects.get", "queryset"],
|
|
817
|
-
},
|
|
818
|
-
"flask": {
|
|
819
|
-
"routing": ["@app.route", "@blueprint.route", "route("],
|
|
820
|
-
"templates": ["render_template", "jinja2", "template"],
|
|
821
|
-
"request": ["request.form", "request.json", "request.args"],
|
|
822
|
-
},
|
|
823
|
-
"sqlalchemy": {
|
|
824
|
-
"orm": ["session", "query(", "relationship", "backref"],
|
|
825
|
-
"models": ["declarative_base", "column", "relationship"],
|
|
826
|
-
"migrations": ["alembic", "migration", "upgrade", "downgrade"],
|
|
827
|
-
},
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
if library.lower() in topic_mappings:
|
|
831
|
-
for topic, keywords in topic_mappings[library.lower()].items():
|
|
832
|
-
if any(keyword.lower() in code_lower for keyword in keywords):
|
|
833
|
-
topics.append(topic)
|
|
834
|
-
|
|
835
|
-
return topics
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
def get_context7_helper(
|
|
839
|
-
agent_instance,
|
|
840
|
-
config: ProjectConfig | None = None,
|
|
841
|
-
project_root: Path | None = None,
|
|
842
|
-
) -> Context7AgentHelper | None:
|
|
843
|
-
"""
|
|
844
|
-
Get Context7 helper for an agent instance.
|
|
845
|
-
|
|
846
|
-
Args:
|
|
847
|
-
agent_instance: Agent instance (must have config and mcp_gateway attributes)
|
|
848
|
-
config: Optional ProjectConfig (uses agent's config if not provided)
|
|
849
|
-
project_root: Optional project root path
|
|
850
|
-
|
|
851
|
-
Returns:
|
|
852
|
-
Context7AgentHelper instance or None if Context7 is disabled
|
|
853
|
-
"""
|
|
854
|
-
if config is None:
|
|
855
|
-
config = agent_instance.config
|
|
856
|
-
|
|
857
|
-
if config is None:
|
|
858
|
-
return None
|
|
859
|
-
|
|
860
|
-
mcp_gateway = getattr(agent_instance, "mcp_gateway", None)
|
|
861
|
-
|
|
862
|
-
helper = Context7AgentHelper(
|
|
863
|
-
config=config, mcp_gateway=mcp_gateway, project_root=project_root
|
|
864
|
-
)
|
|
865
|
-
|
|
866
|
-
if not helper.enabled:
|
|
867
|
-
return None
|
|
868
|
-
|
|
869
|
-
return helper
|
|
1
|
+
"""
|
|
2
|
+
Context7 Agent Integration - Helper functions for agents to use Context7 KB.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from ..core.config import ProjectConfig
|
|
10
|
+
from ..mcp.gateway import MCPGateway
|
|
11
|
+
from .analytics import Analytics
|
|
12
|
+
from .cache_structure import CacheStructure
|
|
13
|
+
from .credential_validation import validate_context7_credentials
|
|
14
|
+
from .doc_manager import Context7DocManager
|
|
15
|
+
from .fuzzy_matcher import FuzzyMatcher
|
|
16
|
+
from .kb_cache import KBCache
|
|
17
|
+
from .library_detector import LibraryDetector
|
|
18
|
+
from .lookup import KBLookup
|
|
19
|
+
from .metadata import MetadataManager
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Context7AgentHelper:
|
|
25
|
+
"""
|
|
26
|
+
Helper class for agents to easily access Context7 KB.
|
|
27
|
+
Provides simplified interface for common operations.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
config: ProjectConfig,
|
|
33
|
+
mcp_gateway: MCPGateway | None = None,
|
|
34
|
+
project_root: Path | None = None,
|
|
35
|
+
):
|
|
36
|
+
"""
|
|
37
|
+
Initialize Context7 helper for an agent.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
config: ProjectConfig instance
|
|
41
|
+
mcp_gateway: Optional MCPGateway instance
|
|
42
|
+
project_root: Optional project root path
|
|
43
|
+
"""
|
|
44
|
+
if project_root is None:
|
|
45
|
+
project_root = Path.cwd()
|
|
46
|
+
|
|
47
|
+
# #region agent log
|
|
48
|
+
from ..core.debug_logger import write_debug_log
|
|
49
|
+
# Extract values before JSON serialization to handle MagicMock objects in tests
|
|
50
|
+
config_exists = config is not None
|
|
51
|
+
context7_config_exists = False
|
|
52
|
+
context7_enabled = False
|
|
53
|
+
if config and hasattr(config, 'context7'):
|
|
54
|
+
try:
|
|
55
|
+
context7_config = config.context7
|
|
56
|
+
context7_config_exists = context7_config is not None
|
|
57
|
+
if context7_config_exists and hasattr(context7_config, 'enabled'):
|
|
58
|
+
context7_enabled = bool(context7_config.enabled)
|
|
59
|
+
except (AttributeError, TypeError):
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
write_debug_log(
|
|
63
|
+
{
|
|
64
|
+
"sessionId": "debug-session",
|
|
65
|
+
"runId": "run1",
|
|
66
|
+
"hypothesisId": "C",
|
|
67
|
+
"message": "Context7AgentHelper __init__ called",
|
|
68
|
+
"data": {
|
|
69
|
+
"config_exists": config_exists,
|
|
70
|
+
"context7_config_exists": context7_config_exists,
|
|
71
|
+
"context7_enabled": context7_enabled
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
project_root=project_root,
|
|
75
|
+
location="context7/agent_integration.py:__init__:entry",
|
|
76
|
+
)
|
|
77
|
+
# #endregion
|
|
78
|
+
|
|
79
|
+
# Check if Context7 is enabled
|
|
80
|
+
context7_config = config.context7
|
|
81
|
+
if not context7_config or not context7_config.enabled:
|
|
82
|
+
self.enabled = False
|
|
83
|
+
# #region agent log
|
|
84
|
+
write_debug_log(
|
|
85
|
+
{
|
|
86
|
+
"sessionId": "debug-session",
|
|
87
|
+
"runId": "run1",
|
|
88
|
+
"hypothesisId": "C",
|
|
89
|
+
"message": "Context7 disabled in config",
|
|
90
|
+
"data": {"context7_config": context7_config is not None, "enabled": context7_config.enabled if context7_config else False},
|
|
91
|
+
},
|
|
92
|
+
project_root=project_root,
|
|
93
|
+
location="context7/agent_integration.py:__init__:disabled",
|
|
94
|
+
)
|
|
95
|
+
# #endregion
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
# Ensure API key is available (loads from encrypted storage if needed)
|
|
99
|
+
# This ensures agents don't need to manually pass the API key
|
|
100
|
+
try:
|
|
101
|
+
from .backup_client import _ensure_context7_api_key
|
|
102
|
+
api_key_result = _ensure_context7_api_key()
|
|
103
|
+
# #region agent log
|
|
104
|
+
write_debug_log(
|
|
105
|
+
{
|
|
106
|
+
"sessionId": "debug-session",
|
|
107
|
+
"runId": "run1",
|
|
108
|
+
"hypothesisId": "A",
|
|
109
|
+
"message": "API key ensured",
|
|
110
|
+
"data": {"api_key_available": api_key_result is not None, "key_length": len(api_key_result) if api_key_result else 0},
|
|
111
|
+
},
|
|
112
|
+
project_root=project_root,
|
|
113
|
+
location="context7/agent_integration.py:__init__:api_key_ensured",
|
|
114
|
+
)
|
|
115
|
+
# #endregion
|
|
116
|
+
except Exception as e:
|
|
117
|
+
logger.debug(f"Could not ensure Context7 API key availability: {e}")
|
|
118
|
+
# #region agent log
|
|
119
|
+
write_debug_log(
|
|
120
|
+
{
|
|
121
|
+
"sessionId": "debug-session",
|
|
122
|
+
"runId": "run1",
|
|
123
|
+
"hypothesisId": "A",
|
|
124
|
+
"message": "API key ensure failed",
|
|
125
|
+
"data": {"error": str(e)},
|
|
126
|
+
},
|
|
127
|
+
project_root=project_root,
|
|
128
|
+
location="context7/agent_integration.py:__init__:api_key_error",
|
|
129
|
+
)
|
|
130
|
+
# #endregion
|
|
131
|
+
|
|
132
|
+
# Validate credentials (non-blocking, only warn if Context7 is actually needed)
|
|
133
|
+
# Context7 is optional - only warn if it's explicitly required for the operation
|
|
134
|
+
try:
|
|
135
|
+
cred_result = validate_context7_credentials(mcp_gateway=mcp_gateway)
|
|
136
|
+
# Only log as debug if MCP is available (Context7 works via MCP)
|
|
137
|
+
# Only warn if both MCP and API key are unavailable
|
|
138
|
+
if not cred_result.valid:
|
|
139
|
+
if mcp_gateway:
|
|
140
|
+
# MCP is available, so Context7 should work via MCP
|
|
141
|
+
# Only log as debug to avoid noise
|
|
142
|
+
logger.debug(
|
|
143
|
+
f"Context7 API key not configured, but MCP gateway is available. "
|
|
144
|
+
f"Context7 will work via MCP: {cred_result.error}"
|
|
145
|
+
)
|
|
146
|
+
else:
|
|
147
|
+
# No MCP and no API key - warn only if Context7 is actually needed
|
|
148
|
+
# For now, we'll still warn but at debug level to reduce noise
|
|
149
|
+
logger.debug(
|
|
150
|
+
f"Context7 credentials not configured: {cred_result.error}. "
|
|
151
|
+
f"Context7 features will be limited. {cred_result.actionable_message}"
|
|
152
|
+
)
|
|
153
|
+
except Exception as e:
|
|
154
|
+
logger.debug(f"Context7 credential validation error: {e}", exc_info=True)
|
|
155
|
+
|
|
156
|
+
# Wrap initialization in try-except to prevent Context7 failures from breaking agents
|
|
157
|
+
try:
|
|
158
|
+
self.enabled = True
|
|
159
|
+
self.config = context7_config
|
|
160
|
+
self.project_root = project_root
|
|
161
|
+
# #region agent log
|
|
162
|
+
write_debug_log(
|
|
163
|
+
{
|
|
164
|
+
"sessionId": "debug-session",
|
|
165
|
+
"runId": "run1",
|
|
166
|
+
"hypothesisId": "C",
|
|
167
|
+
"message": "Context7AgentHelper enabled=True",
|
|
168
|
+
"data": {"enabled": True},
|
|
169
|
+
},
|
|
170
|
+
project_root=project_root,
|
|
171
|
+
location="context7/agent_integration.py:__init__:enabled",
|
|
172
|
+
)
|
|
173
|
+
# #endregion
|
|
174
|
+
|
|
175
|
+
# Initialize cache structure
|
|
176
|
+
# Defensive: if knowledge_base.location is not a string (e.g. MagicMock in tests),
|
|
177
|
+
# use default to avoid creating directories with mock reprs (e.g. "MagicMock/").
|
|
178
|
+
kb = getattr(context7_config, "knowledge_base", None)
|
|
179
|
+
loc = getattr(kb, "location", None) if kb is not None else None
|
|
180
|
+
if not isinstance(loc, str):
|
|
181
|
+
loc = ".tapps-agents/kb/context7-cache"
|
|
182
|
+
cache_root = project_root / loc
|
|
183
|
+
self.cache_structure = CacheStructure(cache_root)
|
|
184
|
+
self.cache_structure.initialize()
|
|
185
|
+
|
|
186
|
+
# Initialize components
|
|
187
|
+
self.metadata_manager = MetadataManager(self.cache_structure)
|
|
188
|
+
self.kb_cache = KBCache(self.cache_structure.cache_root, self.metadata_manager)
|
|
189
|
+
self.fuzzy_matcher = FuzzyMatcher(threshold=0.7)
|
|
190
|
+
self.analytics = Analytics(self.cache_structure, self.metadata_manager)
|
|
191
|
+
|
|
192
|
+
# Initialize KB lookup with MCP Gateway
|
|
193
|
+
self.mcp_gateway = mcp_gateway
|
|
194
|
+
self.kb_lookup = KBLookup(
|
|
195
|
+
kb_cache=self.kb_cache, mcp_gateway=mcp_gateway, fuzzy_threshold=0.7
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
# Initialize library detector for Option 3 quality uplift
|
|
199
|
+
self.library_detector = LibraryDetector(project_root=project_root)
|
|
200
|
+
|
|
201
|
+
# Initialize documentation manager for auto-save and offline access (Phase 7.1)
|
|
202
|
+
self.doc_manager = Context7DocManager(
|
|
203
|
+
cache_root=self.cache_structure.cache_root,
|
|
204
|
+
project_root=project_root,
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
# #region agent log
|
|
208
|
+
write_debug_log(
|
|
209
|
+
{
|
|
210
|
+
"sessionId": "debug-session",
|
|
211
|
+
"runId": "run1",
|
|
212
|
+
"hypothesisId": "C",
|
|
213
|
+
"message": "Library detector initialized",
|
|
214
|
+
"data": {"library_detector_created": self.library_detector is not None},
|
|
215
|
+
},
|
|
216
|
+
project_root=project_root,
|
|
217
|
+
location="context7/agent_integration.py:__init__:library_detector_init",
|
|
218
|
+
)
|
|
219
|
+
# #endregion
|
|
220
|
+
except Exception as e:
|
|
221
|
+
# If Context7 initialization fails, disable it gracefully
|
|
222
|
+
# This prevents Context7 failures from breaking agent initialization
|
|
223
|
+
logger.warning(
|
|
224
|
+
f"Context7 initialization failed, disabling Context7 features: {e}. "
|
|
225
|
+
f"Agents will continue to work without Context7."
|
|
226
|
+
)
|
|
227
|
+
self.enabled = False
|
|
228
|
+
# Set minimal attributes to prevent AttributeError
|
|
229
|
+
self.config = context7_config
|
|
230
|
+
self.project_root = project_root
|
|
231
|
+
self.cache_structure = None
|
|
232
|
+
self.metadata_manager = None
|
|
233
|
+
self.kb_cache = None
|
|
234
|
+
self.fuzzy_matcher = None
|
|
235
|
+
self.analytics = None
|
|
236
|
+
self.mcp_gateway = mcp_gateway
|
|
237
|
+
self.kb_lookup = None
|
|
238
|
+
self.library_detector = None
|
|
239
|
+
self.doc_manager = None # Phase 7.1: Doc manager disabled on init failure
|
|
240
|
+
# #region agent log
|
|
241
|
+
write_debug_log(
|
|
242
|
+
{
|
|
243
|
+
"sessionId": "debug-session",
|
|
244
|
+
"runId": "run1",
|
|
245
|
+
"hypothesisId": "C",
|
|
246
|
+
"message": "Context7 initialization failed, disabled",
|
|
247
|
+
"data": {"error": str(e), "enabled": False},
|
|
248
|
+
},
|
|
249
|
+
project_root=project_root,
|
|
250
|
+
location="context7/agent_integration.py:__init__:init_failed",
|
|
251
|
+
)
|
|
252
|
+
# #endregion
|
|
253
|
+
|
|
254
|
+
async def get_documentation(
|
|
255
|
+
self, library: str, topic: str | None = None, use_fuzzy_match: bool = True
|
|
256
|
+
) -> dict[str, Any] | None:
|
|
257
|
+
"""
|
|
258
|
+
Get documentation for a library/topic.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
library: Library name (e.g., "react", "fastapi")
|
|
262
|
+
topic: Optional topic name (e.g., "hooks", "routing")
|
|
263
|
+
use_fuzzy_match: Whether to use fuzzy matching if exact match not found
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
Dictionary with documentation content, or None if not found
|
|
267
|
+
"""
|
|
268
|
+
# #region agent log
|
|
269
|
+
from ..core.debug_logger import write_debug_log
|
|
270
|
+
write_debug_log(
|
|
271
|
+
{
|
|
272
|
+
"sessionId": "debug-session",
|
|
273
|
+
"runId": "run1",
|
|
274
|
+
"hypothesisId": "E",
|
|
275
|
+
"message": "get_documentation called",
|
|
276
|
+
"data": {"library": library, "topic": topic, "enabled": self.enabled},
|
|
277
|
+
},
|
|
278
|
+
project_root=self.project_root if hasattr(self, 'project_root') else None,
|
|
279
|
+
location="context7/agent_integration.py:get_documentation:entry",
|
|
280
|
+
)
|
|
281
|
+
# #endregion
|
|
282
|
+
if not self.enabled or self.kb_lookup is None:
|
|
283
|
+
return None
|
|
284
|
+
|
|
285
|
+
try:
|
|
286
|
+
# #region agent log
|
|
287
|
+
write_debug_log(
|
|
288
|
+
{
|
|
289
|
+
"sessionId": "debug-session",
|
|
290
|
+
"runId": "run1",
|
|
291
|
+
"hypothesisId": "E",
|
|
292
|
+
"message": "About to call kb_lookup.lookup",
|
|
293
|
+
"data": {"library": library, "topic": topic},
|
|
294
|
+
},
|
|
295
|
+
project_root=self.project_root if hasattr(self, 'project_root') else None,
|
|
296
|
+
location="context7/agent_integration.py:get_documentation:before_lookup",
|
|
297
|
+
)
|
|
298
|
+
# #endregion
|
|
299
|
+
|
|
300
|
+
# Check if we have saved documentation first (offline access)
|
|
301
|
+
if hasattr(self, 'doc_manager') and self.doc_manager:
|
|
302
|
+
saved_doc = self.doc_manager.get_saved_documentation(library, topic)
|
|
303
|
+
if saved_doc:
|
|
304
|
+
logger.debug(f"Using saved documentation for {library} ({topic})")
|
|
305
|
+
return saved_doc
|
|
306
|
+
|
|
307
|
+
result = await self.kb_lookup.lookup(
|
|
308
|
+
library=library, topic=topic, use_fuzzy_match=use_fuzzy_match
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
# Auto-save documentation if available
|
|
312
|
+
if result and result.success and hasattr(self, 'doc_manager') and self.doc_manager:
|
|
313
|
+
try:
|
|
314
|
+
doc_result = {
|
|
315
|
+
"content": result.content,
|
|
316
|
+
"library": result.library,
|
|
317
|
+
"topic": result.topic,
|
|
318
|
+
"source": result.source,
|
|
319
|
+
}
|
|
320
|
+
self.doc_manager.save_documentation(
|
|
321
|
+
library=library,
|
|
322
|
+
topic=topic,
|
|
323
|
+
documentation=doc_result,
|
|
324
|
+
source=result.source,
|
|
325
|
+
)
|
|
326
|
+
except Exception as e:
|
|
327
|
+
logger.debug(f"Failed to auto-save documentation: {e}")
|
|
328
|
+
|
|
329
|
+
# #region agent log
|
|
330
|
+
write_debug_log(
|
|
331
|
+
{
|
|
332
|
+
"sessionId": "debug-session",
|
|
333
|
+
"runId": "run1",
|
|
334
|
+
"hypothesisId": "E",
|
|
335
|
+
"message": "kb_lookup.lookup returned",
|
|
336
|
+
"data": {"library": library, "success": result.success if hasattr(result, 'success') else None},
|
|
337
|
+
},
|
|
338
|
+
project_root=self.project_root if hasattr(self, 'project_root') else None,
|
|
339
|
+
location="context7/agent_integration.py:get_documentation:after_lookup",
|
|
340
|
+
)
|
|
341
|
+
# #endregion
|
|
342
|
+
|
|
343
|
+
if result.success:
|
|
344
|
+
return {
|
|
345
|
+
"content": result.content,
|
|
346
|
+
"library": result.library,
|
|
347
|
+
"topic": result.topic,
|
|
348
|
+
"source": result.source, # "cache", "api", "fuzzy_match"
|
|
349
|
+
"fuzzy_score": result.fuzzy_score,
|
|
350
|
+
"matched_topic": result.matched_topic,
|
|
351
|
+
"response_time_ms": result.response_time_ms,
|
|
352
|
+
}
|
|
353
|
+
elif result.error:
|
|
354
|
+
# Log Context7 unavailability but continue (use debug for common cases)
|
|
355
|
+
# Only log at info level if it's a known library or network error
|
|
356
|
+
error_lower = result.error.lower()
|
|
357
|
+
if "network" in error_lower or "connection" in error_lower:
|
|
358
|
+
logger.warning(
|
|
359
|
+
f"Context7 network error for library '{library}' "
|
|
360
|
+
f"(topic: {topic}): {result.error}. Continuing without Context7 documentation."
|
|
361
|
+
)
|
|
362
|
+
elif "quota" in error_lower:
|
|
363
|
+
# Avoid log spam: once quota is exceeded, subsequent calls are expected to fail.
|
|
364
|
+
try:
|
|
365
|
+
from .backup_client import is_context7_quota_exceeded
|
|
366
|
+
already_exceeded = is_context7_quota_exceeded()
|
|
367
|
+
except Exception:
|
|
368
|
+
already_exceeded = False
|
|
369
|
+
|
|
370
|
+
log_fn = logger.debug if already_exceeded else logger.warning
|
|
371
|
+
log_fn(
|
|
372
|
+
f"Context7 quota exceeded for library '{library}' "
|
|
373
|
+
f"(topic: {topic}): {result.error}. Continuing without Context7 documentation."
|
|
374
|
+
)
|
|
375
|
+
else:
|
|
376
|
+
# Common case: library not found - use debug level
|
|
377
|
+
logger.debug(
|
|
378
|
+
f"Context7 lookup unavailable for library '{library}' "
|
|
379
|
+
f"(topic: {topic}): {result.error}. Continuing without Context7 documentation."
|
|
380
|
+
)
|
|
381
|
+
except (RuntimeError, OSError, PermissionError) as e:
|
|
382
|
+
# Cache lock or file operation failed - log but don't fail the agent
|
|
383
|
+
# These are non-critical errors that shouldn't break agent functionality
|
|
384
|
+
error_msg = str(e)
|
|
385
|
+
if "cache lock" in error_msg.lower() or "lock" in error_msg.lower():
|
|
386
|
+
# Cache lock failures are expected in high-concurrency scenarios
|
|
387
|
+
logger.debug(
|
|
388
|
+
f"Context7 cache lock unavailable for library '{library}' (topic: {topic}): {e}. "
|
|
389
|
+
f"Continuing without Context7 documentation."
|
|
390
|
+
)
|
|
391
|
+
else:
|
|
392
|
+
logger.warning(
|
|
393
|
+
f"Context7 lookup error for library '{library}' (topic: {topic}): {e}. "
|
|
394
|
+
f"Continuing without Context7 documentation.",
|
|
395
|
+
exc_info=True
|
|
396
|
+
)
|
|
397
|
+
except Exception as e:
|
|
398
|
+
# Other unexpected errors - log but don't fail the agent
|
|
399
|
+
logger.warning(
|
|
400
|
+
f"Context7 lookup error for library '{library}' (topic: {topic}): {e}. "
|
|
401
|
+
f"Continuing without Context7 documentation.",
|
|
402
|
+
exc_info=True
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
return None
|
|
406
|
+
|
|
407
|
+
async def search_libraries(
|
|
408
|
+
self, query: str, limit: int = 5
|
|
409
|
+
) -> list[dict[str, str]]:
|
|
410
|
+
"""
|
|
411
|
+
Search for libraries matching a query.
|
|
412
|
+
|
|
413
|
+
Args:
|
|
414
|
+
query: Search query
|
|
415
|
+
limit: Maximum number of results
|
|
416
|
+
|
|
417
|
+
Returns:
|
|
418
|
+
List of dictionaries with library information
|
|
419
|
+
"""
|
|
420
|
+
if not self.enabled or not self.mcp_gateway:
|
|
421
|
+
return []
|
|
422
|
+
|
|
423
|
+
try:
|
|
424
|
+
# Use backup client with automatic fallback (MCP Gateway -> HTTP)
|
|
425
|
+
from .backup_client import call_context7_resolve_with_fallback
|
|
426
|
+
|
|
427
|
+
result = await call_context7_resolve_with_fallback(query, self.mcp_gateway)
|
|
428
|
+
|
|
429
|
+
if result.get("success"):
|
|
430
|
+
matches = result.get("result", {}).get("matches", [])
|
|
431
|
+
return matches[:limit]
|
|
432
|
+
else:
|
|
433
|
+
# Log Context7 unavailability but continue
|
|
434
|
+
error_msg = result.get("error", "Unknown error")
|
|
435
|
+
logger.info(
|
|
436
|
+
f"Context7 search unavailable for query '{query}': {error_msg}. "
|
|
437
|
+
f"Continuing without Context7 library search."
|
|
438
|
+
)
|
|
439
|
+
except Exception as e:
|
|
440
|
+
logger.warning(
|
|
441
|
+
f"Context7 search error for query '{query}': {e}. "
|
|
442
|
+
f"Continuing without Context7 library search.",
|
|
443
|
+
exc_info=True
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
return []
|
|
447
|
+
|
|
448
|
+
def is_library_cached(self, library: str, topic: str | None = None) -> bool:
|
|
449
|
+
"""
|
|
450
|
+
Check if a library/topic is cached.
|
|
451
|
+
|
|
452
|
+
Args:
|
|
453
|
+
library: Library name
|
|
454
|
+
topic: Optional topic name
|
|
455
|
+
|
|
456
|
+
Returns:
|
|
457
|
+
True if cached, False otherwise
|
|
458
|
+
"""
|
|
459
|
+
if not self.enabled or self.kb_cache is None:
|
|
460
|
+
return False
|
|
461
|
+
|
|
462
|
+
if topic is None:
|
|
463
|
+
topic = "overview"
|
|
464
|
+
|
|
465
|
+
return self.kb_cache.exists(library, topic)
|
|
466
|
+
|
|
467
|
+
def get_cache_statistics(self) -> dict[str, Any]:
|
|
468
|
+
"""
|
|
469
|
+
Get cache statistics.
|
|
470
|
+
|
|
471
|
+
Returns:
|
|
472
|
+
Dictionary with cache statistics
|
|
473
|
+
"""
|
|
474
|
+
if not self.enabled or self.analytics is None:
|
|
475
|
+
return {"enabled": False}
|
|
476
|
+
|
|
477
|
+
try:
|
|
478
|
+
metrics = self.analytics.get_cache_metrics()
|
|
479
|
+
return {
|
|
480
|
+
"enabled": True,
|
|
481
|
+
"total_entries": metrics.total_entries,
|
|
482
|
+
"total_libraries": metrics.total_libraries,
|
|
483
|
+
"cache_hits": metrics.cache_hits,
|
|
484
|
+
"cache_misses": metrics.cache_misses,
|
|
485
|
+
"hit_rate": metrics.hit_rate,
|
|
486
|
+
"avg_response_time_ms": metrics.avg_response_time_ms,
|
|
487
|
+
}
|
|
488
|
+
except Exception:
|
|
489
|
+
return {"enabled": True, "error": "Failed to get statistics"}
|
|
490
|
+
|
|
491
|
+
def is_well_known_library(self, lib_name: str) -> bool:
|
|
492
|
+
"""
|
|
493
|
+
Check if a library name is a well-known library (likely to be useful for Context7).
|
|
494
|
+
|
|
495
|
+
This helps filter out local directory names vs. actual libraries that Context7
|
|
496
|
+
might have documentation for.
|
|
497
|
+
|
|
498
|
+
Args:
|
|
499
|
+
lib_name: Library name to check
|
|
500
|
+
|
|
501
|
+
Returns:
|
|
502
|
+
True if it's a well-known library that Context7 likely has docs for
|
|
503
|
+
"""
|
|
504
|
+
well_known = {
|
|
505
|
+
# Python
|
|
506
|
+
"fastapi", "django", "flask", "pydantic", "sqlalchemy", "pytest",
|
|
507
|
+
"requests", "httpx", "aiohttp", "click", "typer", "numpy", "pandas",
|
|
508
|
+
"openai", "anthropic", "yaml", "pyyaml", "marshmallow", "celery",
|
|
509
|
+
# JavaScript/TypeScript
|
|
510
|
+
"react", "vue", "angular", "express", "nextjs", "nuxt", "svelte",
|
|
511
|
+
"typescript", "jest", "vitest", "playwright", "cypress", "selenium",
|
|
512
|
+
"axios", "lodash", "moment", "dayjs", "webpack", "vite",
|
|
513
|
+
# Node.js
|
|
514
|
+
"node", "npm", "yarn", "pnpm",
|
|
515
|
+
# Testing
|
|
516
|
+
"playwright", "puppeteer", "selenium", "cypress", "jest", "mocha",
|
|
517
|
+
# Config/Infra (valid libraries in Context7)
|
|
518
|
+
"config", "dotenv", "env",
|
|
519
|
+
}
|
|
520
|
+
return lib_name.lower() in well_known
|
|
521
|
+
|
|
522
|
+
def should_use_context7(self, user_message: str) -> bool:
|
|
523
|
+
"""
|
|
524
|
+
Heuristic check if Context7 should be used based on user message.
|
|
525
|
+
|
|
526
|
+
Args:
|
|
527
|
+
user_message: User's message/query
|
|
528
|
+
|
|
529
|
+
Returns:
|
|
530
|
+
True if Context7 might be helpful
|
|
531
|
+
"""
|
|
532
|
+
if not self.enabled:
|
|
533
|
+
return False
|
|
534
|
+
|
|
535
|
+
message_lower = user_message.lower()
|
|
536
|
+
|
|
537
|
+
# Common library/framework mentions
|
|
538
|
+
library_keywords = [
|
|
539
|
+
"library",
|
|
540
|
+
"framework",
|
|
541
|
+
"package",
|
|
542
|
+
"npm",
|
|
543
|
+
"pip",
|
|
544
|
+
"import",
|
|
545
|
+
"react",
|
|
546
|
+
"vue",
|
|
547
|
+
"angular",
|
|
548
|
+
"fastapi",
|
|
549
|
+
"django",
|
|
550
|
+
"flask",
|
|
551
|
+
"pytest",
|
|
552
|
+
"jest",
|
|
553
|
+
"vitest",
|
|
554
|
+
"typescript",
|
|
555
|
+
"javascript",
|
|
556
|
+
"documentation",
|
|
557
|
+
"docs",
|
|
558
|
+
"api",
|
|
559
|
+
"sdk",
|
|
560
|
+
]
|
|
561
|
+
|
|
562
|
+
return any(keyword in message_lower for keyword in library_keywords)
|
|
563
|
+
|
|
564
|
+
def detect_libraries(
|
|
565
|
+
self,
|
|
566
|
+
code: str | None = None,
|
|
567
|
+
prompt: str | None = None,
|
|
568
|
+
error_message: str | None = None,
|
|
569
|
+
language: str = "python",
|
|
570
|
+
) -> list[str]:
|
|
571
|
+
"""
|
|
572
|
+
Detect libraries from code, prompt, error messages, or project files.
|
|
573
|
+
|
|
574
|
+
Option 3 (C1) Enhancement: Enhanced library detection for prompt analysis.
|
|
575
|
+
Enhancement 5: Added error message detection.
|
|
576
|
+
|
|
577
|
+
Args:
|
|
578
|
+
code: Optional code content to analyze
|
|
579
|
+
prompt: Optional prompt text to analyze
|
|
580
|
+
error_message: Optional error message or stack trace to analyze
|
|
581
|
+
language: Programming language ("python", "typescript", "javascript")
|
|
582
|
+
|
|
583
|
+
Returns:
|
|
584
|
+
List of detected library names
|
|
585
|
+
"""
|
|
586
|
+
if not self.enabled:
|
|
587
|
+
return []
|
|
588
|
+
|
|
589
|
+
return self.library_detector.detect_all(
|
|
590
|
+
code=code, prompt=prompt, error_message=error_message, language=language
|
|
591
|
+
)
|
|
592
|
+
|
|
593
|
+
async def get_documentation_for_libraries(
|
|
594
|
+
self,
|
|
595
|
+
libraries: list[str],
|
|
596
|
+
topic: str | None = None,
|
|
597
|
+
use_fuzzy_match: bool = True,
|
|
598
|
+
max_concurrency: int = 5,
|
|
599
|
+
per_library_timeout: float = 5.0,
|
|
600
|
+
) -> dict[str, dict[str, Any] | None]:
|
|
601
|
+
"""
|
|
602
|
+
Get documentation for multiple libraries in parallel with circuit breaker.
|
|
603
|
+
|
|
604
|
+
2025 Architecture: Bounded parallelism + circuit breaker for resilience.
|
|
605
|
+
- Max 5 concurrent requests (prevents resource exhaustion)
|
|
606
|
+
- 5s timeout per library (prevents cascading delays)
|
|
607
|
+
- Circuit breaker opens after 3 failures (fast-fails subsequent requests)
|
|
608
|
+
- Early quota detection prevents unnecessary API calls
|
|
609
|
+
|
|
610
|
+
Args:
|
|
611
|
+
libraries: List of library names
|
|
612
|
+
topic: Optional topic name (e.g., "hooks", "routing")
|
|
613
|
+
use_fuzzy_match: Whether to use fuzzy matching
|
|
614
|
+
max_concurrency: Maximum concurrent library lookups (default: 5)
|
|
615
|
+
per_library_timeout: Timeout per library in seconds (default: 5.0)
|
|
616
|
+
|
|
617
|
+
Returns:
|
|
618
|
+
Dictionary mapping library names to their documentation (or None if not found)
|
|
619
|
+
"""
|
|
620
|
+
if not self.enabled:
|
|
621
|
+
return {lib: None for lib in libraries}
|
|
622
|
+
|
|
623
|
+
# Plan 3.2: apply max_chunks_per_step to limit Context7 injection
|
|
624
|
+
max_chunks = 50
|
|
625
|
+
try:
|
|
626
|
+
cb = getattr(self.config, "context_budget", None)
|
|
627
|
+
if cb is not None:
|
|
628
|
+
max_chunks = max(1, getattr(cb, "max_chunks_per_step", 50) or 50)
|
|
629
|
+
except Exception: # pylint: disable=broad-except
|
|
630
|
+
pass
|
|
631
|
+
libraries = list(libraries)[:max_chunks]
|
|
632
|
+
|
|
633
|
+
# CRITICAL FIX: Check quota BEFORE starting parallel execution
|
|
634
|
+
# This prevents making multiple API calls when quota is already exceeded
|
|
635
|
+
try:
|
|
636
|
+
from .backup_client import is_context7_quota_exceeded, get_context7_quota_message
|
|
637
|
+
if is_context7_quota_exceeded():
|
|
638
|
+
quota_msg = get_context7_quota_message() or "Monthly quota exceeded"
|
|
639
|
+
logger.warning(
|
|
640
|
+
f"Context7 API quota exceeded ({quota_msg}). "
|
|
641
|
+
f"Skipping documentation lookup for {len(libraries)} libraries. "
|
|
642
|
+
f"Consider upgrading your Context7 plan or waiting for quota reset."
|
|
643
|
+
)
|
|
644
|
+
# Return empty results for all libraries without making API calls
|
|
645
|
+
return {lib: None for lib in libraries}
|
|
646
|
+
except Exception as e:
|
|
647
|
+
# If quota check fails, log but continue (graceful degradation)
|
|
648
|
+
logger.debug(f"Error checking Context7 quota status: {e}. Continuing with lookups.")
|
|
649
|
+
|
|
650
|
+
import asyncio
|
|
651
|
+
|
|
652
|
+
from .circuit_breaker import get_parallel_executor
|
|
653
|
+
|
|
654
|
+
# Get parallel executor with circuit breaker
|
|
655
|
+
executor = get_parallel_executor(max_concurrency=max_concurrency)
|
|
656
|
+
|
|
657
|
+
# Define the lookup function for each library
|
|
658
|
+
async def lookup_library(lib: str) -> tuple[str, dict[str, Any] | None]:
|
|
659
|
+
# Check quota again before each individual lookup (defense in depth)
|
|
660
|
+
try:
|
|
661
|
+
from .backup_client import is_context7_quota_exceeded
|
|
662
|
+
if is_context7_quota_exceeded():
|
|
663
|
+
return (lib, None) # Fast-fail without API call
|
|
664
|
+
except Exception:
|
|
665
|
+
pass # Continue if quota check fails
|
|
666
|
+
|
|
667
|
+
try:
|
|
668
|
+
result = await asyncio.wait_for(
|
|
669
|
+
self.get_documentation(
|
|
670
|
+
library=lib, topic=topic, use_fuzzy_match=use_fuzzy_match
|
|
671
|
+
),
|
|
672
|
+
timeout=per_library_timeout,
|
|
673
|
+
)
|
|
674
|
+
return (lib, result)
|
|
675
|
+
except asyncio.TimeoutError:
|
|
676
|
+
logger.debug(f"Context7 lookup timeout for {lib} ({per_library_timeout}s)")
|
|
677
|
+
return (lib, None)
|
|
678
|
+
except Exception as e:
|
|
679
|
+
logger.debug(f"Context7 lookup error for {lib}: {e}")
|
|
680
|
+
return (lib, None)
|
|
681
|
+
|
|
682
|
+
# Execute all lookups in parallel with circuit breaker
|
|
683
|
+
results = await executor.execute_all(
|
|
684
|
+
items=libraries,
|
|
685
|
+
func=lookup_library,
|
|
686
|
+
fallback=None,
|
|
687
|
+
)
|
|
688
|
+
|
|
689
|
+
# Map results to libraries
|
|
690
|
+
library_docs = {}
|
|
691
|
+
for result in results:
|
|
692
|
+
if result is None:
|
|
693
|
+
continue
|
|
694
|
+
if isinstance(result, tuple) and len(result) == 2:
|
|
695
|
+
lib, doc = result
|
|
696
|
+
library_docs[lib] = doc
|
|
697
|
+
else:
|
|
698
|
+
logger.debug(f"Unexpected result format: {result}")
|
|
699
|
+
|
|
700
|
+
# Ensure all libraries are in the result (even if lookup failed)
|
|
701
|
+
for lib in libraries:
|
|
702
|
+
if lib not in library_docs:
|
|
703
|
+
library_docs[lib] = None
|
|
704
|
+
|
|
705
|
+
# Log circuit breaker status if any failures
|
|
706
|
+
cb_stats = executor.stats.get("circuit_breaker", {}).get("stats", {})
|
|
707
|
+
if cb_stats.get("failed_requests", 0) > 0:
|
|
708
|
+
logger.debug(
|
|
709
|
+
f"Context7 parallel lookup completed. "
|
|
710
|
+
f"Success: {cb_stats.get('successful_requests', 0)}, "
|
|
711
|
+
f"Failed: {cb_stats.get('failed_requests', 0)}, "
|
|
712
|
+
f"Rejected: {cb_stats.get('rejected_requests', 0)}"
|
|
713
|
+
)
|
|
714
|
+
|
|
715
|
+
return library_docs
|
|
716
|
+
|
|
717
|
+
async def resolve_library_ids(self, libraries: list[str]) -> dict[str, str | None]:
|
|
718
|
+
"""
|
|
719
|
+
Resolve library names to Context7-compatible library IDs.
|
|
720
|
+
|
|
721
|
+
Option 3 Enhancement: Batch library ID resolution.
|
|
722
|
+
|
|
723
|
+
Args:
|
|
724
|
+
libraries: List of library names
|
|
725
|
+
|
|
726
|
+
Returns:
|
|
727
|
+
Dictionary mapping library names to Context7 library IDs (or None if not found)
|
|
728
|
+
"""
|
|
729
|
+
if not self.enabled or not self.mcp_gateway:
|
|
730
|
+
return {lib: None for lib in libraries}
|
|
731
|
+
|
|
732
|
+
import asyncio
|
|
733
|
+
|
|
734
|
+
async def resolve_one(lib: str) -> tuple[str, str | None]:
|
|
735
|
+
try:
|
|
736
|
+
from .backup_client import call_context7_resolve_with_fallback
|
|
737
|
+
|
|
738
|
+
result = await call_context7_resolve_with_fallback(lib, self.mcp_gateway)
|
|
739
|
+
if result.get("success"):
|
|
740
|
+
matches = result.get("result", {}).get("matches", [])
|
|
741
|
+
if matches:
|
|
742
|
+
# Return the first match's library ID
|
|
743
|
+
return (lib, matches[0].get("library_id"))
|
|
744
|
+
except Exception as e:
|
|
745
|
+
logger.debug(f"Error resolving library ID for {lib}: {e}")
|
|
746
|
+
return (lib, None)
|
|
747
|
+
|
|
748
|
+
# Resolve all libraries in parallel
|
|
749
|
+
tasks = [resolve_one(lib) for lib in libraries]
|
|
750
|
+
results = await asyncio.gather(*tasks, return_exceptions=True)
|
|
751
|
+
|
|
752
|
+
# Map results
|
|
753
|
+
library_ids = {}
|
|
754
|
+
for result in results:
|
|
755
|
+
if isinstance(result, Exception):
|
|
756
|
+
logger.warning(f"Error in library resolution: {result}")
|
|
757
|
+
else:
|
|
758
|
+
lib, lib_id = result
|
|
759
|
+
library_ids[lib] = lib_id
|
|
760
|
+
|
|
761
|
+
return library_ids
|
|
762
|
+
|
|
763
|
+
def detect_topics(self, code: str, library: str) -> list[str]:
|
|
764
|
+
"""
|
|
765
|
+
Detect relevant Context7 topics from code context.
|
|
766
|
+
|
|
767
|
+
Enhancement 7: Automatic topic detection from code patterns.
|
|
768
|
+
|
|
769
|
+
Examples:
|
|
770
|
+
- FastAPI code with @router.get() → ["routing", "path-parameters"]
|
|
771
|
+
- React code with useState() → ["hooks", "state-management"]
|
|
772
|
+
- pytest code with @pytest.fixture → ["fixtures", "testing"]
|
|
773
|
+
|
|
774
|
+
Args:
|
|
775
|
+
code: Code content to analyze
|
|
776
|
+
library: Library name (e.g., "fastapi", "react", "pytest")
|
|
777
|
+
|
|
778
|
+
Returns:
|
|
779
|
+
List of detected topic names
|
|
780
|
+
"""
|
|
781
|
+
if not self.enabled:
|
|
782
|
+
return []
|
|
783
|
+
|
|
784
|
+
topics = []
|
|
785
|
+
code_lower = code.lower()
|
|
786
|
+
|
|
787
|
+
# Library-specific topic mappings
|
|
788
|
+
topic_mappings = {
|
|
789
|
+
"fastapi": {
|
|
790
|
+
"routing": ["@router.get", "@router.post", "@router.put", "@router.delete", "apirouter", "route"],
|
|
791
|
+
"path-parameters": ["/{", "{id}", "path parameter", "pathparam"],
|
|
792
|
+
"query-parameters": ["query(", "query parameter", "queryparam"],
|
|
793
|
+
"dependencies": ["depends(", "dependency injection", "inject"],
|
|
794
|
+
"middleware": ["middleware", "@app.middleware", "starlette.middleware"],
|
|
795
|
+
"authentication": ["oauth2", "jwt", "security", "httponly", "authorization"],
|
|
796
|
+
"validation": ["pydantic", "basemodel", "validator", "field"],
|
|
797
|
+
},
|
|
798
|
+
"react": {
|
|
799
|
+
"hooks": ["usestate", "useeffect", "usecallback", "usememo", "useref"],
|
|
800
|
+
"state-management": ["usestate", "usereducer", "context", "redux", "zustand"],
|
|
801
|
+
"routing": ["router", "route", "link", "usenavigate", "browserrouter"],
|
|
802
|
+
"components": ["component", "jsx", "props", "children"],
|
|
803
|
+
"lifecycle": ["useeffect", "componentdidmount", "componentwillunmount"],
|
|
804
|
+
},
|
|
805
|
+
"pytest": {
|
|
806
|
+
"fixtures": ["@pytest.fixture", "fixture", "conftest"],
|
|
807
|
+
"parametrization": ["@pytest.mark.parametrize", "parametrize"],
|
|
808
|
+
"mocking": ["mock", "patch", "magicmock", "mock.patch"],
|
|
809
|
+
"async": ["pytest.mark.asyncio", "async def", "await"],
|
|
810
|
+
},
|
|
811
|
+
"django": {
|
|
812
|
+
"models": ["models.model", "models.charfield", "models.foreignkey"],
|
|
813
|
+
"views": ["view", "class view", "function view", "generic view"],
|
|
814
|
+
"urls": ["urlpatterns", "path(", "re_path("],
|
|
815
|
+
"admin": ["admin.site.register", "admin.modeladmin"],
|
|
816
|
+
"orm": ["objects.filter", "objects.get", "queryset"],
|
|
817
|
+
},
|
|
818
|
+
"flask": {
|
|
819
|
+
"routing": ["@app.route", "@blueprint.route", "route("],
|
|
820
|
+
"templates": ["render_template", "jinja2", "template"],
|
|
821
|
+
"request": ["request.form", "request.json", "request.args"],
|
|
822
|
+
},
|
|
823
|
+
"sqlalchemy": {
|
|
824
|
+
"orm": ["session", "query(", "relationship", "backref"],
|
|
825
|
+
"models": ["declarative_base", "column", "relationship"],
|
|
826
|
+
"migrations": ["alembic", "migration", "upgrade", "downgrade"],
|
|
827
|
+
},
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
if library.lower() in topic_mappings:
|
|
831
|
+
for topic, keywords in topic_mappings[library.lower()].items():
|
|
832
|
+
if any(keyword.lower() in code_lower for keyword in keywords):
|
|
833
|
+
topics.append(topic)
|
|
834
|
+
|
|
835
|
+
return topics
|
|
836
|
+
|
|
837
|
+
|
|
838
|
+
def get_context7_helper(
|
|
839
|
+
agent_instance,
|
|
840
|
+
config: ProjectConfig | None = None,
|
|
841
|
+
project_root: Path | None = None,
|
|
842
|
+
) -> Context7AgentHelper | None:
|
|
843
|
+
"""
|
|
844
|
+
Get Context7 helper for an agent instance.
|
|
845
|
+
|
|
846
|
+
Args:
|
|
847
|
+
agent_instance: Agent instance (must have config and mcp_gateway attributes)
|
|
848
|
+
config: Optional ProjectConfig (uses agent's config if not provided)
|
|
849
|
+
project_root: Optional project root path
|
|
850
|
+
|
|
851
|
+
Returns:
|
|
852
|
+
Context7AgentHelper instance or None if Context7 is disabled
|
|
853
|
+
"""
|
|
854
|
+
if config is None:
|
|
855
|
+
config = agent_instance.config
|
|
856
|
+
|
|
857
|
+
if config is None:
|
|
858
|
+
return None
|
|
859
|
+
|
|
860
|
+
mcp_gateway = getattr(agent_instance, "mcp_gateway", None)
|
|
861
|
+
|
|
862
|
+
helper = Context7AgentHelper(
|
|
863
|
+
config=config, mcp_gateway=mcp_gateway, project_root=project_root
|
|
864
|
+
)
|
|
865
|
+
|
|
866
|
+
if not helper.enabled:
|
|
867
|
+
return None
|
|
868
|
+
|
|
869
|
+
return helper
|