attune-ai 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- attune/__init__.py +358 -0
- attune/adaptive/__init__.py +13 -0
- attune/adaptive/task_complexity.py +127 -0
- attune/agent_monitoring.py +414 -0
- attune/cache/__init__.py +117 -0
- attune/cache/base.py +166 -0
- attune/cache/dependency_manager.py +256 -0
- attune/cache/hash_only.py +251 -0
- attune/cache/hybrid.py +457 -0
- attune/cache/storage.py +285 -0
- attune/cache_monitor.py +356 -0
- attune/cache_stats.py +298 -0
- attune/cli/__init__.py +152 -0
- attune/cli/__main__.py +12 -0
- attune/cli/commands/__init__.py +1 -0
- attune/cli/commands/batch.py +264 -0
- attune/cli/commands/cache.py +248 -0
- attune/cli/commands/help.py +331 -0
- attune/cli/commands/info.py +140 -0
- attune/cli/commands/inspect.py +436 -0
- attune/cli/commands/inspection.py +57 -0
- attune/cli/commands/memory.py +48 -0
- attune/cli/commands/metrics.py +92 -0
- attune/cli/commands/orchestrate.py +184 -0
- attune/cli/commands/patterns.py +207 -0
- attune/cli/commands/profiling.py +202 -0
- attune/cli/commands/provider.py +98 -0
- attune/cli/commands/routing.py +285 -0
- attune/cli/commands/setup.py +96 -0
- attune/cli/commands/status.py +235 -0
- attune/cli/commands/sync.py +166 -0
- attune/cli/commands/tier.py +121 -0
- attune/cli/commands/utilities.py +114 -0
- attune/cli/commands/workflow.py +579 -0
- attune/cli/core.py +32 -0
- attune/cli/parsers/__init__.py +68 -0
- attune/cli/parsers/batch.py +118 -0
- attune/cli/parsers/cache.py +65 -0
- attune/cli/parsers/help.py +41 -0
- attune/cli/parsers/info.py +26 -0
- attune/cli/parsers/inspect.py +66 -0
- attune/cli/parsers/metrics.py +42 -0
- attune/cli/parsers/orchestrate.py +61 -0
- attune/cli/parsers/patterns.py +54 -0
- attune/cli/parsers/provider.py +40 -0
- attune/cli/parsers/routing.py +110 -0
- attune/cli/parsers/setup.py +42 -0
- attune/cli/parsers/status.py +47 -0
- attune/cli/parsers/sync.py +31 -0
- attune/cli/parsers/tier.py +33 -0
- attune/cli/parsers/workflow.py +77 -0
- attune/cli/utils/__init__.py +1 -0
- attune/cli/utils/data.py +242 -0
- attune/cli/utils/helpers.py +68 -0
- attune/cli_legacy.py +3957 -0
- attune/cli_minimal.py +1159 -0
- attune/cli_router.py +437 -0
- attune/cli_unified.py +814 -0
- attune/config/__init__.py +66 -0
- attune/config/xml_config.py +286 -0
- attune/config.py +545 -0
- attune/coordination.py +870 -0
- attune/core.py +1511 -0
- attune/core_modules/__init__.py +15 -0
- attune/cost_tracker.py +626 -0
- attune/dashboard/__init__.py +41 -0
- attune/dashboard/app.py +512 -0
- attune/dashboard/simple_server.py +435 -0
- attune/dashboard/standalone_server.py +547 -0
- attune/discovery.py +306 -0
- attune/emergence.py +306 -0
- attune/exceptions.py +123 -0
- attune/feedback_loops.py +373 -0
- attune/hot_reload/README.md +473 -0
- attune/hot_reload/__init__.py +62 -0
- attune/hot_reload/config.py +83 -0
- attune/hot_reload/integration.py +229 -0
- attune/hot_reload/reloader.py +298 -0
- attune/hot_reload/watcher.py +183 -0
- attune/hot_reload/websocket.py +177 -0
- attune/levels.py +577 -0
- attune/leverage_points.py +441 -0
- attune/logging_config.py +261 -0
- attune/mcp/__init__.py +10 -0
- attune/mcp/server.py +506 -0
- attune/memory/__init__.py +237 -0
- attune/memory/claude_memory.py +469 -0
- attune/memory/config.py +224 -0
- attune/memory/control_panel.py +1290 -0
- attune/memory/control_panel_support.py +145 -0
- attune/memory/cross_session.py +845 -0
- attune/memory/edges.py +179 -0
- attune/memory/encryption.py +159 -0
- attune/memory/file_session.py +770 -0
- attune/memory/graph.py +570 -0
- attune/memory/long_term.py +913 -0
- attune/memory/long_term_types.py +99 -0
- attune/memory/mixins/__init__.py +25 -0
- attune/memory/mixins/backend_init_mixin.py +249 -0
- attune/memory/mixins/capabilities_mixin.py +208 -0
- attune/memory/mixins/handoff_mixin.py +208 -0
- attune/memory/mixins/lifecycle_mixin.py +49 -0
- attune/memory/mixins/long_term_mixin.py +352 -0
- attune/memory/mixins/promotion_mixin.py +109 -0
- attune/memory/mixins/short_term_mixin.py +182 -0
- attune/memory/nodes.py +179 -0
- attune/memory/redis_bootstrap.py +540 -0
- attune/memory/security/__init__.py +31 -0
- attune/memory/security/audit_logger.py +932 -0
- attune/memory/security/pii_scrubber.py +640 -0
- attune/memory/security/secrets_detector.py +678 -0
- attune/memory/short_term.py +2192 -0
- attune/memory/simple_storage.py +302 -0
- attune/memory/storage/__init__.py +15 -0
- attune/memory/storage_backend.py +167 -0
- attune/memory/summary_index.py +583 -0
- attune/memory/types.py +446 -0
- attune/memory/unified.py +182 -0
- attune/meta_workflows/__init__.py +74 -0
- attune/meta_workflows/agent_creator.py +248 -0
- attune/meta_workflows/builtin_templates.py +567 -0
- attune/meta_workflows/cli_commands/__init__.py +56 -0
- attune/meta_workflows/cli_commands/agent_commands.py +321 -0
- attune/meta_workflows/cli_commands/analytics_commands.py +442 -0
- attune/meta_workflows/cli_commands/config_commands.py +232 -0
- attune/meta_workflows/cli_commands/memory_commands.py +182 -0
- attune/meta_workflows/cli_commands/template_commands.py +354 -0
- attune/meta_workflows/cli_commands/workflow_commands.py +382 -0
- attune/meta_workflows/cli_meta_workflows.py +59 -0
- attune/meta_workflows/form_engine.py +292 -0
- attune/meta_workflows/intent_detector.py +409 -0
- attune/meta_workflows/models.py +569 -0
- attune/meta_workflows/pattern_learner.py +738 -0
- attune/meta_workflows/plan_generator.py +384 -0
- attune/meta_workflows/session_context.py +397 -0
- attune/meta_workflows/template_registry.py +229 -0
- attune/meta_workflows/workflow.py +984 -0
- attune/metrics/__init__.py +12 -0
- attune/metrics/collector.py +31 -0
- attune/metrics/prompt_metrics.py +194 -0
- attune/models/__init__.py +172 -0
- attune/models/__main__.py +13 -0
- attune/models/adaptive_routing.py +437 -0
- attune/models/auth_cli.py +444 -0
- attune/models/auth_strategy.py +450 -0
- attune/models/cli.py +655 -0
- attune/models/empathy_executor.py +354 -0
- attune/models/executor.py +257 -0
- attune/models/fallback.py +762 -0
- attune/models/provider_config.py +282 -0
- attune/models/registry.py +472 -0
- attune/models/tasks.py +359 -0
- attune/models/telemetry/__init__.py +71 -0
- attune/models/telemetry/analytics.py +594 -0
- attune/models/telemetry/backend.py +196 -0
- attune/models/telemetry/data_models.py +431 -0
- attune/models/telemetry/storage.py +489 -0
- attune/models/token_estimator.py +420 -0
- attune/models/validation.py +280 -0
- attune/monitoring/__init__.py +52 -0
- attune/monitoring/alerts.py +946 -0
- attune/monitoring/alerts_cli.py +448 -0
- attune/monitoring/multi_backend.py +271 -0
- attune/monitoring/otel_backend.py +362 -0
- attune/optimization/__init__.py +19 -0
- attune/optimization/context_optimizer.py +272 -0
- attune/orchestration/__init__.py +67 -0
- attune/orchestration/agent_templates.py +707 -0
- attune/orchestration/config_store.py +499 -0
- attune/orchestration/execution_strategies.py +2111 -0
- attune/orchestration/meta_orchestrator.py +1168 -0
- attune/orchestration/pattern_learner.py +696 -0
- attune/orchestration/real_tools.py +931 -0
- attune/pattern_cache.py +187 -0
- attune/pattern_library.py +542 -0
- attune/patterns/debugging/all_patterns.json +81 -0
- attune/patterns/debugging/workflow_20260107_1770825e.json +77 -0
- attune/patterns/refactoring_memory.json +89 -0
- attune/persistence.py +564 -0
- attune/platform_utils.py +265 -0
- attune/plugins/__init__.py +28 -0
- attune/plugins/base.py +361 -0
- attune/plugins/registry.py +268 -0
- attune/project_index/__init__.py +32 -0
- attune/project_index/cli.py +335 -0
- attune/project_index/index.py +667 -0
- attune/project_index/models.py +504 -0
- attune/project_index/reports.py +474 -0
- attune/project_index/scanner.py +777 -0
- attune/project_index/scanner_parallel.py +291 -0
- attune/prompts/__init__.py +61 -0
- attune/prompts/config.py +77 -0
- attune/prompts/context.py +177 -0
- attune/prompts/parser.py +285 -0
- attune/prompts/registry.py +313 -0
- attune/prompts/templates.py +208 -0
- attune/redis_config.py +302 -0
- attune/redis_memory.py +799 -0
- attune/resilience/__init__.py +56 -0
- attune/resilience/circuit_breaker.py +256 -0
- attune/resilience/fallback.py +179 -0
- attune/resilience/health.py +300 -0
- attune/resilience/retry.py +209 -0
- attune/resilience/timeout.py +135 -0
- attune/routing/__init__.py +43 -0
- attune/routing/chain_executor.py +433 -0
- attune/routing/classifier.py +217 -0
- attune/routing/smart_router.py +234 -0
- attune/routing/workflow_registry.py +343 -0
- attune/scaffolding/README.md +589 -0
- attune/scaffolding/__init__.py +35 -0
- attune/scaffolding/__main__.py +14 -0
- attune/scaffolding/cli.py +240 -0
- attune/scaffolding/templates/base_wizard.py.jinja2 +121 -0
- attune/scaffolding/templates/coach_wizard.py.jinja2 +321 -0
- attune/scaffolding/templates/domain_wizard.py.jinja2 +408 -0
- attune/scaffolding/templates/linear_flow_wizard.py.jinja2 +203 -0
- attune/socratic/__init__.py +256 -0
- attune/socratic/ab_testing.py +958 -0
- attune/socratic/blueprint.py +533 -0
- attune/socratic/cli.py +703 -0
- attune/socratic/collaboration.py +1114 -0
- attune/socratic/domain_templates.py +924 -0
- attune/socratic/embeddings.py +738 -0
- attune/socratic/engine.py +794 -0
- attune/socratic/explainer.py +682 -0
- attune/socratic/feedback.py +772 -0
- attune/socratic/forms.py +629 -0
- attune/socratic/generator.py +732 -0
- attune/socratic/llm_analyzer.py +637 -0
- attune/socratic/mcp_server.py +702 -0
- attune/socratic/session.py +312 -0
- attune/socratic/storage.py +667 -0
- attune/socratic/success.py +730 -0
- attune/socratic/visual_editor.py +860 -0
- attune/socratic/web_ui.py +958 -0
- attune/telemetry/__init__.py +39 -0
- attune/telemetry/agent_coordination.py +475 -0
- attune/telemetry/agent_tracking.py +367 -0
- attune/telemetry/approval_gates.py +545 -0
- attune/telemetry/cli.py +1231 -0
- attune/telemetry/commands/__init__.py +14 -0
- attune/telemetry/commands/dashboard_commands.py +696 -0
- attune/telemetry/event_streaming.py +409 -0
- attune/telemetry/feedback_loop.py +567 -0
- attune/telemetry/usage_tracker.py +591 -0
- attune/templates.py +754 -0
- attune/test_generator/__init__.py +38 -0
- attune/test_generator/__main__.py +14 -0
- attune/test_generator/cli.py +234 -0
- attune/test_generator/generator.py +355 -0
- attune/test_generator/risk_analyzer.py +216 -0
- attune/test_generator/templates/unit_test.py.jinja2 +272 -0
- attune/tier_recommender.py +384 -0
- attune/tools.py +183 -0
- attune/trust/__init__.py +28 -0
- attune/trust/circuit_breaker.py +579 -0
- attune/trust_building.py +527 -0
- attune/validation/__init__.py +19 -0
- attune/validation/xml_validator.py +281 -0
- attune/vscode_bridge.py +173 -0
- attune/workflow_commands.py +780 -0
- attune/workflow_patterns/__init__.py +33 -0
- attune/workflow_patterns/behavior.py +249 -0
- attune/workflow_patterns/core.py +76 -0
- attune/workflow_patterns/output.py +99 -0
- attune/workflow_patterns/registry.py +255 -0
- attune/workflow_patterns/structural.py +288 -0
- attune/workflows/__init__.py +539 -0
- attune/workflows/autonomous_test_gen.py +1268 -0
- attune/workflows/base.py +2667 -0
- attune/workflows/batch_processing.py +342 -0
- attune/workflows/bug_predict.py +1084 -0
- attune/workflows/builder.py +273 -0
- attune/workflows/caching.py +253 -0
- attune/workflows/code_review.py +1048 -0
- attune/workflows/code_review_adapters.py +312 -0
- attune/workflows/code_review_pipeline.py +722 -0
- attune/workflows/config.py +645 -0
- attune/workflows/dependency_check.py +644 -0
- attune/workflows/document_gen/__init__.py +25 -0
- attune/workflows/document_gen/config.py +30 -0
- attune/workflows/document_gen/report_formatter.py +162 -0
- attune/workflows/document_gen/workflow.py +1426 -0
- attune/workflows/document_manager.py +216 -0
- attune/workflows/document_manager_README.md +134 -0
- attune/workflows/documentation_orchestrator.py +1205 -0
- attune/workflows/history.py +510 -0
- attune/workflows/keyboard_shortcuts/__init__.py +39 -0
- attune/workflows/keyboard_shortcuts/generators.py +391 -0
- attune/workflows/keyboard_shortcuts/parsers.py +416 -0
- attune/workflows/keyboard_shortcuts/prompts.py +295 -0
- attune/workflows/keyboard_shortcuts/schema.py +193 -0
- attune/workflows/keyboard_shortcuts/workflow.py +509 -0
- attune/workflows/llm_base.py +363 -0
- attune/workflows/manage_docs.py +87 -0
- attune/workflows/manage_docs_README.md +134 -0
- attune/workflows/manage_documentation.py +821 -0
- attune/workflows/new_sample_workflow1.py +149 -0
- attune/workflows/new_sample_workflow1_README.md +150 -0
- attune/workflows/orchestrated_health_check.py +849 -0
- attune/workflows/orchestrated_release_prep.py +600 -0
- attune/workflows/output.py +413 -0
- attune/workflows/perf_audit.py +863 -0
- attune/workflows/pr_review.py +762 -0
- attune/workflows/progress.py +785 -0
- attune/workflows/progress_server.py +322 -0
- attune/workflows/progressive/README 2.md +454 -0
- attune/workflows/progressive/README.md +454 -0
- attune/workflows/progressive/__init__.py +82 -0
- attune/workflows/progressive/cli.py +219 -0
- attune/workflows/progressive/core.py +488 -0
- attune/workflows/progressive/orchestrator.py +723 -0
- attune/workflows/progressive/reports.py +520 -0
- attune/workflows/progressive/telemetry.py +274 -0
- attune/workflows/progressive/test_gen.py +495 -0
- attune/workflows/progressive/workflow.py +589 -0
- attune/workflows/refactor_plan.py +694 -0
- attune/workflows/release_prep.py +895 -0
- attune/workflows/release_prep_crew.py +969 -0
- attune/workflows/research_synthesis.py +404 -0
- attune/workflows/routing.py +168 -0
- attune/workflows/secure_release.py +593 -0
- attune/workflows/security_adapters.py +297 -0
- attune/workflows/security_audit.py +1329 -0
- attune/workflows/security_audit_phase3.py +355 -0
- attune/workflows/seo_optimization.py +633 -0
- attune/workflows/step_config.py +234 -0
- attune/workflows/telemetry_mixin.py +269 -0
- attune/workflows/test5.py +125 -0
- attune/workflows/test5_README.md +158 -0
- attune/workflows/test_coverage_boost_crew.py +849 -0
- attune/workflows/test_gen/__init__.py +52 -0
- attune/workflows/test_gen/ast_analyzer.py +249 -0
- attune/workflows/test_gen/config.py +88 -0
- attune/workflows/test_gen/data_models.py +38 -0
- attune/workflows/test_gen/report_formatter.py +289 -0
- attune/workflows/test_gen/test_templates.py +381 -0
- attune/workflows/test_gen/workflow.py +655 -0
- attune/workflows/test_gen.py +54 -0
- attune/workflows/test_gen_behavioral.py +477 -0
- attune/workflows/test_gen_parallel.py +341 -0
- attune/workflows/test_lifecycle.py +526 -0
- attune/workflows/test_maintenance.py +627 -0
- attune/workflows/test_maintenance_cli.py +590 -0
- attune/workflows/test_maintenance_crew.py +840 -0
- attune/workflows/test_runner.py +622 -0
- attune/workflows/tier_tracking.py +531 -0
- attune/workflows/xml_enhanced_crew.py +285 -0
- attune_ai-2.0.0.dist-info/METADATA +1026 -0
- attune_ai-2.0.0.dist-info/RECORD +457 -0
- attune_ai-2.0.0.dist-info/WHEEL +5 -0
- attune_ai-2.0.0.dist-info/entry_points.txt +26 -0
- attune_ai-2.0.0.dist-info/licenses/LICENSE +201 -0
- attune_ai-2.0.0.dist-info/licenses/LICENSE_CHANGE_ANNOUNCEMENT.md +101 -0
- attune_ai-2.0.0.dist-info/top_level.txt +5 -0
- attune_healthcare/__init__.py +13 -0
- attune_healthcare/monitors/__init__.py +9 -0
- attune_healthcare/monitors/clinical_protocol_monitor.py +315 -0
- attune_healthcare/monitors/monitoring/__init__.py +44 -0
- attune_healthcare/monitors/monitoring/protocol_checker.py +300 -0
- attune_healthcare/monitors/monitoring/protocol_loader.py +214 -0
- attune_healthcare/monitors/monitoring/sensor_parsers.py +306 -0
- attune_healthcare/monitors/monitoring/trajectory_analyzer.py +389 -0
- attune_llm/README.md +553 -0
- attune_llm/__init__.py +28 -0
- attune_llm/agent_factory/__init__.py +53 -0
- attune_llm/agent_factory/adapters/__init__.py +85 -0
- attune_llm/agent_factory/adapters/autogen_adapter.py +312 -0
- attune_llm/agent_factory/adapters/crewai_adapter.py +483 -0
- attune_llm/agent_factory/adapters/haystack_adapter.py +298 -0
- attune_llm/agent_factory/adapters/langchain_adapter.py +362 -0
- attune_llm/agent_factory/adapters/langgraph_adapter.py +333 -0
- attune_llm/agent_factory/adapters/native.py +228 -0
- attune_llm/agent_factory/adapters/wizard_adapter.py +423 -0
- attune_llm/agent_factory/base.py +305 -0
- attune_llm/agent_factory/crews/__init__.py +67 -0
- attune_llm/agent_factory/crews/code_review.py +1113 -0
- attune_llm/agent_factory/crews/health_check.py +1262 -0
- attune_llm/agent_factory/crews/refactoring.py +1128 -0
- attune_llm/agent_factory/crews/security_audit.py +1018 -0
- attune_llm/agent_factory/decorators.py +287 -0
- attune_llm/agent_factory/factory.py +558 -0
- attune_llm/agent_factory/framework.py +193 -0
- attune_llm/agent_factory/memory_integration.py +328 -0
- attune_llm/agent_factory/resilient.py +320 -0
- attune_llm/agents_md/__init__.py +22 -0
- attune_llm/agents_md/loader.py +218 -0
- attune_llm/agents_md/parser.py +271 -0
- attune_llm/agents_md/registry.py +307 -0
- attune_llm/claude_memory.py +466 -0
- attune_llm/cli/__init__.py +8 -0
- attune_llm/cli/sync_claude.py +487 -0
- attune_llm/code_health.py +1313 -0
- attune_llm/commands/__init__.py +51 -0
- attune_llm/commands/context.py +375 -0
- attune_llm/commands/loader.py +301 -0
- attune_llm/commands/models.py +231 -0
- attune_llm/commands/parser.py +371 -0
- attune_llm/commands/registry.py +429 -0
- attune_llm/config/__init__.py +29 -0
- attune_llm/config/unified.py +291 -0
- attune_llm/context/__init__.py +22 -0
- attune_llm/context/compaction.py +455 -0
- attune_llm/context/manager.py +434 -0
- attune_llm/contextual_patterns.py +361 -0
- attune_llm/core.py +907 -0
- attune_llm/git_pattern_extractor.py +435 -0
- attune_llm/hooks/__init__.py +24 -0
- attune_llm/hooks/config.py +306 -0
- attune_llm/hooks/executor.py +289 -0
- attune_llm/hooks/registry.py +302 -0
- attune_llm/hooks/scripts/__init__.py +39 -0
- attune_llm/hooks/scripts/evaluate_session.py +201 -0
- attune_llm/hooks/scripts/first_time_init.py +285 -0
- attune_llm/hooks/scripts/pre_compact.py +207 -0
- attune_llm/hooks/scripts/session_end.py +183 -0
- attune_llm/hooks/scripts/session_start.py +163 -0
- attune_llm/hooks/scripts/suggest_compact.py +225 -0
- attune_llm/learning/__init__.py +30 -0
- attune_llm/learning/evaluator.py +438 -0
- attune_llm/learning/extractor.py +514 -0
- attune_llm/learning/storage.py +560 -0
- attune_llm/levels.py +227 -0
- attune_llm/pattern_confidence.py +414 -0
- attune_llm/pattern_resolver.py +272 -0
- attune_llm/pattern_summary.py +350 -0
- attune_llm/providers.py +967 -0
- attune_llm/routing/__init__.py +32 -0
- attune_llm/routing/model_router.py +362 -0
- attune_llm/security/IMPLEMENTATION_SUMMARY.md +413 -0
- attune_llm/security/PHASE2_COMPLETE.md +384 -0
- attune_llm/security/PHASE2_SECRETS_DETECTOR_COMPLETE.md +271 -0
- attune_llm/security/QUICK_REFERENCE.md +316 -0
- attune_llm/security/README.md +262 -0
- attune_llm/security/__init__.py +62 -0
- attune_llm/security/audit_logger.py +929 -0
- attune_llm/security/audit_logger_example.py +152 -0
- attune_llm/security/pii_scrubber.py +640 -0
- attune_llm/security/secrets_detector.py +678 -0
- attune_llm/security/secrets_detector_example.py +304 -0
- attune_llm/security/secure_memdocs.py +1192 -0
- attune_llm/security/secure_memdocs_example.py +278 -0
- attune_llm/session_status.py +745 -0
- attune_llm/state.py +246 -0
- attune_llm/utils/__init__.py +5 -0
- attune_llm/utils/tokens.py +349 -0
- attune_software/SOFTWARE_PLUGIN_README.md +57 -0
- attune_software/__init__.py +13 -0
- attune_software/cli/__init__.py +120 -0
- attune_software/cli/inspect.py +362 -0
- attune_software/cli.py +574 -0
- attune_software/plugin.py +188 -0
- workflow_scaffolding/__init__.py +11 -0
- workflow_scaffolding/__main__.py +12 -0
- workflow_scaffolding/cli.py +206 -0
- workflow_scaffolding/generator.py +265 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
"""Agent Factory Decorators
|
|
2
|
+
|
|
3
|
+
Standardized decorators for agent operations including error handling,
|
|
4
|
+
logging, and performance monitoring.
|
|
5
|
+
|
|
6
|
+
Copyright 2025 Smart-AI-Memory
|
|
7
|
+
Licensed under Fair Source License 0.9
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import asyncio
|
|
11
|
+
import logging
|
|
12
|
+
import time
|
|
13
|
+
from collections.abc import Callable
|
|
14
|
+
from functools import wraps
|
|
15
|
+
from typing import Any, TypeVar
|
|
16
|
+
|
|
17
|
+
from attune_llm.config.unified import AgentOperationError
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
F = TypeVar("F", bound=Callable[..., Any])
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def safe_agent_operation(operation_name: str):
|
|
25
|
+
"""Decorator for safe agent operations with logging and error handling.
|
|
26
|
+
|
|
27
|
+
Wraps async agent methods to:
|
|
28
|
+
- Log operation start/end
|
|
29
|
+
- Catch and wrap exceptions
|
|
30
|
+
- Add to audit trail if available
|
|
31
|
+
- Provide graceful degradation
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
operation_name: Human-readable name for the operation
|
|
35
|
+
|
|
36
|
+
Example:
|
|
37
|
+
class MyAgent(BaseAgent):
|
|
38
|
+
@safe_agent_operation("invoke")
|
|
39
|
+
async def invoke(self, input_data, context=None):
|
|
40
|
+
# Operation code here
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def decorator(func: F) -> F:
|
|
46
|
+
@wraps(func)
|
|
47
|
+
async def wrapper(self, *args, **kwargs):
|
|
48
|
+
start_time = time.time()
|
|
49
|
+
agent_name = getattr(self, "name", self.__class__.__name__)
|
|
50
|
+
|
|
51
|
+
logger.debug(f"[{agent_name}] Starting {operation_name}")
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
result = await func(self, *args, **kwargs)
|
|
55
|
+
|
|
56
|
+
elapsed = time.time() - start_time
|
|
57
|
+
logger.debug(f"[{agent_name}] Completed {operation_name} in {elapsed:.2f}s")
|
|
58
|
+
|
|
59
|
+
return result
|
|
60
|
+
|
|
61
|
+
except Exception as e:
|
|
62
|
+
elapsed = time.time() - start_time
|
|
63
|
+
logger.error(f"[{agent_name}] {operation_name} failed after {elapsed:.2f}s: {e}")
|
|
64
|
+
|
|
65
|
+
# Add to audit trail if available
|
|
66
|
+
if hasattr(self, "add_audit_entry"):
|
|
67
|
+
state = kwargs.get("state", {})
|
|
68
|
+
if not state and args:
|
|
69
|
+
# Check if first arg is state-like
|
|
70
|
+
if isinstance(args[0], dict):
|
|
71
|
+
state = args[0]
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
self.add_audit_entry(
|
|
75
|
+
state=state,
|
|
76
|
+
action=f"{operation_name}_error",
|
|
77
|
+
details={
|
|
78
|
+
"error": str(e),
|
|
79
|
+
"error_type": type(e).__name__,
|
|
80
|
+
"elapsed_seconds": elapsed,
|
|
81
|
+
},
|
|
82
|
+
)
|
|
83
|
+
except Exception: # noqa: BLE001
|
|
84
|
+
# INTENTIONAL: Audit trail is optional - don't fail the main operation
|
|
85
|
+
pass
|
|
86
|
+
|
|
87
|
+
raise AgentOperationError(operation_name, e) from e
|
|
88
|
+
|
|
89
|
+
return wrapper # type: ignore
|
|
90
|
+
|
|
91
|
+
return decorator
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def retry_on_failure(
|
|
95
|
+
max_attempts: int = 3,
|
|
96
|
+
delay: float = 1.0,
|
|
97
|
+
backoff: float = 2.0,
|
|
98
|
+
exceptions: tuple = (Exception,),
|
|
99
|
+
):
|
|
100
|
+
"""Decorator to retry failed operations with exponential backoff.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
max_attempts: Maximum number of attempts
|
|
104
|
+
delay: Initial delay between retries (seconds)
|
|
105
|
+
backoff: Multiplier for delay after each retry
|
|
106
|
+
exceptions: Tuple of exception types to catch and retry
|
|
107
|
+
|
|
108
|
+
Example:
|
|
109
|
+
@retry_on_failure(max_attempts=3, delay=1.0)
|
|
110
|
+
async def call_external_api(self):
|
|
111
|
+
# Flaky operation
|
|
112
|
+
pass
|
|
113
|
+
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
def decorator(func: F) -> F:
|
|
117
|
+
@wraps(func)
|
|
118
|
+
async def wrapper(*args, **kwargs):
|
|
119
|
+
last_exception = None
|
|
120
|
+
current_delay = delay
|
|
121
|
+
|
|
122
|
+
for attempt in range(max_attempts):
|
|
123
|
+
try:
|
|
124
|
+
return await func(*args, **kwargs)
|
|
125
|
+
except exceptions as e:
|
|
126
|
+
last_exception = e
|
|
127
|
+
if attempt < max_attempts - 1:
|
|
128
|
+
logger.warning(
|
|
129
|
+
f"Attempt {attempt + 1}/{max_attempts} failed: {e}. "
|
|
130
|
+
f"Retrying in {current_delay:.1f}s...",
|
|
131
|
+
)
|
|
132
|
+
await asyncio.sleep(current_delay)
|
|
133
|
+
current_delay *= backoff
|
|
134
|
+
else:
|
|
135
|
+
logger.error(f"All {max_attempts} attempts failed. Last error: {e}")
|
|
136
|
+
|
|
137
|
+
raise last_exception
|
|
138
|
+
|
|
139
|
+
return wrapper # type: ignore
|
|
140
|
+
|
|
141
|
+
return decorator
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def log_performance(threshold_seconds: float = 1.0):
|
|
145
|
+
"""Decorator to log slow operations.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
threshold_seconds: Log warning if operation exceeds this duration
|
|
149
|
+
|
|
150
|
+
Example:
|
|
151
|
+
@log_performance(threshold_seconds=2.0)
|
|
152
|
+
async def heavy_computation(self):
|
|
153
|
+
# Potentially slow operation
|
|
154
|
+
pass
|
|
155
|
+
|
|
156
|
+
"""
|
|
157
|
+
|
|
158
|
+
def decorator(func: F) -> F:
|
|
159
|
+
@wraps(func)
|
|
160
|
+
async def wrapper(*args, **kwargs):
|
|
161
|
+
start_time = time.time()
|
|
162
|
+
|
|
163
|
+
result = await func(*args, **kwargs)
|
|
164
|
+
|
|
165
|
+
elapsed = time.time() - start_time
|
|
166
|
+
func_name = func.__name__
|
|
167
|
+
|
|
168
|
+
if elapsed > threshold_seconds:
|
|
169
|
+
logger.warning(
|
|
170
|
+
f"Slow operation: {func_name} took {elapsed:.2f}s "
|
|
171
|
+
f"(threshold: {threshold_seconds}s)",
|
|
172
|
+
)
|
|
173
|
+
else:
|
|
174
|
+
logger.debug(f"{func_name} completed in {elapsed:.2f}s")
|
|
175
|
+
|
|
176
|
+
return result
|
|
177
|
+
|
|
178
|
+
return wrapper # type: ignore
|
|
179
|
+
|
|
180
|
+
return decorator
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def validate_input(required_fields: list[str]):
|
|
184
|
+
"""Decorator to validate required fields in input data.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
required_fields: List of required field names
|
|
188
|
+
|
|
189
|
+
Example:
|
|
190
|
+
@validate_input(["query", "context"])
|
|
191
|
+
async def process(self, input_data: dict):
|
|
192
|
+
# input_data is guaranteed to have query and context
|
|
193
|
+
pass
|
|
194
|
+
|
|
195
|
+
"""
|
|
196
|
+
|
|
197
|
+
def decorator(func: F) -> F:
|
|
198
|
+
@wraps(func)
|
|
199
|
+
async def wrapper(self, input_data, *args, **kwargs):
|
|
200
|
+
if not isinstance(input_data, dict):
|
|
201
|
+
raise ValueError(f"Input must be a dict, got {type(input_data).__name__}")
|
|
202
|
+
|
|
203
|
+
missing = [f for f in required_fields if f not in input_data]
|
|
204
|
+
if missing:
|
|
205
|
+
raise ValueError(f"Missing required fields: {missing}")
|
|
206
|
+
|
|
207
|
+
return await func(self, input_data, *args, **kwargs)
|
|
208
|
+
|
|
209
|
+
return wrapper # type: ignore
|
|
210
|
+
|
|
211
|
+
return decorator
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def with_cost_tracking(operation_type: str = "agent_call"):
|
|
215
|
+
"""Decorator to track API costs for operations.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
operation_type: Type of operation for cost categorization
|
|
219
|
+
|
|
220
|
+
Example:
|
|
221
|
+
@with_cost_tracking(operation_type="research")
|
|
222
|
+
async def research(self, query: str):
|
|
223
|
+
# LLM call that should be tracked
|
|
224
|
+
pass
|
|
225
|
+
|
|
226
|
+
"""
|
|
227
|
+
|
|
228
|
+
def decorator(func: F) -> F:
|
|
229
|
+
@wraps(func)
|
|
230
|
+
async def wrapper(self, *args, **kwargs):
|
|
231
|
+
# Record start
|
|
232
|
+
operation_id = f"{operation_type}_{time.time_ns()}"
|
|
233
|
+
|
|
234
|
+
result = await func(self, *args, **kwargs)
|
|
235
|
+
|
|
236
|
+
# Extract token usage if available
|
|
237
|
+
if isinstance(result, dict):
|
|
238
|
+
metadata = result.get("metadata", {})
|
|
239
|
+
tokens_used = metadata.get("tokens_used", 0)
|
|
240
|
+
model = metadata.get("model", "unknown")
|
|
241
|
+
|
|
242
|
+
# Log for cost tracking
|
|
243
|
+
if hasattr(self, "_track_cost"):
|
|
244
|
+
self._track_cost(
|
|
245
|
+
operation_id=operation_id,
|
|
246
|
+
operation_type=operation_type,
|
|
247
|
+
model=model,
|
|
248
|
+
tokens=tokens_used,
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
return result
|
|
252
|
+
|
|
253
|
+
return wrapper # type: ignore
|
|
254
|
+
|
|
255
|
+
return decorator
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def graceful_degradation(fallback_value: Any = None, log_level: str = "warning"):
|
|
259
|
+
"""Decorator for graceful degradation on failure.
|
|
260
|
+
|
|
261
|
+
Instead of raising an exception, returns a fallback value.
|
|
262
|
+
|
|
263
|
+
Args:
|
|
264
|
+
fallback_value: Value to return on failure
|
|
265
|
+
log_level: Log level for the failure message
|
|
266
|
+
|
|
267
|
+
Example:
|
|
268
|
+
@graceful_degradation(fallback_value=[], log_level="warning")
|
|
269
|
+
async def get_optional_data(self):
|
|
270
|
+
# If this fails, return empty list instead of crashing
|
|
271
|
+
pass
|
|
272
|
+
|
|
273
|
+
"""
|
|
274
|
+
|
|
275
|
+
def decorator(func: F) -> F:
|
|
276
|
+
@wraps(func)
|
|
277
|
+
async def wrapper(*args, **kwargs):
|
|
278
|
+
try:
|
|
279
|
+
return await func(*args, **kwargs)
|
|
280
|
+
except Exception as e:
|
|
281
|
+
log_func = getattr(logger, log_level.lower(), logger.warning)
|
|
282
|
+
log_func(f"{func.__name__} failed, using fallback: {e}")
|
|
283
|
+
return fallback_value
|
|
284
|
+
|
|
285
|
+
return wrapper # type: ignore
|
|
286
|
+
|
|
287
|
+
return decorator
|