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,483 @@
|
|
|
1
|
+
"""CrewAI Adapter
|
|
2
|
+
|
|
3
|
+
Creates agents using CrewAI's role/goal/backstory pattern while integrating
|
|
4
|
+
with Empathy's cost optimization and pattern learning.
|
|
5
|
+
|
|
6
|
+
CrewAI is a multi-agent framework focusing on:
|
|
7
|
+
- Role-based agents with goals and backstories
|
|
8
|
+
- Hierarchical and sequential crew orchestration
|
|
9
|
+
- Task delegation and collaboration
|
|
10
|
+
|
|
11
|
+
Requires: pip install crewai
|
|
12
|
+
|
|
13
|
+
Copyright 2025 Smart-AI-Memory
|
|
14
|
+
Licensed under Fair Source License 0.9
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import asyncio
|
|
18
|
+
import os
|
|
19
|
+
from collections.abc import Callable
|
|
20
|
+
from typing import Any
|
|
21
|
+
|
|
22
|
+
from attune_llm.agent_factory.base import (
|
|
23
|
+
AgentConfig,
|
|
24
|
+
AgentRole,
|
|
25
|
+
BaseAdapter,
|
|
26
|
+
BaseAgent,
|
|
27
|
+
BaseWorkflow,
|
|
28
|
+
WorkflowConfig,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# Lazy imports for CrewAI
|
|
32
|
+
_crewai_available = None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _check_crewai():
|
|
36
|
+
"""Check if CrewAI is available."""
|
|
37
|
+
global _crewai_available
|
|
38
|
+
if _crewai_available is None:
|
|
39
|
+
try:
|
|
40
|
+
import crewai # noqa: F401
|
|
41
|
+
|
|
42
|
+
_crewai_available = True
|
|
43
|
+
except (ImportError, AttributeError):
|
|
44
|
+
# INTENTIONAL: Catch AttributeError for CrewAI 0.203.x RAG module import issues
|
|
45
|
+
# ImportError: CrewAI not installed
|
|
46
|
+
# AttributeError: CrewAI 0.203.x has RAG module attribute protection issues
|
|
47
|
+
_crewai_available = False
|
|
48
|
+
return _crewai_available
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class CrewAIAgent(BaseAgent):
|
|
52
|
+
"""Agent wrapping a CrewAI Agent."""
|
|
53
|
+
|
|
54
|
+
def __init__(self, config: AgentConfig, crewai_agent=None):
|
|
55
|
+
"""Initialize CrewAI agent wrapper.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
config: Agent configuration
|
|
59
|
+
crewai_agent: The underlying CrewAI Agent instance
|
|
60
|
+
|
|
61
|
+
"""
|
|
62
|
+
super().__init__(config)
|
|
63
|
+
self._crewai_agent = crewai_agent
|
|
64
|
+
|
|
65
|
+
async def invoke(self, input_data: str | dict, context: dict | None = None) -> dict:
|
|
66
|
+
"""Invoke the CrewAI agent.
|
|
67
|
+
|
|
68
|
+
CrewAI agents work within crews/tasks, so we create a temporary
|
|
69
|
+
task for standalone invocation.
|
|
70
|
+
"""
|
|
71
|
+
if not self._crewai_agent:
|
|
72
|
+
return {"output": "No CrewAI agent configured", "metadata": {}}
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
from crewai import Task
|
|
76
|
+
|
|
77
|
+
# Format input as task description
|
|
78
|
+
if isinstance(input_data, str):
|
|
79
|
+
description = input_data
|
|
80
|
+
else:
|
|
81
|
+
description = input_data.get("task", input_data.get("input", str(input_data)))
|
|
82
|
+
|
|
83
|
+
# Add context to description if provided
|
|
84
|
+
if context:
|
|
85
|
+
context_str = "\n".join(f"{k}: {v}" for k, v in context.items())
|
|
86
|
+
description = f"{description}\n\nContext:\n{context_str}"
|
|
87
|
+
|
|
88
|
+
# Create and execute task
|
|
89
|
+
task = Task(
|
|
90
|
+
description=description,
|
|
91
|
+
expected_output="A comprehensive response addressing the task.",
|
|
92
|
+
agent=self._crewai_agent,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# CrewAI is primarily sync, so run in executor
|
|
96
|
+
loop = asyncio.get_event_loop()
|
|
97
|
+
result = await loop.run_in_executor(None, self._execute_task, task)
|
|
98
|
+
|
|
99
|
+
output = str(result) if result else "Task completed"
|
|
100
|
+
|
|
101
|
+
# Track conversation
|
|
102
|
+
self._conversation_history.append({"role": "user", "content": description})
|
|
103
|
+
self._conversation_history.append({"role": "assistant", "content": output})
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
"output": output,
|
|
107
|
+
"metadata": {
|
|
108
|
+
"model": self.model,
|
|
109
|
+
"framework": "crewai",
|
|
110
|
+
"agent_role": self._crewai_agent.role if self._crewai_agent else None,
|
|
111
|
+
},
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
except Exception as e:
|
|
115
|
+
return {
|
|
116
|
+
"output": f"Error: {e!s}",
|
|
117
|
+
"metadata": {"error": str(e), "framework": "crewai"},
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
def _execute_task(self, task):
|
|
121
|
+
"""Execute a CrewAI task synchronously."""
|
|
122
|
+
try:
|
|
123
|
+
# In newer versions of CrewAI, tasks are executed via Crew
|
|
124
|
+
# For standalone execution, we use the agent's execute_task method
|
|
125
|
+
if hasattr(task, "execute"):
|
|
126
|
+
return task.execute()
|
|
127
|
+
if hasattr(self._crewai_agent, "execute_task"):
|
|
128
|
+
return self._crewai_agent.execute_task(task)
|
|
129
|
+
# Fallback: create minimal crew
|
|
130
|
+
from crewai import Crew
|
|
131
|
+
|
|
132
|
+
crew = Crew(agents=[self._crewai_agent], tasks=[task], verbose=False)
|
|
133
|
+
return crew.kickoff()
|
|
134
|
+
except Exception as e:
|
|
135
|
+
return f"Task execution error: {e}"
|
|
136
|
+
|
|
137
|
+
async def stream(self, input_data: str | dict, context: dict | None = None):
|
|
138
|
+
"""Stream CrewAI response.
|
|
139
|
+
|
|
140
|
+
Note: CrewAI doesn't natively support streaming, so we simulate
|
|
141
|
+
by yielding the complete response.
|
|
142
|
+
"""
|
|
143
|
+
result = await self.invoke(input_data, context)
|
|
144
|
+
yield result
|
|
145
|
+
|
|
146
|
+
@property
|
|
147
|
+
def crewai_agent(self):
|
|
148
|
+
"""Get the underlying CrewAI agent."""
|
|
149
|
+
return self._crewai_agent
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class CrewAIWorkflow(BaseWorkflow):
|
|
153
|
+
"""Workflow using CrewAI's Crew orchestration."""
|
|
154
|
+
|
|
155
|
+
def __init__(self, config: WorkflowConfig, agents: list[BaseAgent], crew=None):
|
|
156
|
+
"""Initialize CrewAI workflow.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
config: Workflow configuration
|
|
160
|
+
agents: List of CrewAIAgent instances
|
|
161
|
+
crew: Optional pre-built CrewAI Crew instance
|
|
162
|
+
|
|
163
|
+
"""
|
|
164
|
+
super().__init__(config, agents)
|
|
165
|
+
self._crew = crew
|
|
166
|
+
self._tasks: list = []
|
|
167
|
+
|
|
168
|
+
async def run(self, input_data: str | dict, initial_state: dict | None = None) -> dict:
|
|
169
|
+
"""Run the CrewAI crew workflow."""
|
|
170
|
+
self._state = initial_state or {}
|
|
171
|
+
|
|
172
|
+
if not self._crew:
|
|
173
|
+
return {"output": "No CrewAI Crew configured", "error": "Missing crew"}
|
|
174
|
+
|
|
175
|
+
try:
|
|
176
|
+
# Format input
|
|
177
|
+
if isinstance(input_data, str):
|
|
178
|
+
inputs = {"input": input_data}
|
|
179
|
+
else:
|
|
180
|
+
inputs = dict(input_data)
|
|
181
|
+
|
|
182
|
+
# Add state to inputs
|
|
183
|
+
inputs.update(self._state)
|
|
184
|
+
|
|
185
|
+
# Run crew in executor (CrewAI is sync)
|
|
186
|
+
loop = asyncio.get_event_loop()
|
|
187
|
+
result = await loop.run_in_executor(None, self._crew.kickoff, inputs)
|
|
188
|
+
|
|
189
|
+
output = str(result) if result else "Crew completed"
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
"output": output,
|
|
193
|
+
"results": [{"crew_result": str(result)}],
|
|
194
|
+
"state": self._state,
|
|
195
|
+
"metadata": {"framework": "crewai"},
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
except Exception as e:
|
|
199
|
+
return {
|
|
200
|
+
"output": f"Crew execution error: {e}",
|
|
201
|
+
"error": str(e),
|
|
202
|
+
"state": self._state,
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async def stream(self, input_data: str | dict, initial_state: dict | None = None):
|
|
206
|
+
"""Stream workflow execution.
|
|
207
|
+
|
|
208
|
+
Note: CrewAI doesn't support streaming, so we yield progress updates.
|
|
209
|
+
"""
|
|
210
|
+
yield {"event": "crew_start", "crew": self.config.name}
|
|
211
|
+
|
|
212
|
+
result = await self.run(input_data, initial_state)
|
|
213
|
+
|
|
214
|
+
yield {"event": "crew_end", "result": result}
|
|
215
|
+
|
|
216
|
+
def add_task(self, description: str, expected_output: str, agent_name: str) -> None:
|
|
217
|
+
"""Add a task to the workflow.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
description: Task description
|
|
221
|
+
expected_output: Expected output description
|
|
222
|
+
agent_name: Name of agent to assign task to
|
|
223
|
+
|
|
224
|
+
"""
|
|
225
|
+
if not _check_crewai():
|
|
226
|
+
return
|
|
227
|
+
|
|
228
|
+
from crewai import Task
|
|
229
|
+
|
|
230
|
+
agent = self.agents.get(agent_name)
|
|
231
|
+
if agent and hasattr(agent, "crewai_agent"):
|
|
232
|
+
task = Task(
|
|
233
|
+
description=description,
|
|
234
|
+
expected_output=expected_output,
|
|
235
|
+
agent=agent.crewai_agent,
|
|
236
|
+
)
|
|
237
|
+
self._tasks.append(task)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
class CrewAIAdapter(BaseAdapter):
|
|
241
|
+
"""Adapter for CrewAI framework."""
|
|
242
|
+
|
|
243
|
+
def __init__(self, provider: str = "openai", api_key: str | None = None):
|
|
244
|
+
"""Initialize CrewAI adapter.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
provider: LLM provider (openai is default for CrewAI)
|
|
248
|
+
api_key: API key (uses env var if not provided)
|
|
249
|
+
|
|
250
|
+
"""
|
|
251
|
+
self.provider = provider
|
|
252
|
+
self.api_key = api_key or os.getenv(
|
|
253
|
+
"OPENAI_API_KEY" if provider == "openai" else "ANTHROPIC_API_KEY",
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
@property
|
|
257
|
+
def framework_name(self) -> str:
|
|
258
|
+
return "crewai"
|
|
259
|
+
|
|
260
|
+
def is_available(self) -> bool:
|
|
261
|
+
"""Check if CrewAI is installed."""
|
|
262
|
+
return bool(_check_crewai())
|
|
263
|
+
|
|
264
|
+
def create_agent(self, config: AgentConfig) -> CrewAIAgent:
|
|
265
|
+
"""Create a CrewAI-based agent."""
|
|
266
|
+
if not self.is_available():
|
|
267
|
+
raise ImportError("CrewAI not installed. Run: pip install crewai")
|
|
268
|
+
|
|
269
|
+
from crewai import Agent
|
|
270
|
+
|
|
271
|
+
# Map Empathy role to CrewAI role string
|
|
272
|
+
role = self._map_role(config.role)
|
|
273
|
+
|
|
274
|
+
# Generate goal based on role and description
|
|
275
|
+
goal = config.description or self._default_goal(config.role)
|
|
276
|
+
|
|
277
|
+
# Generate backstory from system prompt or role
|
|
278
|
+
backstory = config.system_prompt or self._default_backstory(config.role)
|
|
279
|
+
|
|
280
|
+
# Convert tools to CrewAI format
|
|
281
|
+
crewai_tools = [self._convert_tool(t) for t in config.tools if t]
|
|
282
|
+
crewai_tools = [t for t in crewai_tools if t is not None]
|
|
283
|
+
|
|
284
|
+
# Create CrewAI agent
|
|
285
|
+
crewai_agent = Agent(
|
|
286
|
+
role=role,
|
|
287
|
+
goal=goal,
|
|
288
|
+
backstory=backstory,
|
|
289
|
+
tools=crewai_tools if crewai_tools else None,
|
|
290
|
+
verbose=False,
|
|
291
|
+
allow_delegation=config.role == AgentRole.COORDINATOR,
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
return CrewAIAgent(config, crewai_agent)
|
|
295
|
+
|
|
296
|
+
def create_workflow(self, config: WorkflowConfig, agents: list[BaseAgent]) -> CrewAIWorkflow:
|
|
297
|
+
"""Create a CrewAI Crew workflow."""
|
|
298
|
+
if not self.is_available():
|
|
299
|
+
raise ImportError("CrewAI not installed")
|
|
300
|
+
|
|
301
|
+
from crewai import Crew, Process
|
|
302
|
+
|
|
303
|
+
# Determine process type from workflow mode
|
|
304
|
+
if config.mode == "hierarchical":
|
|
305
|
+
process = Process.hierarchical
|
|
306
|
+
else:
|
|
307
|
+
process = Process.sequential
|
|
308
|
+
|
|
309
|
+
# Extract CrewAI agents from wrappers
|
|
310
|
+
crewai_agents = []
|
|
311
|
+
for agent in agents:
|
|
312
|
+
if isinstance(agent, CrewAIAgent) and agent.crewai_agent:
|
|
313
|
+
crewai_agents.append(agent.crewai_agent)
|
|
314
|
+
|
|
315
|
+
if not crewai_agents:
|
|
316
|
+
return CrewAIWorkflow(config, agents)
|
|
317
|
+
|
|
318
|
+
# Create Crew with manager_llm for hierarchical process
|
|
319
|
+
crew_kwargs = {
|
|
320
|
+
"agents": crewai_agents,
|
|
321
|
+
"tasks": [], # Tasks will be added dynamically or via add_task
|
|
322
|
+
"process": process,
|
|
323
|
+
"verbose": False,
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
# Hierarchical process requires manager_llm
|
|
327
|
+
if config.mode == "hierarchical":
|
|
328
|
+
# Try langchain_openai first (preferred by CrewAI)
|
|
329
|
+
try:
|
|
330
|
+
from langchain_openai import ChatOpenAI
|
|
331
|
+
|
|
332
|
+
crew_kwargs["manager_llm"] = ChatOpenAI(
|
|
333
|
+
model="gpt-4o-mini",
|
|
334
|
+
temperature=0.7,
|
|
335
|
+
)
|
|
336
|
+
except ImportError:
|
|
337
|
+
# Fallback: Use CrewAI's native LLM class
|
|
338
|
+
from crewai import LLM
|
|
339
|
+
|
|
340
|
+
crew_kwargs["manager_llm"] = LLM(
|
|
341
|
+
model="gpt-4o-mini",
|
|
342
|
+
temperature=0.7,
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
crew = Crew(**crew_kwargs)
|
|
346
|
+
|
|
347
|
+
return CrewAIWorkflow(config, agents, crew)
|
|
348
|
+
|
|
349
|
+
def create_tool(
|
|
350
|
+
self,
|
|
351
|
+
name: str,
|
|
352
|
+
description: str,
|
|
353
|
+
func: Callable,
|
|
354
|
+
args_schema: dict | None = None,
|
|
355
|
+
) -> Any:
|
|
356
|
+
"""Create a CrewAI tool."""
|
|
357
|
+
if not self.is_available():
|
|
358
|
+
return super().create_tool(name, description, func, args_schema)
|
|
359
|
+
|
|
360
|
+
try:
|
|
361
|
+
from crewai.tools import BaseTool
|
|
362
|
+
|
|
363
|
+
# Capture function parameters in closure variables
|
|
364
|
+
tool_name = name
|
|
365
|
+
tool_description = description
|
|
366
|
+
tool_func = func
|
|
367
|
+
|
|
368
|
+
# Create dynamic tool class
|
|
369
|
+
class DynamicTool(BaseTool):
|
|
370
|
+
name: str = tool_name
|
|
371
|
+
description: str = tool_description
|
|
372
|
+
|
|
373
|
+
def _run(self, **kwargs) -> str:
|
|
374
|
+
return str(tool_func(**kwargs))
|
|
375
|
+
|
|
376
|
+
return DynamicTool()
|
|
377
|
+
except ImportError:
|
|
378
|
+
# Fallback to dict format
|
|
379
|
+
return {"name": name, "description": description, "func": func}
|
|
380
|
+
|
|
381
|
+
def _convert_tool(self, tool: Any) -> Any:
|
|
382
|
+
"""Convert a tool to CrewAI format."""
|
|
383
|
+
if not self.is_available():
|
|
384
|
+
return None
|
|
385
|
+
|
|
386
|
+
# If already a CrewAI tool, return as-is
|
|
387
|
+
try:
|
|
388
|
+
from crewai.tools import BaseTool
|
|
389
|
+
|
|
390
|
+
if isinstance(tool, BaseTool):
|
|
391
|
+
return tool
|
|
392
|
+
except ImportError:
|
|
393
|
+
pass
|
|
394
|
+
|
|
395
|
+
# If dict, convert to CrewAI tool
|
|
396
|
+
if isinstance(tool, dict):
|
|
397
|
+
return self.create_tool(
|
|
398
|
+
name=tool.get("name", "tool"),
|
|
399
|
+
description=tool.get("description", ""),
|
|
400
|
+
func=tool.get("func", lambda: None),
|
|
401
|
+
args_schema=tool.get("args_schema"),
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
return None
|
|
405
|
+
|
|
406
|
+
def _map_role(self, role: AgentRole) -> str:
|
|
407
|
+
"""Map Empathy AgentRole to CrewAI role string."""
|
|
408
|
+
role_map = {
|
|
409
|
+
AgentRole.COORDINATOR: "Project Manager",
|
|
410
|
+
AgentRole.RESEARCHER: "Senior Researcher",
|
|
411
|
+
AgentRole.WRITER: "Content Writer",
|
|
412
|
+
AgentRole.REVIEWER: "Quality Reviewer",
|
|
413
|
+
AgentRole.EDITOR: "Editor",
|
|
414
|
+
AgentRole.EXECUTOR: "Task Executor",
|
|
415
|
+
AgentRole.DEBUGGER: "Software Debugger",
|
|
416
|
+
AgentRole.SECURITY: "Security Analyst",
|
|
417
|
+
AgentRole.ARCHITECT: "Software Architect",
|
|
418
|
+
AgentRole.TESTER: "QA Tester",
|
|
419
|
+
AgentRole.DOCUMENTER: "Technical Writer",
|
|
420
|
+
AgentRole.RETRIEVER: "Information Retriever",
|
|
421
|
+
AgentRole.SUMMARIZER: "Content Summarizer",
|
|
422
|
+
AgentRole.ANSWERER: "Question Answerer",
|
|
423
|
+
AgentRole.CUSTOM: "Specialist",
|
|
424
|
+
}
|
|
425
|
+
return role_map.get(role, "Specialist")
|
|
426
|
+
|
|
427
|
+
def _default_goal(self, role: AgentRole) -> str:
|
|
428
|
+
"""Generate default goal based on role."""
|
|
429
|
+
goal_map = {
|
|
430
|
+
AgentRole.COORDINATOR: "Coordinate team efforts and ensure project success",
|
|
431
|
+
AgentRole.RESEARCHER: "Conduct thorough research and gather comprehensive information",
|
|
432
|
+
AgentRole.WRITER: "Create clear, engaging, and high-quality content",
|
|
433
|
+
AgentRole.REVIEWER: "Provide constructive feedback and ensure quality standards",
|
|
434
|
+
AgentRole.EDITOR: "Refine and improve content for clarity and impact",
|
|
435
|
+
AgentRole.EXECUTOR: "Execute tasks efficiently and report results accurately",
|
|
436
|
+
AgentRole.DEBUGGER: "Identify and resolve software bugs systematically",
|
|
437
|
+
AgentRole.SECURITY: "Analyze systems for security vulnerabilities",
|
|
438
|
+
AgentRole.ARCHITECT: "Design robust and scalable system architectures",
|
|
439
|
+
AgentRole.TESTER: "Create comprehensive tests and ensure software quality",
|
|
440
|
+
AgentRole.DOCUMENTER: "Create clear and comprehensive documentation",
|
|
441
|
+
AgentRole.RETRIEVER: "Retrieve relevant information from knowledge bases",
|
|
442
|
+
AgentRole.SUMMARIZER: "Create concise and accurate summaries",
|
|
443
|
+
AgentRole.ANSWERER: "Provide accurate answers to questions",
|
|
444
|
+
AgentRole.CUSTOM: "Complete assigned tasks with excellence",
|
|
445
|
+
}
|
|
446
|
+
return goal_map.get(role, "Complete assigned tasks with excellence")
|
|
447
|
+
|
|
448
|
+
def _default_backstory(self, role: AgentRole) -> str:
|
|
449
|
+
"""Generate default backstory based on role."""
|
|
450
|
+
backstory_map = {
|
|
451
|
+
AgentRole.COORDINATOR: (
|
|
452
|
+
"You are an experienced project manager with a track record of "
|
|
453
|
+
"successfully coordinating complex projects and diverse teams."
|
|
454
|
+
),
|
|
455
|
+
AgentRole.RESEARCHER: (
|
|
456
|
+
"You are a meticulous researcher with expertise in finding "
|
|
457
|
+
"reliable information and synthesizing complex topics."
|
|
458
|
+
),
|
|
459
|
+
AgentRole.WRITER: (
|
|
460
|
+
"You are a skilled writer with years of experience crafting "
|
|
461
|
+
"compelling narratives and clear technical content."
|
|
462
|
+
),
|
|
463
|
+
AgentRole.REVIEWER: (
|
|
464
|
+
"You are a seasoned reviewer known for your attention to detail "
|
|
465
|
+
"and constructive feedback that improves quality."
|
|
466
|
+
),
|
|
467
|
+
AgentRole.DEBUGGER: (
|
|
468
|
+
"You are an expert debugger with deep knowledge of software systems "
|
|
469
|
+
"and a systematic approach to problem-solving."
|
|
470
|
+
),
|
|
471
|
+
AgentRole.SECURITY: (
|
|
472
|
+
"You are a security analyst with expertise in identifying "
|
|
473
|
+
"vulnerabilities and recommending protective measures."
|
|
474
|
+
),
|
|
475
|
+
AgentRole.ARCHITECT: (
|
|
476
|
+
"You are a software architect with experience designing "
|
|
477
|
+
"scalable, maintainable systems for enterprise applications."
|
|
478
|
+
),
|
|
479
|
+
}
|
|
480
|
+
return backstory_map.get(
|
|
481
|
+
role,
|
|
482
|
+
f"You are an experienced professional excelling as a {role.value}.",
|
|
483
|
+
)
|