tapps-agents 3.6.0__py3-none-any.whl → 3.6.1__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/service_discovery.py +534 -534
- 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/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 -227
- tapps_agents/cli/commands/tester.py +191 -191
- 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/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/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.backup_20260204_064058.py +324 -0
- tapps_agents/health/checks/outcomes.backup_20260204_064256.py +324 -0
- tapps_agents/health/checks/outcomes.backup_20260204_064600.py +324 -0
- 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/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/resources/__init__.py +5 -0
- tapps_agents/resources/claude/__init__.py +1 -0
- tapps_agents/resources/claude/commands/README.md +156 -0
- tapps_agents/resources/claude/commands/__init__.py +1 -0
- tapps_agents/resources/claude/commands/build-fix.md +22 -0
- tapps_agents/resources/claude/commands/build.md +77 -0
- tapps_agents/resources/claude/commands/debug.md +53 -0
- tapps_agents/resources/claude/commands/design.md +68 -0
- tapps_agents/resources/claude/commands/docs.md +53 -0
- tapps_agents/resources/claude/commands/e2e.md +22 -0
- tapps_agents/resources/claude/commands/fix.md +54 -0
- tapps_agents/resources/claude/commands/implement.md +53 -0
- tapps_agents/resources/claude/commands/improve.md +53 -0
- tapps_agents/resources/claude/commands/library-docs.md +64 -0
- tapps_agents/resources/claude/commands/lint.md +52 -0
- tapps_agents/resources/claude/commands/plan.md +65 -0
- tapps_agents/resources/claude/commands/refactor-clean.md +21 -0
- tapps_agents/resources/claude/commands/refactor.md +55 -0
- tapps_agents/resources/claude/commands/review.md +67 -0
- tapps_agents/resources/claude/commands/score.md +60 -0
- tapps_agents/resources/claude/commands/security-review.md +22 -0
- tapps_agents/resources/claude/commands/security-scan.md +54 -0
- tapps_agents/resources/claude/commands/tdd.md +24 -0
- tapps_agents/resources/claude/commands/test-coverage.md +21 -0
- tapps_agents/resources/claude/commands/test.md +54 -0
- tapps_agents/resources/claude/commands/update-codemaps.md +20 -0
- tapps_agents/resources/claude/commands/update-docs.md +21 -0
- tapps_agents/resources/claude/skills/__init__.py +1 -0
- tapps_agents/resources/claude/skills/analyst/SKILL.md +272 -0
- tapps_agents/resources/claude/skills/analyst/__init__.py +1 -0
- tapps_agents/resources/claude/skills/architect/SKILL.md +282 -0
- tapps_agents/resources/claude/skills/architect/__init__.py +1 -0
- tapps_agents/resources/claude/skills/backend-patterns/SKILL.md +30 -0
- tapps_agents/resources/claude/skills/backend-patterns/__init__.py +1 -0
- tapps_agents/resources/claude/skills/coding-standards/SKILL.md +29 -0
- tapps_agents/resources/claude/skills/coding-standards/__init__.py +1 -0
- tapps_agents/resources/claude/skills/debugger/SKILL.md +203 -0
- tapps_agents/resources/claude/skills/debugger/__init__.py +1 -0
- tapps_agents/resources/claude/skills/designer/SKILL.md +243 -0
- tapps_agents/resources/claude/skills/designer/__init__.py +1 -0
- tapps_agents/resources/claude/skills/documenter/SKILL.md +252 -0
- tapps_agents/resources/claude/skills/documenter/__init__.py +1 -0
- tapps_agents/resources/claude/skills/enhancer/SKILL.md +307 -0
- tapps_agents/resources/claude/skills/enhancer/__init__.py +1 -0
- tapps_agents/resources/claude/skills/evaluator/SKILL.md +204 -0
- tapps_agents/resources/claude/skills/evaluator/__init__.py +1 -0
- tapps_agents/resources/claude/skills/frontend-patterns/SKILL.md +29 -0
- tapps_agents/resources/claude/skills/frontend-patterns/__init__.py +1 -0
- tapps_agents/resources/claude/skills/implementer/SKILL.md +188 -0
- tapps_agents/resources/claude/skills/implementer/__init__.py +1 -0
- tapps_agents/resources/claude/skills/improver/SKILL.md +218 -0
- tapps_agents/resources/claude/skills/improver/__init__.py +1 -0
- tapps_agents/resources/claude/skills/ops/SKILL.md +281 -0
- tapps_agents/resources/claude/skills/ops/__init__.py +1 -0
- tapps_agents/resources/claude/skills/orchestrator/SKILL.md +390 -0
- tapps_agents/resources/claude/skills/orchestrator/__init__.py +1 -0
- tapps_agents/resources/claude/skills/planner/SKILL.md +254 -0
- tapps_agents/resources/claude/skills/planner/__init__.py +1 -0
- tapps_agents/resources/claude/skills/reviewer/SKILL.md +434 -0
- tapps_agents/resources/claude/skills/reviewer/__init__.py +1 -0
- tapps_agents/resources/claude/skills/security-review/SKILL.md +31 -0
- tapps_agents/resources/claude/skills/security-review/__init__.py +1 -0
- tapps_agents/resources/claude/skills/simple-mode/SKILL.md +695 -0
- tapps_agents/resources/claude/skills/simple-mode/__init__.py +1 -0
- tapps_agents/resources/claude/skills/tester/SKILL.md +219 -0
- tapps_agents/resources/claude/skills/tester/__init__.py +1 -0
- tapps_agents/resources/cursor/.cursorignore +35 -0
- tapps_agents/resources/cursor/__init__.py +1 -0
- tapps_agents/resources/cursor/commands/__init__.py +1 -0
- tapps_agents/resources/cursor/commands/build-fix.md +11 -0
- tapps_agents/resources/cursor/commands/build.md +11 -0
- tapps_agents/resources/cursor/commands/e2e.md +11 -0
- tapps_agents/resources/cursor/commands/fix.md +11 -0
- tapps_agents/resources/cursor/commands/refactor-clean.md +11 -0
- tapps_agents/resources/cursor/commands/review.md +11 -0
- tapps_agents/resources/cursor/commands/security-review.md +11 -0
- tapps_agents/resources/cursor/commands/tdd.md +11 -0
- tapps_agents/resources/cursor/commands/test-coverage.md +11 -0
- tapps_agents/resources/cursor/commands/test.md +11 -0
- tapps_agents/resources/cursor/commands/update-codemaps.md +10 -0
- tapps_agents/resources/cursor/commands/update-docs.md +11 -0
- tapps_agents/resources/cursor/rules/__init__.py +1 -0
- tapps_agents/resources/cursor/rules/agent-capabilities.mdc +687 -0
- tapps_agents/resources/cursor/rules/coding-style.mdc +31 -0
- tapps_agents/resources/cursor/rules/command-reference.mdc +2081 -0
- tapps_agents/resources/cursor/rules/cursor-mode-usage.mdc +125 -0
- tapps_agents/resources/cursor/rules/git-workflow.mdc +29 -0
- tapps_agents/resources/cursor/rules/performance.mdc +29 -0
- tapps_agents/resources/cursor/rules/project-context.mdc +163 -0
- tapps_agents/resources/cursor/rules/project-profiling.mdc +197 -0
- tapps_agents/resources/cursor/rules/quick-reference.mdc +630 -0
- tapps_agents/resources/cursor/rules/security.mdc +32 -0
- tapps_agents/resources/cursor/rules/simple-mode.mdc +500 -0
- tapps_agents/resources/cursor/rules/testing.mdc +31 -0
- tapps_agents/resources/cursor/rules/when-to-use.mdc +156 -0
- tapps_agents/resources/cursor/rules/workflow-presets.mdc +179 -0
- tapps_agents/resources/customizations/__init__.py +1 -0
- tapps_agents/resources/customizations/example-custom.yaml +83 -0
- tapps_agents/resources/hooks/__init__.py +1 -0
- tapps_agents/resources/hooks/templates/README.md +5 -0
- tapps_agents/resources/hooks/templates/__init__.py +1 -0
- tapps_agents/resources/hooks/templates/add-project-context.yaml +8 -0
- tapps_agents/resources/hooks/templates/auto-format-js.yaml +10 -0
- tapps_agents/resources/hooks/templates/auto-format-python.yaml +10 -0
- tapps_agents/resources/hooks/templates/git-commit-check.yaml +7 -0
- tapps_agents/resources/hooks/templates/notify-on-complete.yaml +8 -0
- tapps_agents/resources/hooks/templates/quality-gate.yaml +8 -0
- tapps_agents/resources/hooks/templates/security-scan-on-edit.yaml +10 -0
- tapps_agents/resources/hooks/templates/session-end-log.yaml +7 -0
- tapps_agents/resources/hooks/templates/show-beads-ready.yaml +8 -0
- tapps_agents/resources/hooks/templates/test-on-edit.yaml +10 -0
- tapps_agents/resources/hooks/templates/update-docs-on-complete.yaml +8 -0
- tapps_agents/resources/hooks/templates/user-prompt-log.yaml +7 -0
- tapps_agents/resources/scripts/__init__.py +1 -0
- tapps_agents/resources/scripts/set_bd_path.ps1 +51 -0
- tapps_agents/resources/workflows/__init__.py +1 -0
- tapps_agents/resources/workflows/presets/__init__.py +1 -0
- tapps_agents/resources/workflows/presets/brownfield-analysis.yaml +235 -0
- tapps_agents/resources/workflows/presets/fix.yaml +78 -0
- tapps_agents/resources/workflows/presets/full-sdlc.yaml +122 -0
- tapps_agents/resources/workflows/presets/quality.yaml +82 -0
- tapps_agents/resources/workflows/presets/rapid-dev.yaml +84 -0
- 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/breakdown_orchestrator.py +49 -49
- tapps_agents/simple_mode/orchestrators/brownfield_orchestrator.py +135 -135
- 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/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_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/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 -148
- 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.6.0.dist-info → tapps_agents-3.6.1.dist-info}/METADATA +672 -672
- tapps_agents-3.6.1.dist-info/RECORD +883 -0
- {tapps_agents-3.6.0.dist-info → tapps_agents-3.6.1.dist-info}/licenses/LICENSE +22 -22
- tapps_agents-3.6.0.dist-info/RECORD +0 -758
- {tapps_agents-3.6.0.dist-info → tapps_agents-3.6.1.dist-info}/WHEEL +0 -0
- {tapps_agents-3.6.0.dist-info → tapps_agents-3.6.1.dist-info}/entry_points.txt +0 -0
- {tapps_agents-3.6.0.dist-info → tapps_agents-3.6.1.dist-info}/top_level.txt +0 -0
|
@@ -1,489 +1,489 @@
|
|
|
1
|
-
# External API Integration Patterns
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
This guide covers patterns for integrating with external APIs (OpenWeatherMap, WattTime, etc.) used in HomeIQ and similar applications.
|
|
6
|
-
|
|
7
|
-
## Core Patterns
|
|
8
|
-
|
|
9
|
-
### Pattern 1: API Client with Retry
|
|
10
|
-
|
|
11
|
-
```python
|
|
12
|
-
import httpx
|
|
13
|
-
from tenacity import retry, stop_after_attempt, wait_exponential
|
|
14
|
-
|
|
15
|
-
class ExternalAPIClient:
|
|
16
|
-
def __init__(self, base_url: str, api_key: str):
|
|
17
|
-
self.base_url = base_url
|
|
18
|
-
self.api_key = api_key
|
|
19
|
-
self.client = httpx.AsyncClient(
|
|
20
|
-
timeout=30.0,
|
|
21
|
-
headers={"Authorization": f"Bearer {self.api_key}"}
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
@retry(
|
|
25
|
-
stop=stop_after_attempt(3),
|
|
26
|
-
wait=wait_exponential(multiplier=1, min=2, max=10)
|
|
27
|
-
)
|
|
28
|
-
async def get(self, endpoint: str, params: dict = None):
|
|
29
|
-
"""GET request with retry."""
|
|
30
|
-
try:
|
|
31
|
-
response = await self.client.get(
|
|
32
|
-
f"{self.base_url}/{endpoint}",
|
|
33
|
-
params=params
|
|
34
|
-
)
|
|
35
|
-
response.raise_for_status()
|
|
36
|
-
return response.json()
|
|
37
|
-
except httpx.HTTPStatusError as e:
|
|
38
|
-
if e.response.status_code >= 500:
|
|
39
|
-
# Retry on server errors
|
|
40
|
-
raise
|
|
41
|
-
# Don't retry on client errors
|
|
42
|
-
raise
|
|
43
|
-
|
|
44
|
-
async def close(self):
|
|
45
|
-
"""Close client."""
|
|
46
|
-
await self.client.aclose()
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### Pattern 2: Rate Limiting
|
|
50
|
-
|
|
51
|
-
```python
|
|
52
|
-
import asyncio
|
|
53
|
-
from collections import deque
|
|
54
|
-
from datetime import datetime, timedelta
|
|
55
|
-
|
|
56
|
-
class RateLimiter:
|
|
57
|
-
def __init__(self, max_requests: int, time_window: int):
|
|
58
|
-
self.max_requests = max_requests
|
|
59
|
-
self.time_window = timedelta(seconds=time_window)
|
|
60
|
-
self.requests = deque()
|
|
61
|
-
|
|
62
|
-
async def acquire(self):
|
|
63
|
-
"""Acquire rate limit token."""
|
|
64
|
-
now = datetime.now()
|
|
65
|
-
|
|
66
|
-
# Remove old requests
|
|
67
|
-
while self.requests and now - self.requests[0] > self.time_window:
|
|
68
|
-
self.requests.popleft()
|
|
69
|
-
|
|
70
|
-
# Check if limit reached
|
|
71
|
-
if len(self.requests) >= self.max_requests:
|
|
72
|
-
wait_time = (self.requests[0] + self.time_window - now).total_seconds()
|
|
73
|
-
await asyncio.sleep(wait_time)
|
|
74
|
-
return await self.acquire()
|
|
75
|
-
|
|
76
|
-
# Add current request
|
|
77
|
-
self.requests.append(now)
|
|
78
|
-
|
|
79
|
-
class RateLimitedAPIClient:
|
|
80
|
-
def __init__(self, base_url: str, api_key: str, rate_limit: RateLimiter):
|
|
81
|
-
self.base_url = base_url
|
|
82
|
-
self.api_key = api_key
|
|
83
|
-
self.rate_limit = rate_limit
|
|
84
|
-
self.client = httpx.AsyncClient()
|
|
85
|
-
|
|
86
|
-
async def get(self, endpoint: str):
|
|
87
|
-
"""GET request with rate limiting."""
|
|
88
|
-
await self.rate_limit.acquire()
|
|
89
|
-
response = await self.client.get(
|
|
90
|
-
f"{self.base_url}/{endpoint}",
|
|
91
|
-
headers={"Authorization": f"Bearer {self.api_key}"}
|
|
92
|
-
)
|
|
93
|
-
response.raise_for_status()
|
|
94
|
-
return response.json()
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
### Pattern 3: Caching
|
|
98
|
-
|
|
99
|
-
```python
|
|
100
|
-
from functools import lru_cache
|
|
101
|
-
from datetime import datetime, timedelta
|
|
102
|
-
import asyncio
|
|
103
|
-
|
|
104
|
-
class CachedAPIClient:
|
|
105
|
-
def __init__(self, base_url: str, api_key: str, cache_ttl: int = 3600):
|
|
106
|
-
self.base_url = base_url
|
|
107
|
-
self.api_key = api_key
|
|
108
|
-
self.cache_ttl = cache_ttl
|
|
109
|
-
self.cache = {}
|
|
110
|
-
self.client = httpx.AsyncClient()
|
|
111
|
-
|
|
112
|
-
async def get(self, endpoint: str, use_cache: bool = True):
|
|
113
|
-
"""GET request with caching."""
|
|
114
|
-
cache_key = endpoint
|
|
115
|
-
|
|
116
|
-
# Check cache
|
|
117
|
-
if use_cache and cache_key in self.cache:
|
|
118
|
-
cached_data, cached_time = self.cache[cache_key]
|
|
119
|
-
if datetime.now() - cached_time < timedelta(seconds=self.cache_ttl):
|
|
120
|
-
return cached_data
|
|
121
|
-
|
|
122
|
-
# Fetch from API
|
|
123
|
-
response = await self.client.get(
|
|
124
|
-
f"{self.base_url}/{endpoint}",
|
|
125
|
-
headers={"Authorization": f"Bearer {self.api_key}"}
|
|
126
|
-
)
|
|
127
|
-
response.raise_for_status()
|
|
128
|
-
data = response.json()
|
|
129
|
-
|
|
130
|
-
# Cache result
|
|
131
|
-
if use_cache:
|
|
132
|
-
self.cache[cache_key] = (data, datetime.now())
|
|
133
|
-
|
|
134
|
-
return data
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
## HomeIQ-Specific Patterns
|
|
138
|
-
|
|
139
|
-
### Pattern 1: OpenWeatherMap Integration
|
|
140
|
-
|
|
141
|
-
```python
|
|
142
|
-
class OpenWeatherMapClient:
|
|
143
|
-
def __init__(self, api_key: str):
|
|
144
|
-
self.base_url = "https://api.openweathermap.org/data/2.5"
|
|
145
|
-
self.api_key = api_key
|
|
146
|
-
self.client = httpx.AsyncClient()
|
|
147
|
-
self.rate_limit = RateLimiter(max_requests=60, time_window=60) # 60/min
|
|
148
|
-
|
|
149
|
-
async def get_weather(self, lat: float, lon: float):
|
|
150
|
-
"""Get weather data."""
|
|
151
|
-
await self.rate_limit.acquire()
|
|
152
|
-
response = await self.client.get(
|
|
153
|
-
f"{self.base_url}/weather",
|
|
154
|
-
params={
|
|
155
|
-
"lat": lat,
|
|
156
|
-
"lon": lon,
|
|
157
|
-
"appid": self.api_key,
|
|
158
|
-
"units": "imperial"
|
|
159
|
-
}
|
|
160
|
-
)
|
|
161
|
-
response.raise_for_status()
|
|
162
|
-
return response.json()
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
### Pattern 2: WattTime Integration
|
|
166
|
-
|
|
167
|
-
```python
|
|
168
|
-
class WattTimeClient:
|
|
169
|
-
def __init__(self, username: str, password: str):
|
|
170
|
-
self.base_url = "https://api.watttime.org/v2"
|
|
171
|
-
self.username = username
|
|
172
|
-
self.password = password
|
|
173
|
-
self.token = None
|
|
174
|
-
self.client = httpx.AsyncClient()
|
|
175
|
-
|
|
176
|
-
async def authenticate(self):
|
|
177
|
-
"""Authenticate and get token."""
|
|
178
|
-
response = await self.client.get(
|
|
179
|
-
f"{self.base_url}/login",
|
|
180
|
-
auth=(self.username, self.password)
|
|
181
|
-
)
|
|
182
|
-
response.raise_for_status()
|
|
183
|
-
self.token = response.json()["token"]
|
|
184
|
-
|
|
185
|
-
async def get_grid_data(self, region: str):
|
|
186
|
-
"""Get grid data."""
|
|
187
|
-
if not self.token:
|
|
188
|
-
await self.authenticate()
|
|
189
|
-
|
|
190
|
-
response = await self.client.get(
|
|
191
|
-
f"{self.base_url}/data",
|
|
192
|
-
params={"region": region},
|
|
193
|
-
headers={"Authorization": f"Bearer {self.token}"}
|
|
194
|
-
)
|
|
195
|
-
response.raise_for_status()
|
|
196
|
-
return response.json()
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
### Pattern 3: Multiple External APIs
|
|
200
|
-
|
|
201
|
-
```python
|
|
202
|
-
class ExternalAPIManager:
|
|
203
|
-
def __init__(self):
|
|
204
|
-
self.weather_client = OpenWeatherMapClient(api_key="...")
|
|
205
|
-
self.watttime_client = WattTimeClient(username="...", password="...")
|
|
206
|
-
self.airnow_client = AirNowClient(api_key="...")
|
|
207
|
-
|
|
208
|
-
async def get_all_data(self, location: dict):
|
|
209
|
-
"""Get data from all external APIs."""
|
|
210
|
-
results = {}
|
|
211
|
-
|
|
212
|
-
# Fetch from all APIs concurrently
|
|
213
|
-
tasks = [
|
|
214
|
-
self.weather_client.get_weather(location["lat"], location["lon"]),
|
|
215
|
-
self.watttime_client.get_grid_data(location["region"]),
|
|
216
|
-
self.airnow_client.get_air_quality(location["lat"], location["lon"])
|
|
217
|
-
]
|
|
218
|
-
|
|
219
|
-
weather, grid, air_quality = await asyncio.gather(*tasks, return_exceptions=True)
|
|
220
|
-
|
|
221
|
-
results["weather"] = weather if not isinstance(weather, Exception) else None
|
|
222
|
-
results["grid"] = grid if not isinstance(grid, Exception) else None
|
|
223
|
-
results["air_quality"] = air_quality if not isinstance(air_quality, Exception) else None
|
|
224
|
-
|
|
225
|
-
return results
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
## OAuth2-Based External APIs
|
|
229
|
-
|
|
230
|
-
Many SaaS APIs use OAuth2 refresh-token flows for long-lived API access without user re-authentication.
|
|
231
|
-
|
|
232
|
-
### Pattern: OAuth2 Refresh-Token Client
|
|
233
|
-
|
|
234
|
-
```python
|
|
235
|
-
import os
|
|
236
|
-
import time
|
|
237
|
-
from typing import Any
|
|
238
|
-
|
|
239
|
-
import requests
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
class OAuth2ExternalAPIClient:
|
|
243
|
-
"""
|
|
244
|
-
OAuth2-based external API client using refresh-token flow.
|
|
245
|
-
|
|
246
|
-
This pattern is used by many SaaS APIs that require long-term access
|
|
247
|
-
without user re-authentication. See api-security-patterns.md for detailed
|
|
248
|
-
OAuth2 refresh-token implementation patterns.
|
|
249
|
-
"""
|
|
250
|
-
|
|
251
|
-
def __init__(
|
|
252
|
-
self,
|
|
253
|
-
client_id: str,
|
|
254
|
-
client_secret: str,
|
|
255
|
-
refresh_token: str,
|
|
256
|
-
api_base_url: str,
|
|
257
|
-
token_url: str,
|
|
258
|
-
timeout_s: int = 30,
|
|
259
|
-
):
|
|
260
|
-
self.client_id = client_id
|
|
261
|
-
self.client_secret = client_secret
|
|
262
|
-
self.refresh_token = refresh_token
|
|
263
|
-
self.api_base_url = api_base_url.rstrip("/")
|
|
264
|
-
self.token_url = token_url
|
|
265
|
-
self.timeout_s = timeout_s
|
|
266
|
-
|
|
267
|
-
self._access_token: str | None = None
|
|
268
|
-
self._access_token_expiry_epoch: float = 0.0
|
|
269
|
-
|
|
270
|
-
def _refresh_access_token(self) -> str:
|
|
271
|
-
"""Exchange refresh_token for access_token."""
|
|
272
|
-
resp = requests.post(
|
|
273
|
-
self.token_url,
|
|
274
|
-
data={
|
|
275
|
-
"refresh_token": self.refresh_token,
|
|
276
|
-
"client_id": self.client_id,
|
|
277
|
-
"client_secret": self.client_secret,
|
|
278
|
-
"grant_type": "refresh_token",
|
|
279
|
-
},
|
|
280
|
-
timeout=self.timeout_s,
|
|
281
|
-
)
|
|
282
|
-
resp.raise_for_status()
|
|
283
|
-
payload = resp.json()
|
|
284
|
-
|
|
285
|
-
if "access_token" not in payload:
|
|
286
|
-
raise RuntimeError("Token response missing access_token")
|
|
287
|
-
|
|
288
|
-
access_token = payload["access_token"]
|
|
289
|
-
expires_in = payload.get("expires_in_sec", payload.get("expires_in", 3600))
|
|
290
|
-
|
|
291
|
-
# Refresh proactively (60 seconds before expiry)
|
|
292
|
-
self._access_token_expiry_epoch = time.time() + int(expires_in) - 60
|
|
293
|
-
self._access_token = access_token
|
|
294
|
-
return access_token
|
|
295
|
-
|
|
296
|
-
def _get_access_token(self) -> str:
|
|
297
|
-
"""Get valid access token, refreshing if necessary."""
|
|
298
|
-
if self._access_token and time.time() < self._access_token_expiry_epoch:
|
|
299
|
-
return self._access_token
|
|
300
|
-
return self._refresh_access_token()
|
|
301
|
-
|
|
302
|
-
def _headers(self) -> dict[str, str]:
|
|
303
|
-
"""Get HTTP headers for authenticated requests."""
|
|
304
|
-
token = self._get_access_token()
|
|
305
|
-
# Note: Some APIs use custom headers
|
|
306
|
-
# See api-security-patterns.md for custom header patterns
|
|
307
|
-
return {
|
|
308
|
-
"Authorization": f"Bearer {token}", # Standard OAuth2
|
|
309
|
-
"Accept": "application/json",
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
def get(self, path: str, params: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
313
|
-
"""Make authenticated GET request."""
|
|
314
|
-
url = f"{self.api_base_url}/{path.lstrip('/')}"
|
|
315
|
-
resp = requests.get(
|
|
316
|
-
url,
|
|
317
|
-
headers=self._headers(),
|
|
318
|
-
params=params,
|
|
319
|
-
timeout=self.timeout_s,
|
|
320
|
-
)
|
|
321
|
-
resp.raise_for_status()
|
|
322
|
-
return resp.json()
|
|
323
|
-
|
|
324
|
-
def post(
|
|
325
|
-
self, path: str, data: dict[str, Any] | None = None, json: dict[str, Any] | None = None
|
|
326
|
-
) -> dict[str, Any]:
|
|
327
|
-
"""Make authenticated POST request."""
|
|
328
|
-
url = f"{self.api_base_url}/{path.lstrip('/')}"
|
|
329
|
-
resp = requests.post(
|
|
330
|
-
url,
|
|
331
|
-
headers=self._headers(),
|
|
332
|
-
data=data,
|
|
333
|
-
json=json,
|
|
334
|
-
timeout=self.timeout_s,
|
|
335
|
-
)
|
|
336
|
-
resp.raise_for_status()
|
|
337
|
-
return resp.json()
|
|
338
|
-
```
|
|
339
|
-
|
|
340
|
-
### Example Usage
|
|
341
|
-
|
|
342
|
-
```python
|
|
343
|
-
# Configure with your API's specific endpoints
|
|
344
|
-
client = OAuth2ExternalAPIClient(
|
|
345
|
-
client_id=os.environ["API_CLIENT_ID"],
|
|
346
|
-
client_secret=os.environ["API_CLIENT_SECRET"],
|
|
347
|
-
refresh_token=os.environ["API_REFRESH_TOKEN"],
|
|
348
|
-
api_base_url="https://api.example.com/v1",
|
|
349
|
-
token_url="https://api.example.com/oauth/v2/token",
|
|
350
|
-
)
|
|
351
|
-
|
|
352
|
-
# For APIs with custom header formats:
|
|
353
|
-
# Override _headers() method for custom header format:
|
|
354
|
-
def _headers(self) -> dict[str, str]:
|
|
355
|
-
token = self._get_access_token()
|
|
356
|
-
return {
|
|
357
|
-
"Authorization": f"CustomPrefix {token}", # Custom header format
|
|
358
|
-
"Accept": "application/json",
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
# Make authenticated requests
|
|
362
|
-
data = client.get("/resource")
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
### Best Practices for OAuth2 External APIs
|
|
366
|
-
|
|
367
|
-
1. **Token Refresh Strategies:**
|
|
368
|
-
- **Proactive refresh:** Refresh 60 seconds before expiry to avoid race conditions
|
|
369
|
-
- **Reactive refresh:** Refresh only when token expires (simpler but may cause request failures)
|
|
370
|
-
- **Recommendation:** Use proactive refresh for production systems
|
|
371
|
-
|
|
372
|
-
2. **Error Handling for Token Expiry:**
|
|
373
|
-
```python
|
|
374
|
-
def get(self, path: str, params: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
375
|
-
"""Make request with automatic token refresh on 401."""
|
|
376
|
-
try:
|
|
377
|
-
return self._make_request("GET", path, params=params)
|
|
378
|
-
except requests.HTTPError as e:
|
|
379
|
-
if e.response.status_code == 401:
|
|
380
|
-
# Token expired, refresh and retry once
|
|
381
|
-
self._access_token = None # Force refresh
|
|
382
|
-
return self._make_request("GET", path, params=params)
|
|
383
|
-
raise
|
|
384
|
-
```
|
|
385
|
-
|
|
386
|
-
3. **Multi-Region Support:**
|
|
387
|
-
```python
|
|
388
|
-
# Some providers have different endpoints for different regions
|
|
389
|
-
REGIONS = {
|
|
390
|
-
"us": {
|
|
391
|
-
"api_base_url": "https://api.example.com/v1",
|
|
392
|
-
"token_url": "https://auth.example.com/oauth/v2/token",
|
|
393
|
-
},
|
|
394
|
-
"eu": {
|
|
395
|
-
"api_base_url": "https://api.example.eu/v1",
|
|
396
|
-
"token_url": "https://auth.example.eu/oauth/v2/token",
|
|
397
|
-
},
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
client = OAuth2ExternalAPIClient(
|
|
401
|
-
...,
|
|
402
|
-
**REGIONS["eu"], # Use EU endpoints
|
|
403
|
-
)
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
4. **Custom Header Formats:**
|
|
407
|
-
- Some APIs use non-standard auth headers (e.g., GitHub's "Token" format)
|
|
408
|
-
- Make header format configurable or override `_headers()` method
|
|
409
|
-
- See `api-security-patterns.md` section "Custom Authentication Headers" for details
|
|
410
|
-
|
|
411
|
-
5. **Secure Storage:**
|
|
412
|
-
- Never hardcode credentials
|
|
413
|
-
- Use environment variables or secret managers
|
|
414
|
-
- Rotate refresh tokens regularly
|
|
415
|
-
|
|
416
|
-
## Error Handling
|
|
417
|
-
|
|
418
|
-
### Pattern 1: API Error Handling
|
|
419
|
-
|
|
420
|
-
```python
|
|
421
|
-
class APIError(Exception):
|
|
422
|
-
pass
|
|
423
|
-
|
|
424
|
-
class RateLimitError(APIError):
|
|
425
|
-
pass
|
|
426
|
-
|
|
427
|
-
class AuthenticationError(APIError):
|
|
428
|
-
pass
|
|
429
|
-
|
|
430
|
-
async def handle_api_error(response: httpx.Response):
|
|
431
|
-
"""Handle API errors."""
|
|
432
|
-
if response.status_code == 429:
|
|
433
|
-
raise RateLimitError("Rate limit exceeded")
|
|
434
|
-
elif response.status_code == 401:
|
|
435
|
-
raise AuthenticationError("Authentication failed")
|
|
436
|
-
elif response.status_code >= 500:
|
|
437
|
-
raise APIError(f"Server error: {response.status_code}")
|
|
438
|
-
else:
|
|
439
|
-
response.raise_for_status()
|
|
440
|
-
```
|
|
441
|
-
|
|
442
|
-
## Best Practices
|
|
443
|
-
|
|
444
|
-
### 1. Use Retry Logic
|
|
445
|
-
|
|
446
|
-
```python
|
|
447
|
-
@retry(
|
|
448
|
-
stop=stop_after_attempt(3),
|
|
449
|
-
wait=wait_exponential(multiplier=1, min=2, max=10)
|
|
450
|
-
)
|
|
451
|
-
```
|
|
452
|
-
|
|
453
|
-
### 2. Implement Rate Limiting
|
|
454
|
-
|
|
455
|
-
```python
|
|
456
|
-
rate_limit = RateLimiter(max_requests=60, time_window=60)
|
|
457
|
-
```
|
|
458
|
-
|
|
459
|
-
### 3. Cache Responses
|
|
460
|
-
|
|
461
|
-
```python
|
|
462
|
-
cache_ttl = 3600 # 1 hour
|
|
463
|
-
```
|
|
464
|
-
|
|
465
|
-
### 4. Handle Errors Gracefully
|
|
466
|
-
|
|
467
|
-
```python
|
|
468
|
-
try:
|
|
469
|
-
data = await api_client.get(endpoint)
|
|
470
|
-
except RateLimitError:
|
|
471
|
-
# Handle rate limit
|
|
472
|
-
pass
|
|
473
|
-
except AuthenticationError:
|
|
474
|
-
# Re-authenticate
|
|
475
|
-
pass
|
|
476
|
-
```
|
|
477
|
-
|
|
478
|
-
### 5. Use Async for Concurrent Requests
|
|
479
|
-
|
|
480
|
-
```python
|
|
481
|
-
results = await asyncio.gather(*tasks)
|
|
482
|
-
```
|
|
483
|
-
|
|
484
|
-
## References
|
|
485
|
-
|
|
486
|
-
- [HTTPX Documentation](https://www.python-httpx.org/)
|
|
487
|
-
- [Tenacity Retry Library](https://tenacity.readthedocs.io/)
|
|
488
|
-
- [API Rate Limiting](https://en.wikipedia.org/wiki/Rate_limiting)
|
|
489
|
-
|
|
1
|
+
# External API Integration Patterns
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This guide covers patterns for integrating with external APIs (OpenWeatherMap, WattTime, etc.) used in HomeIQ and similar applications.
|
|
6
|
+
|
|
7
|
+
## Core Patterns
|
|
8
|
+
|
|
9
|
+
### Pattern 1: API Client with Retry
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
import httpx
|
|
13
|
+
from tenacity import retry, stop_after_attempt, wait_exponential
|
|
14
|
+
|
|
15
|
+
class ExternalAPIClient:
|
|
16
|
+
def __init__(self, base_url: str, api_key: str):
|
|
17
|
+
self.base_url = base_url
|
|
18
|
+
self.api_key = api_key
|
|
19
|
+
self.client = httpx.AsyncClient(
|
|
20
|
+
timeout=30.0,
|
|
21
|
+
headers={"Authorization": f"Bearer {self.api_key}"}
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
@retry(
|
|
25
|
+
stop=stop_after_attempt(3),
|
|
26
|
+
wait=wait_exponential(multiplier=1, min=2, max=10)
|
|
27
|
+
)
|
|
28
|
+
async def get(self, endpoint: str, params: dict = None):
|
|
29
|
+
"""GET request with retry."""
|
|
30
|
+
try:
|
|
31
|
+
response = await self.client.get(
|
|
32
|
+
f"{self.base_url}/{endpoint}",
|
|
33
|
+
params=params
|
|
34
|
+
)
|
|
35
|
+
response.raise_for_status()
|
|
36
|
+
return response.json()
|
|
37
|
+
except httpx.HTTPStatusError as e:
|
|
38
|
+
if e.response.status_code >= 500:
|
|
39
|
+
# Retry on server errors
|
|
40
|
+
raise
|
|
41
|
+
# Don't retry on client errors
|
|
42
|
+
raise
|
|
43
|
+
|
|
44
|
+
async def close(self):
|
|
45
|
+
"""Close client."""
|
|
46
|
+
await self.client.aclose()
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Pattern 2: Rate Limiting
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
import asyncio
|
|
53
|
+
from collections import deque
|
|
54
|
+
from datetime import datetime, timedelta
|
|
55
|
+
|
|
56
|
+
class RateLimiter:
|
|
57
|
+
def __init__(self, max_requests: int, time_window: int):
|
|
58
|
+
self.max_requests = max_requests
|
|
59
|
+
self.time_window = timedelta(seconds=time_window)
|
|
60
|
+
self.requests = deque()
|
|
61
|
+
|
|
62
|
+
async def acquire(self):
|
|
63
|
+
"""Acquire rate limit token."""
|
|
64
|
+
now = datetime.now()
|
|
65
|
+
|
|
66
|
+
# Remove old requests
|
|
67
|
+
while self.requests and now - self.requests[0] > self.time_window:
|
|
68
|
+
self.requests.popleft()
|
|
69
|
+
|
|
70
|
+
# Check if limit reached
|
|
71
|
+
if len(self.requests) >= self.max_requests:
|
|
72
|
+
wait_time = (self.requests[0] + self.time_window - now).total_seconds()
|
|
73
|
+
await asyncio.sleep(wait_time)
|
|
74
|
+
return await self.acquire()
|
|
75
|
+
|
|
76
|
+
# Add current request
|
|
77
|
+
self.requests.append(now)
|
|
78
|
+
|
|
79
|
+
class RateLimitedAPIClient:
|
|
80
|
+
def __init__(self, base_url: str, api_key: str, rate_limit: RateLimiter):
|
|
81
|
+
self.base_url = base_url
|
|
82
|
+
self.api_key = api_key
|
|
83
|
+
self.rate_limit = rate_limit
|
|
84
|
+
self.client = httpx.AsyncClient()
|
|
85
|
+
|
|
86
|
+
async def get(self, endpoint: str):
|
|
87
|
+
"""GET request with rate limiting."""
|
|
88
|
+
await self.rate_limit.acquire()
|
|
89
|
+
response = await self.client.get(
|
|
90
|
+
f"{self.base_url}/{endpoint}",
|
|
91
|
+
headers={"Authorization": f"Bearer {self.api_key}"}
|
|
92
|
+
)
|
|
93
|
+
response.raise_for_status()
|
|
94
|
+
return response.json()
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Pattern 3: Caching
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
from functools import lru_cache
|
|
101
|
+
from datetime import datetime, timedelta
|
|
102
|
+
import asyncio
|
|
103
|
+
|
|
104
|
+
class CachedAPIClient:
|
|
105
|
+
def __init__(self, base_url: str, api_key: str, cache_ttl: int = 3600):
|
|
106
|
+
self.base_url = base_url
|
|
107
|
+
self.api_key = api_key
|
|
108
|
+
self.cache_ttl = cache_ttl
|
|
109
|
+
self.cache = {}
|
|
110
|
+
self.client = httpx.AsyncClient()
|
|
111
|
+
|
|
112
|
+
async def get(self, endpoint: str, use_cache: bool = True):
|
|
113
|
+
"""GET request with caching."""
|
|
114
|
+
cache_key = endpoint
|
|
115
|
+
|
|
116
|
+
# Check cache
|
|
117
|
+
if use_cache and cache_key in self.cache:
|
|
118
|
+
cached_data, cached_time = self.cache[cache_key]
|
|
119
|
+
if datetime.now() - cached_time < timedelta(seconds=self.cache_ttl):
|
|
120
|
+
return cached_data
|
|
121
|
+
|
|
122
|
+
# Fetch from API
|
|
123
|
+
response = await self.client.get(
|
|
124
|
+
f"{self.base_url}/{endpoint}",
|
|
125
|
+
headers={"Authorization": f"Bearer {self.api_key}"}
|
|
126
|
+
)
|
|
127
|
+
response.raise_for_status()
|
|
128
|
+
data = response.json()
|
|
129
|
+
|
|
130
|
+
# Cache result
|
|
131
|
+
if use_cache:
|
|
132
|
+
self.cache[cache_key] = (data, datetime.now())
|
|
133
|
+
|
|
134
|
+
return data
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## HomeIQ-Specific Patterns
|
|
138
|
+
|
|
139
|
+
### Pattern 1: OpenWeatherMap Integration
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
class OpenWeatherMapClient:
|
|
143
|
+
def __init__(self, api_key: str):
|
|
144
|
+
self.base_url = "https://api.openweathermap.org/data/2.5"
|
|
145
|
+
self.api_key = api_key
|
|
146
|
+
self.client = httpx.AsyncClient()
|
|
147
|
+
self.rate_limit = RateLimiter(max_requests=60, time_window=60) # 60/min
|
|
148
|
+
|
|
149
|
+
async def get_weather(self, lat: float, lon: float):
|
|
150
|
+
"""Get weather data."""
|
|
151
|
+
await self.rate_limit.acquire()
|
|
152
|
+
response = await self.client.get(
|
|
153
|
+
f"{self.base_url}/weather",
|
|
154
|
+
params={
|
|
155
|
+
"lat": lat,
|
|
156
|
+
"lon": lon,
|
|
157
|
+
"appid": self.api_key,
|
|
158
|
+
"units": "imperial"
|
|
159
|
+
}
|
|
160
|
+
)
|
|
161
|
+
response.raise_for_status()
|
|
162
|
+
return response.json()
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Pattern 2: WattTime Integration
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
class WattTimeClient:
|
|
169
|
+
def __init__(self, username: str, password: str):
|
|
170
|
+
self.base_url = "https://api.watttime.org/v2"
|
|
171
|
+
self.username = username
|
|
172
|
+
self.password = password
|
|
173
|
+
self.token = None
|
|
174
|
+
self.client = httpx.AsyncClient()
|
|
175
|
+
|
|
176
|
+
async def authenticate(self):
|
|
177
|
+
"""Authenticate and get token."""
|
|
178
|
+
response = await self.client.get(
|
|
179
|
+
f"{self.base_url}/login",
|
|
180
|
+
auth=(self.username, self.password)
|
|
181
|
+
)
|
|
182
|
+
response.raise_for_status()
|
|
183
|
+
self.token = response.json()["token"]
|
|
184
|
+
|
|
185
|
+
async def get_grid_data(self, region: str):
|
|
186
|
+
"""Get grid data."""
|
|
187
|
+
if not self.token:
|
|
188
|
+
await self.authenticate()
|
|
189
|
+
|
|
190
|
+
response = await self.client.get(
|
|
191
|
+
f"{self.base_url}/data",
|
|
192
|
+
params={"region": region},
|
|
193
|
+
headers={"Authorization": f"Bearer {self.token}"}
|
|
194
|
+
)
|
|
195
|
+
response.raise_for_status()
|
|
196
|
+
return response.json()
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Pattern 3: Multiple External APIs
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
class ExternalAPIManager:
|
|
203
|
+
def __init__(self):
|
|
204
|
+
self.weather_client = OpenWeatherMapClient(api_key="...")
|
|
205
|
+
self.watttime_client = WattTimeClient(username="...", password="...")
|
|
206
|
+
self.airnow_client = AirNowClient(api_key="...")
|
|
207
|
+
|
|
208
|
+
async def get_all_data(self, location: dict):
|
|
209
|
+
"""Get data from all external APIs."""
|
|
210
|
+
results = {}
|
|
211
|
+
|
|
212
|
+
# Fetch from all APIs concurrently
|
|
213
|
+
tasks = [
|
|
214
|
+
self.weather_client.get_weather(location["lat"], location["lon"]),
|
|
215
|
+
self.watttime_client.get_grid_data(location["region"]),
|
|
216
|
+
self.airnow_client.get_air_quality(location["lat"], location["lon"])
|
|
217
|
+
]
|
|
218
|
+
|
|
219
|
+
weather, grid, air_quality = await asyncio.gather(*tasks, return_exceptions=True)
|
|
220
|
+
|
|
221
|
+
results["weather"] = weather if not isinstance(weather, Exception) else None
|
|
222
|
+
results["grid"] = grid if not isinstance(grid, Exception) else None
|
|
223
|
+
results["air_quality"] = air_quality if not isinstance(air_quality, Exception) else None
|
|
224
|
+
|
|
225
|
+
return results
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## OAuth2-Based External APIs
|
|
229
|
+
|
|
230
|
+
Many SaaS APIs use OAuth2 refresh-token flows for long-lived API access without user re-authentication.
|
|
231
|
+
|
|
232
|
+
### Pattern: OAuth2 Refresh-Token Client
|
|
233
|
+
|
|
234
|
+
```python
|
|
235
|
+
import os
|
|
236
|
+
import time
|
|
237
|
+
from typing import Any
|
|
238
|
+
|
|
239
|
+
import requests
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
class OAuth2ExternalAPIClient:
|
|
243
|
+
"""
|
|
244
|
+
OAuth2-based external API client using refresh-token flow.
|
|
245
|
+
|
|
246
|
+
This pattern is used by many SaaS APIs that require long-term access
|
|
247
|
+
without user re-authentication. See api-security-patterns.md for detailed
|
|
248
|
+
OAuth2 refresh-token implementation patterns.
|
|
249
|
+
"""
|
|
250
|
+
|
|
251
|
+
def __init__(
|
|
252
|
+
self,
|
|
253
|
+
client_id: str,
|
|
254
|
+
client_secret: str,
|
|
255
|
+
refresh_token: str,
|
|
256
|
+
api_base_url: str,
|
|
257
|
+
token_url: str,
|
|
258
|
+
timeout_s: int = 30,
|
|
259
|
+
):
|
|
260
|
+
self.client_id = client_id
|
|
261
|
+
self.client_secret = client_secret
|
|
262
|
+
self.refresh_token = refresh_token
|
|
263
|
+
self.api_base_url = api_base_url.rstrip("/")
|
|
264
|
+
self.token_url = token_url
|
|
265
|
+
self.timeout_s = timeout_s
|
|
266
|
+
|
|
267
|
+
self._access_token: str | None = None
|
|
268
|
+
self._access_token_expiry_epoch: float = 0.0
|
|
269
|
+
|
|
270
|
+
def _refresh_access_token(self) -> str:
|
|
271
|
+
"""Exchange refresh_token for access_token."""
|
|
272
|
+
resp = requests.post(
|
|
273
|
+
self.token_url,
|
|
274
|
+
data={
|
|
275
|
+
"refresh_token": self.refresh_token,
|
|
276
|
+
"client_id": self.client_id,
|
|
277
|
+
"client_secret": self.client_secret,
|
|
278
|
+
"grant_type": "refresh_token",
|
|
279
|
+
},
|
|
280
|
+
timeout=self.timeout_s,
|
|
281
|
+
)
|
|
282
|
+
resp.raise_for_status()
|
|
283
|
+
payload = resp.json()
|
|
284
|
+
|
|
285
|
+
if "access_token" not in payload:
|
|
286
|
+
raise RuntimeError("Token response missing access_token")
|
|
287
|
+
|
|
288
|
+
access_token = payload["access_token"]
|
|
289
|
+
expires_in = payload.get("expires_in_sec", payload.get("expires_in", 3600))
|
|
290
|
+
|
|
291
|
+
# Refresh proactively (60 seconds before expiry)
|
|
292
|
+
self._access_token_expiry_epoch = time.time() + int(expires_in) - 60
|
|
293
|
+
self._access_token = access_token
|
|
294
|
+
return access_token
|
|
295
|
+
|
|
296
|
+
def _get_access_token(self) -> str:
|
|
297
|
+
"""Get valid access token, refreshing if necessary."""
|
|
298
|
+
if self._access_token and time.time() < self._access_token_expiry_epoch:
|
|
299
|
+
return self._access_token
|
|
300
|
+
return self._refresh_access_token()
|
|
301
|
+
|
|
302
|
+
def _headers(self) -> dict[str, str]:
|
|
303
|
+
"""Get HTTP headers for authenticated requests."""
|
|
304
|
+
token = self._get_access_token()
|
|
305
|
+
# Note: Some APIs use custom headers
|
|
306
|
+
# See api-security-patterns.md for custom header patterns
|
|
307
|
+
return {
|
|
308
|
+
"Authorization": f"Bearer {token}", # Standard OAuth2
|
|
309
|
+
"Accept": "application/json",
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
def get(self, path: str, params: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
313
|
+
"""Make authenticated GET request."""
|
|
314
|
+
url = f"{self.api_base_url}/{path.lstrip('/')}"
|
|
315
|
+
resp = requests.get(
|
|
316
|
+
url,
|
|
317
|
+
headers=self._headers(),
|
|
318
|
+
params=params,
|
|
319
|
+
timeout=self.timeout_s,
|
|
320
|
+
)
|
|
321
|
+
resp.raise_for_status()
|
|
322
|
+
return resp.json()
|
|
323
|
+
|
|
324
|
+
def post(
|
|
325
|
+
self, path: str, data: dict[str, Any] | None = None, json: dict[str, Any] | None = None
|
|
326
|
+
) -> dict[str, Any]:
|
|
327
|
+
"""Make authenticated POST request."""
|
|
328
|
+
url = f"{self.api_base_url}/{path.lstrip('/')}"
|
|
329
|
+
resp = requests.post(
|
|
330
|
+
url,
|
|
331
|
+
headers=self._headers(),
|
|
332
|
+
data=data,
|
|
333
|
+
json=json,
|
|
334
|
+
timeout=self.timeout_s,
|
|
335
|
+
)
|
|
336
|
+
resp.raise_for_status()
|
|
337
|
+
return resp.json()
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Example Usage
|
|
341
|
+
|
|
342
|
+
```python
|
|
343
|
+
# Configure with your API's specific endpoints
|
|
344
|
+
client = OAuth2ExternalAPIClient(
|
|
345
|
+
client_id=os.environ["API_CLIENT_ID"],
|
|
346
|
+
client_secret=os.environ["API_CLIENT_SECRET"],
|
|
347
|
+
refresh_token=os.environ["API_REFRESH_TOKEN"],
|
|
348
|
+
api_base_url="https://api.example.com/v1",
|
|
349
|
+
token_url="https://api.example.com/oauth/v2/token",
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
# For APIs with custom header formats:
|
|
353
|
+
# Override _headers() method for custom header format:
|
|
354
|
+
def _headers(self) -> dict[str, str]:
|
|
355
|
+
token = self._get_access_token()
|
|
356
|
+
return {
|
|
357
|
+
"Authorization": f"CustomPrefix {token}", # Custom header format
|
|
358
|
+
"Accept": "application/json",
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
# Make authenticated requests
|
|
362
|
+
data = client.get("/resource")
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### Best Practices for OAuth2 External APIs
|
|
366
|
+
|
|
367
|
+
1. **Token Refresh Strategies:**
|
|
368
|
+
- **Proactive refresh:** Refresh 60 seconds before expiry to avoid race conditions
|
|
369
|
+
- **Reactive refresh:** Refresh only when token expires (simpler but may cause request failures)
|
|
370
|
+
- **Recommendation:** Use proactive refresh for production systems
|
|
371
|
+
|
|
372
|
+
2. **Error Handling for Token Expiry:**
|
|
373
|
+
```python
|
|
374
|
+
def get(self, path: str, params: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
375
|
+
"""Make request with automatic token refresh on 401."""
|
|
376
|
+
try:
|
|
377
|
+
return self._make_request("GET", path, params=params)
|
|
378
|
+
except requests.HTTPError as e:
|
|
379
|
+
if e.response.status_code == 401:
|
|
380
|
+
# Token expired, refresh and retry once
|
|
381
|
+
self._access_token = None # Force refresh
|
|
382
|
+
return self._make_request("GET", path, params=params)
|
|
383
|
+
raise
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
3. **Multi-Region Support:**
|
|
387
|
+
```python
|
|
388
|
+
# Some providers have different endpoints for different regions
|
|
389
|
+
REGIONS = {
|
|
390
|
+
"us": {
|
|
391
|
+
"api_base_url": "https://api.example.com/v1",
|
|
392
|
+
"token_url": "https://auth.example.com/oauth/v2/token",
|
|
393
|
+
},
|
|
394
|
+
"eu": {
|
|
395
|
+
"api_base_url": "https://api.example.eu/v1",
|
|
396
|
+
"token_url": "https://auth.example.eu/oauth/v2/token",
|
|
397
|
+
},
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
client = OAuth2ExternalAPIClient(
|
|
401
|
+
...,
|
|
402
|
+
**REGIONS["eu"], # Use EU endpoints
|
|
403
|
+
)
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
4. **Custom Header Formats:**
|
|
407
|
+
- Some APIs use non-standard auth headers (e.g., GitHub's "Token" format)
|
|
408
|
+
- Make header format configurable or override `_headers()` method
|
|
409
|
+
- See `api-security-patterns.md` section "Custom Authentication Headers" for details
|
|
410
|
+
|
|
411
|
+
5. **Secure Storage:**
|
|
412
|
+
- Never hardcode credentials
|
|
413
|
+
- Use environment variables or secret managers
|
|
414
|
+
- Rotate refresh tokens regularly
|
|
415
|
+
|
|
416
|
+
## Error Handling
|
|
417
|
+
|
|
418
|
+
### Pattern 1: API Error Handling
|
|
419
|
+
|
|
420
|
+
```python
|
|
421
|
+
class APIError(Exception):
|
|
422
|
+
pass
|
|
423
|
+
|
|
424
|
+
class RateLimitError(APIError):
|
|
425
|
+
pass
|
|
426
|
+
|
|
427
|
+
class AuthenticationError(APIError):
|
|
428
|
+
pass
|
|
429
|
+
|
|
430
|
+
async def handle_api_error(response: httpx.Response):
|
|
431
|
+
"""Handle API errors."""
|
|
432
|
+
if response.status_code == 429:
|
|
433
|
+
raise RateLimitError("Rate limit exceeded")
|
|
434
|
+
elif response.status_code == 401:
|
|
435
|
+
raise AuthenticationError("Authentication failed")
|
|
436
|
+
elif response.status_code >= 500:
|
|
437
|
+
raise APIError(f"Server error: {response.status_code}")
|
|
438
|
+
else:
|
|
439
|
+
response.raise_for_status()
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
## Best Practices
|
|
443
|
+
|
|
444
|
+
### 1. Use Retry Logic
|
|
445
|
+
|
|
446
|
+
```python
|
|
447
|
+
@retry(
|
|
448
|
+
stop=stop_after_attempt(3),
|
|
449
|
+
wait=wait_exponential(multiplier=1, min=2, max=10)
|
|
450
|
+
)
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
### 2. Implement Rate Limiting
|
|
454
|
+
|
|
455
|
+
```python
|
|
456
|
+
rate_limit = RateLimiter(max_requests=60, time_window=60)
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### 3. Cache Responses
|
|
460
|
+
|
|
461
|
+
```python
|
|
462
|
+
cache_ttl = 3600 # 1 hour
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### 4. Handle Errors Gracefully
|
|
466
|
+
|
|
467
|
+
```python
|
|
468
|
+
try:
|
|
469
|
+
data = await api_client.get(endpoint)
|
|
470
|
+
except RateLimitError:
|
|
471
|
+
# Handle rate limit
|
|
472
|
+
pass
|
|
473
|
+
except AuthenticationError:
|
|
474
|
+
# Re-authenticate
|
|
475
|
+
pass
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### 5. Use Async for Concurrent Requests
|
|
479
|
+
|
|
480
|
+
```python
|
|
481
|
+
results = await asyncio.gather(*tasks)
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
## References
|
|
485
|
+
|
|
486
|
+
- [HTTPX Documentation](https://www.python-httpx.org/)
|
|
487
|
+
- [Tenacity Retry Library](https://tenacity.readthedocs.io/)
|
|
488
|
+
- [API Rate Limiting](https://en.wikipedia.org/wiki/Rate_limiting)
|
|
489
|
+
|