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
attune/discovery.py
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
"""Progressive Feature Discovery for Empathy Framework
|
|
2
|
+
|
|
3
|
+
Surface tips and suggestions at the right time based on usage patterns.
|
|
4
|
+
Helps users discover power-user features without overwhelming them upfront.
|
|
5
|
+
|
|
6
|
+
Copyright 2025 Smart-AI-Memory
|
|
7
|
+
Licensed under Fair Source License 0.9
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
from attune.config import _validate_file_path
|
|
15
|
+
|
|
16
|
+
# Discovery tips with conditions
|
|
17
|
+
DISCOVERY_TIPS = {
|
|
18
|
+
# After first commands
|
|
19
|
+
"after_first_inspect": {
|
|
20
|
+
"tip": "Try 'empathy ship' before commits for automated pre-flight checks",
|
|
21
|
+
"trigger": "inspect",
|
|
22
|
+
"min_uses": 1,
|
|
23
|
+
"priority": 2,
|
|
24
|
+
"shown": False,
|
|
25
|
+
},
|
|
26
|
+
"after_first_health": {
|
|
27
|
+
"tip": "Use 'empathy fix-all' to auto-fix lint and format issues",
|
|
28
|
+
"trigger": "health",
|
|
29
|
+
"min_uses": 1,
|
|
30
|
+
"priority": 2,
|
|
31
|
+
"shown": False,
|
|
32
|
+
},
|
|
33
|
+
# After accumulating usage
|
|
34
|
+
"after_10_inspects": {
|
|
35
|
+
"tip": "You've got patterns! Run 'empathy sync-claude' to share them with Claude Code",
|
|
36
|
+
"trigger": "inspect",
|
|
37
|
+
"min_uses": 10,
|
|
38
|
+
"priority": 1,
|
|
39
|
+
"shown": False,
|
|
40
|
+
},
|
|
41
|
+
"after_5_ships": {
|
|
42
|
+
"tip": "Start your day with 'empathy morning' for a productivity briefing",
|
|
43
|
+
"trigger": "ship",
|
|
44
|
+
"min_uses": 5,
|
|
45
|
+
"priority": 1,
|
|
46
|
+
"shown": False,
|
|
47
|
+
},
|
|
48
|
+
# Context-based tips
|
|
49
|
+
"high_tech_debt": {
|
|
50
|
+
"tip": "Tech debt is trending up. Try 'empathy status' for priority focus areas",
|
|
51
|
+
"condition": lambda stats: stats.get("tech_debt_trend") == "increasing",
|
|
52
|
+
"priority": 1,
|
|
53
|
+
"shown": False,
|
|
54
|
+
},
|
|
55
|
+
"no_patterns": {
|
|
56
|
+
"tip": "Run 'empathy learn' to extract patterns from your git history",
|
|
57
|
+
"condition": lambda stats: stats.get("patterns_learned", 0) == 0
|
|
58
|
+
and stats.get("total_commands", 0) > 5,
|
|
59
|
+
"priority": 1,
|
|
60
|
+
"shown": False,
|
|
61
|
+
},
|
|
62
|
+
"cost_savings": {
|
|
63
|
+
"tip": "Check your API savings with 'empathy costs' - model routing can save 80%!",
|
|
64
|
+
"condition": lambda stats: stats.get("api_requests", 0) > 10,
|
|
65
|
+
"priority": 2,
|
|
66
|
+
"shown": False,
|
|
67
|
+
},
|
|
68
|
+
# Weekly reminders
|
|
69
|
+
"weekly_sync": {
|
|
70
|
+
"tip": "Weekly reminder: Run 'empathy sync-claude' to keep Claude Code patterns current",
|
|
71
|
+
"condition": lambda stats: _days_since_sync(stats) >= 7,
|
|
72
|
+
"priority": 2,
|
|
73
|
+
"shown": False,
|
|
74
|
+
},
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _days_since_sync(stats: dict) -> int:
|
|
79
|
+
"""Calculate days since last Claude sync."""
|
|
80
|
+
last_sync = stats.get("last_claude_sync")
|
|
81
|
+
if not last_sync:
|
|
82
|
+
return 999
|
|
83
|
+
try:
|
|
84
|
+
sync_date = datetime.fromisoformat(last_sync)
|
|
85
|
+
return (datetime.now() - sync_date).days
|
|
86
|
+
except (ValueError, TypeError):
|
|
87
|
+
return 999
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class DiscoveryEngine:
|
|
91
|
+
"""Tracks usage and surfaces contextual tips.
|
|
92
|
+
|
|
93
|
+
Usage:
|
|
94
|
+
engine = DiscoveryEngine()
|
|
95
|
+
engine.record_command("inspect")
|
|
96
|
+
tips = engine.get_pending_tips()
|
|
97
|
+
engine.mark_shown("after_first_inspect")
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
def __init__(self, storage_dir: str = ".empathy"):
|
|
101
|
+
"""Initialize discovery engine."""
|
|
102
|
+
self.storage_dir = Path(storage_dir)
|
|
103
|
+
self.storage_dir.mkdir(parents=True, exist_ok=True)
|
|
104
|
+
self.stats_file = self.storage_dir / "discovery_stats.json"
|
|
105
|
+
self._load()
|
|
106
|
+
|
|
107
|
+
def _load(self) -> None:
|
|
108
|
+
"""Load discovery state from storage."""
|
|
109
|
+
if self.stats_file.exists():
|
|
110
|
+
try:
|
|
111
|
+
with open(self.stats_file) as f:
|
|
112
|
+
self.state = json.load(f)
|
|
113
|
+
except (OSError, json.JSONDecodeError):
|
|
114
|
+
self.state = self._default_state()
|
|
115
|
+
else:
|
|
116
|
+
self.state = self._default_state()
|
|
117
|
+
|
|
118
|
+
def _default_state(self) -> dict:
|
|
119
|
+
"""Return default state structure."""
|
|
120
|
+
return {
|
|
121
|
+
"command_counts": {},
|
|
122
|
+
"tips_shown": [],
|
|
123
|
+
"total_commands": 0,
|
|
124
|
+
"patterns_learned": 0,
|
|
125
|
+
"api_requests": 0,
|
|
126
|
+
"tech_debt_trend": "unknown",
|
|
127
|
+
"last_claude_sync": None,
|
|
128
|
+
"first_run": datetime.now().isoformat(),
|
|
129
|
+
"last_updated": datetime.now().isoformat(),
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
def _save(self) -> None:
|
|
133
|
+
"""Save state to storage."""
|
|
134
|
+
self.state["last_updated"] = datetime.now().isoformat()
|
|
135
|
+
validated_path = _validate_file_path(str(self.stats_file))
|
|
136
|
+
with open(validated_path, "w") as f:
|
|
137
|
+
json.dump(self.state, f, indent=2)
|
|
138
|
+
|
|
139
|
+
def record_command(self, command: str) -> list:
|
|
140
|
+
"""Record a command execution and return any triggered tips.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
command: The command that was executed
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
List of tip messages to show
|
|
147
|
+
|
|
148
|
+
"""
|
|
149
|
+
# Update counts
|
|
150
|
+
counts = self.state.get("command_counts", {})
|
|
151
|
+
counts[command] = counts.get(command, 0) + 1
|
|
152
|
+
self.state["command_counts"] = counts
|
|
153
|
+
self.state["total_commands"] = self.state.get("total_commands", 0) + 1
|
|
154
|
+
|
|
155
|
+
self._save()
|
|
156
|
+
|
|
157
|
+
# Check for triggered tips
|
|
158
|
+
return self.get_pending_tips(trigger=command)
|
|
159
|
+
|
|
160
|
+
def record_patterns_learned(self, count: int) -> None:
|
|
161
|
+
"""Record patterns learned."""
|
|
162
|
+
self.state["patterns_learned"] = self.state.get("patterns_learned", 0) + count
|
|
163
|
+
self._save()
|
|
164
|
+
|
|
165
|
+
def record_api_request(self) -> None:
|
|
166
|
+
"""Record an API request."""
|
|
167
|
+
self.state["api_requests"] = self.state.get("api_requests", 0) + 1
|
|
168
|
+
self._save()
|
|
169
|
+
|
|
170
|
+
def record_claude_sync(self) -> None:
|
|
171
|
+
"""Record a Claude sync."""
|
|
172
|
+
self.state["last_claude_sync"] = datetime.now().isoformat()
|
|
173
|
+
self._save()
|
|
174
|
+
|
|
175
|
+
def set_tech_debt_trend(self, trend: str) -> None:
|
|
176
|
+
"""Set tech debt trend (increasing/decreasing/stable)."""
|
|
177
|
+
self.state["tech_debt_trend"] = trend
|
|
178
|
+
self._save()
|
|
179
|
+
|
|
180
|
+
def get_pending_tips(self, trigger: str | None = None, max_tips: int = 2) -> list:
|
|
181
|
+
"""Get pending tips based on current state.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
trigger: Command that triggered this check (optional)
|
|
185
|
+
max_tips: Maximum number of tips to return
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
List of tip messages
|
|
189
|
+
|
|
190
|
+
"""
|
|
191
|
+
tips_to_show = []
|
|
192
|
+
shown_tips = set(self.state.get("tips_shown", []))
|
|
193
|
+
|
|
194
|
+
for tip_id, tip_config in DISCOVERY_TIPS.items():
|
|
195
|
+
# Skip if already shown
|
|
196
|
+
if tip_id in shown_tips:
|
|
197
|
+
continue
|
|
198
|
+
|
|
199
|
+
should_show = False
|
|
200
|
+
|
|
201
|
+
# Check trigger-based tips
|
|
202
|
+
if "trigger" in tip_config:
|
|
203
|
+
if trigger == tip_config["trigger"]:
|
|
204
|
+
count = self.state.get("command_counts", {}).get(trigger, 0)
|
|
205
|
+
if count >= tip_config.get("min_uses", 1):
|
|
206
|
+
should_show = True
|
|
207
|
+
|
|
208
|
+
# Check condition-based tips
|
|
209
|
+
elif "condition" in tip_config:
|
|
210
|
+
try:
|
|
211
|
+
condition = tip_config["condition"]
|
|
212
|
+
if callable(condition) and condition(self.state):
|
|
213
|
+
should_show = True
|
|
214
|
+
except Exception:
|
|
215
|
+
pass
|
|
216
|
+
|
|
217
|
+
if should_show:
|
|
218
|
+
tips_to_show.append(
|
|
219
|
+
{
|
|
220
|
+
"id": tip_id,
|
|
221
|
+
"tip": tip_config["tip"],
|
|
222
|
+
"priority": tip_config.get("priority", 3),
|
|
223
|
+
},
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
# Sort by priority and limit - ensure we get an int for sorting
|
|
227
|
+
def get_priority(x: dict) -> int:
|
|
228
|
+
p = x.get("priority", 3)
|
|
229
|
+
return int(p) if isinstance(p, int | float | str) else 3
|
|
230
|
+
|
|
231
|
+
tips_to_show.sort(key=get_priority)
|
|
232
|
+
return tips_to_show[:max_tips]
|
|
233
|
+
|
|
234
|
+
def mark_shown(self, tip_id: str) -> None:
|
|
235
|
+
"""Mark a tip as shown so it won't be repeated."""
|
|
236
|
+
shown = self.state.get("tips_shown", [])
|
|
237
|
+
if tip_id not in shown:
|
|
238
|
+
shown.append(tip_id)
|
|
239
|
+
self.state["tips_shown"] = shown
|
|
240
|
+
self._save()
|
|
241
|
+
|
|
242
|
+
def get_stats(self) -> dict:
|
|
243
|
+
"""Get current discovery statistics."""
|
|
244
|
+
return {
|
|
245
|
+
"total_commands": self.state.get("total_commands", 0),
|
|
246
|
+
"command_counts": self.state.get("command_counts", {}),
|
|
247
|
+
"patterns_learned": self.state.get("patterns_learned", 0),
|
|
248
|
+
"tips_shown": len(self.state.get("tips_shown", [])),
|
|
249
|
+
"tips_remaining": len(DISCOVERY_TIPS) - len(self.state.get("tips_shown", [])),
|
|
250
|
+
"days_active": self._days_active(),
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
def _days_active(self) -> int:
|
|
254
|
+
"""Calculate days since first run."""
|
|
255
|
+
first_run = self.state.get("first_run")
|
|
256
|
+
if not first_run:
|
|
257
|
+
return 0
|
|
258
|
+
try:
|
|
259
|
+
first = datetime.fromisoformat(first_run)
|
|
260
|
+
return (datetime.now() - first).days
|
|
261
|
+
except (ValueError, TypeError):
|
|
262
|
+
return 0
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
# Singleton instance
|
|
266
|
+
_engine: DiscoveryEngine | None = None
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def get_engine(storage_dir: str = ".empathy") -> DiscoveryEngine:
|
|
270
|
+
"""Get or create the global discovery engine."""
|
|
271
|
+
global _engine
|
|
272
|
+
if _engine is None:
|
|
273
|
+
_engine = DiscoveryEngine(storage_dir)
|
|
274
|
+
return _engine
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def show_tip_if_available(command: str, quiet: bool = False) -> None:
|
|
278
|
+
"""Check for tips after a command and display them.
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
command: The command that was just executed
|
|
282
|
+
quiet: If True, don't print anything
|
|
283
|
+
|
|
284
|
+
"""
|
|
285
|
+
engine = get_engine()
|
|
286
|
+
tips = engine.record_command(command)
|
|
287
|
+
|
|
288
|
+
if tips and not quiet:
|
|
289
|
+
print()
|
|
290
|
+
for tip_data in tips:
|
|
291
|
+
print(f" TIP: {tip_data['tip']}")
|
|
292
|
+
engine.mark_shown(tip_data["id"])
|
|
293
|
+
print()
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def format_tips_for_cli(tips: list) -> str:
|
|
297
|
+
"""Format tips for CLI output."""
|
|
298
|
+
if not tips:
|
|
299
|
+
return ""
|
|
300
|
+
|
|
301
|
+
lines = ["\n TIPS", " " + "-" * 38]
|
|
302
|
+
for tip_data in tips:
|
|
303
|
+
lines.append(f" {tip_data['tip']}")
|
|
304
|
+
lines.append("")
|
|
305
|
+
|
|
306
|
+
return "\n".join(lines)
|
attune/emergence.py
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
"""Emergence Detection for AI-Human Collaboration
|
|
2
|
+
|
|
3
|
+
Detects emergent properties in AI-human collaboration - system-level behaviors
|
|
4
|
+
that arise from component interactions but aren't properties of components.
|
|
5
|
+
|
|
6
|
+
Based on systems thinking principles from Donella Meadows and Peter Senge.
|
|
7
|
+
|
|
8
|
+
Copyright 2025 Smart AI Memory, LLC
|
|
9
|
+
Licensed under Fair Source 0.9
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
from datetime import datetime
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class EmergentProperty:
|
|
19
|
+
"""An emergent property discovered in the system
|
|
20
|
+
|
|
21
|
+
Emergent properties are behaviors or patterns that arise from the
|
|
22
|
+
interactions of system components but cannot be predicted from
|
|
23
|
+
the components alone.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
property_type: str # "norm", "pattern", "behavior", "capability"
|
|
27
|
+
description: str
|
|
28
|
+
first_observed: datetime = field(default_factory=datetime.now)
|
|
29
|
+
confidence: float = 0.0 # 0.0-1.0
|
|
30
|
+
evidence: list[dict[str, Any]] = field(default_factory=list)
|
|
31
|
+
components_involved: list[str] = field(default_factory=list)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class EmergenceDetector:
|
|
35
|
+
"""Detects emergent properties in AI-human collaboration
|
|
36
|
+
|
|
37
|
+
Emergent properties are system-level behaviors that arise from
|
|
38
|
+
component interactions but aren't properties of the components themselves.
|
|
39
|
+
|
|
40
|
+
Examples:
|
|
41
|
+
- Team norms that developed organically (not prescribed)
|
|
42
|
+
- Collaboration patterns that emerged from repeated interactions
|
|
43
|
+
- Shared understanding that goes beyond individual knowledge
|
|
44
|
+
- Trust dynamics that affect system behavior
|
|
45
|
+
|
|
46
|
+
Based on systems thinking:
|
|
47
|
+
- Whole is greater than sum of parts
|
|
48
|
+
- Properties emerge at system level
|
|
49
|
+
- Cannot reduce to component analysis
|
|
50
|
+
|
|
51
|
+
Example:
|
|
52
|
+
>>> detector = EmergenceDetector()
|
|
53
|
+
>>> baseline = {"trust": 0.3, "interactions": 10}
|
|
54
|
+
>>> current = {"trust": 0.8, "interactions": 50, "shared_patterns": 5}
|
|
55
|
+
>>> score = detector.measure_emergence(baseline, current)
|
|
56
|
+
>>> print(f"Emergence score: {score:.2f}")
|
|
57
|
+
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
def __init__(self):
|
|
61
|
+
"""Initialize EmergenceDetector with tracking structures"""
|
|
62
|
+
self.detected_properties: list[EmergentProperty] = []
|
|
63
|
+
self.baseline_metrics: dict[str, Any] = {}
|
|
64
|
+
|
|
65
|
+
def detect_emergent_norms(
|
|
66
|
+
self,
|
|
67
|
+
team_interactions: list[dict[str, Any]],
|
|
68
|
+
) -> list[EmergentProperty]:
|
|
69
|
+
"""Detect team norms that emerged organically
|
|
70
|
+
|
|
71
|
+
Analyzes interaction history to identify behavioral patterns that:
|
|
72
|
+
1. Were not explicitly programmed or prescribed
|
|
73
|
+
2. Emerged from repeated interactions
|
|
74
|
+
3. Are now consistently followed by team members
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
team_interactions: List of interaction records with metadata
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
List of detected emergent norms
|
|
81
|
+
|
|
82
|
+
Example:
|
|
83
|
+
>>> interactions = [
|
|
84
|
+
... {"type": "help_request", "response_time": 5},
|
|
85
|
+
... {"type": "help_request", "response_time": 3},
|
|
86
|
+
... {"type": "help_request", "response_time": 4}
|
|
87
|
+
... ]
|
|
88
|
+
>>> norms = detector.detect_emergent_norms(interactions)
|
|
89
|
+
|
|
90
|
+
"""
|
|
91
|
+
norms: list[EmergentProperty] = []
|
|
92
|
+
|
|
93
|
+
if not team_interactions:
|
|
94
|
+
return norms
|
|
95
|
+
|
|
96
|
+
# Detect response time norms
|
|
97
|
+
response_times = [
|
|
98
|
+
i.get("response_time", 0) for i in team_interactions if "response_time" in i
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
if len(response_times) >= 3:
|
|
102
|
+
avg_response = sum(response_times) / len(response_times)
|
|
103
|
+
consistency = self._calculate_consistency(response_times)
|
|
104
|
+
|
|
105
|
+
if consistency > 0.7: # High consistency indicates norm
|
|
106
|
+
norm = EmergentProperty(
|
|
107
|
+
property_type="norm",
|
|
108
|
+
description=f"Response time norm emerged: ~{avg_response:.1f} minutes",
|
|
109
|
+
confidence=consistency,
|
|
110
|
+
evidence=[{"response_times": response_times}],
|
|
111
|
+
components_involved=["ai_agent", "human_user"],
|
|
112
|
+
)
|
|
113
|
+
norms.append(norm)
|
|
114
|
+
|
|
115
|
+
# Detect communication style norms
|
|
116
|
+
communication_patterns = self._analyze_communication_patterns(team_interactions)
|
|
117
|
+
if communication_patterns:
|
|
118
|
+
for pattern_name, pattern_data in communication_patterns.items():
|
|
119
|
+
if pattern_data["frequency"] > 0.6: # Appears in >60% of interactions
|
|
120
|
+
norm = EmergentProperty(
|
|
121
|
+
property_type="norm",
|
|
122
|
+
description=f"Communication pattern emerged: {pattern_name}",
|
|
123
|
+
confidence=pattern_data["frequency"],
|
|
124
|
+
evidence=[pattern_data],
|
|
125
|
+
components_involved=["communication_style"],
|
|
126
|
+
)
|
|
127
|
+
norms.append(norm)
|
|
128
|
+
|
|
129
|
+
self.detected_properties.extend(norms)
|
|
130
|
+
return norms
|
|
131
|
+
|
|
132
|
+
def measure_emergence(self, baseline: dict[str, Any], current: dict[str, Any]) -> float:
|
|
133
|
+
"""Quantify emergence by comparing baseline to current state
|
|
134
|
+
|
|
135
|
+
Measures how much new system-level properties have emerged that
|
|
136
|
+
weren't present in the baseline state.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
baseline: Initial system state metrics
|
|
140
|
+
current: Current system state metrics
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
Emergence score (0.0-1.0), where:
|
|
144
|
+
- 0.0: No emergence (system unchanged)
|
|
145
|
+
- 0.5: Moderate emergence (some new properties)
|
|
146
|
+
- 1.0: High emergence (significant new system capabilities)
|
|
147
|
+
|
|
148
|
+
Example:
|
|
149
|
+
>>> baseline = {"trust": 0.3, "interactions": 10}
|
|
150
|
+
>>> current = {"trust": 0.8, "interactions": 50, "patterns": 5}
|
|
151
|
+
>>> score = detector.measure_emergence(baseline, current)
|
|
152
|
+
|
|
153
|
+
"""
|
|
154
|
+
emergence_score = 0.0
|
|
155
|
+
max_score = 0.0
|
|
156
|
+
|
|
157
|
+
# Measure growth in key metrics
|
|
158
|
+
if "trust" in baseline and "trust" in current:
|
|
159
|
+
trust_growth = (current["trust"] - baseline["trust"]) / max(baseline["trust"], 0.1)
|
|
160
|
+
emergence_score += min(trust_growth, 1.0) * 0.3
|
|
161
|
+
max_score += 0.3
|
|
162
|
+
|
|
163
|
+
# Measure new capabilities
|
|
164
|
+
baseline_keys = set(baseline.keys())
|
|
165
|
+
current_keys = set(current.keys())
|
|
166
|
+
new_capabilities = current_keys - baseline_keys
|
|
167
|
+
|
|
168
|
+
if new_capabilities:
|
|
169
|
+
capability_score = len(new_capabilities) / max(len(baseline_keys), 1)
|
|
170
|
+
emergence_score += min(capability_score, 1.0) * 0.3
|
|
171
|
+
max_score += 0.3
|
|
172
|
+
|
|
173
|
+
# Measure interaction complexity growth
|
|
174
|
+
if "interactions" in baseline and "interactions" in current:
|
|
175
|
+
if baseline["interactions"] > 0:
|
|
176
|
+
interaction_ratio = current["interactions"] / baseline["interactions"]
|
|
177
|
+
complexity_score = min((interaction_ratio - 1.0) / 4.0, 1.0) # Normalize
|
|
178
|
+
emergence_score += complexity_score * 0.2
|
|
179
|
+
max_score += 0.2
|
|
180
|
+
|
|
181
|
+
# Measure pattern development
|
|
182
|
+
if "shared_patterns" in current:
|
|
183
|
+
pattern_score = min(current["shared_patterns"] / 10.0, 1.0)
|
|
184
|
+
emergence_score += pattern_score * 0.2
|
|
185
|
+
max_score += 0.2
|
|
186
|
+
|
|
187
|
+
# Normalize to 0-1 range
|
|
188
|
+
if max_score > 0:
|
|
189
|
+
return min(emergence_score / max_score, 1.0)
|
|
190
|
+
return 0.0
|
|
191
|
+
|
|
192
|
+
def detect_emergent_capabilities(
|
|
193
|
+
self,
|
|
194
|
+
historical_states: list[dict[str, Any]],
|
|
195
|
+
) -> list[EmergentProperty]:
|
|
196
|
+
"""Detect new capabilities that emerged over time
|
|
197
|
+
|
|
198
|
+
Analyzes historical system states to identify capabilities that:
|
|
199
|
+
1. Weren't present initially
|
|
200
|
+
2. Emerged through system evolution
|
|
201
|
+
3. Enable new behaviors
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
historical_states: List of system states over time
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
List of emergent capabilities detected
|
|
208
|
+
|
|
209
|
+
"""
|
|
210
|
+
if len(historical_states) < 2:
|
|
211
|
+
return []
|
|
212
|
+
|
|
213
|
+
capabilities: list[EmergentProperty] = []
|
|
214
|
+
initial_state = historical_states[0]
|
|
215
|
+
|
|
216
|
+
# Track capability development
|
|
217
|
+
for state in historical_states[1:]:
|
|
218
|
+
new_keys = set(state.keys()) - set(initial_state.keys())
|
|
219
|
+
|
|
220
|
+
for key in new_keys:
|
|
221
|
+
capability = EmergentProperty(
|
|
222
|
+
property_type="capability",
|
|
223
|
+
description=f"New capability emerged: {key}",
|
|
224
|
+
confidence=0.8,
|
|
225
|
+
evidence=[{"state": state, "timestamp": state.get("timestamp")}],
|
|
226
|
+
components_involved=["system"],
|
|
227
|
+
)
|
|
228
|
+
capabilities.append(capability)
|
|
229
|
+
|
|
230
|
+
self.detected_properties.extend(capabilities)
|
|
231
|
+
return capabilities
|
|
232
|
+
|
|
233
|
+
def _calculate_consistency(self, values: list[float]) -> float:
|
|
234
|
+
"""Calculate consistency of a set of values (0.0-1.0)
|
|
235
|
+
|
|
236
|
+
Uses coefficient of variation: lower variation = higher consistency
|
|
237
|
+
"""
|
|
238
|
+
if not values or len(values) < 2:
|
|
239
|
+
return 0.0
|
|
240
|
+
|
|
241
|
+
mean = sum(values) / len(values)
|
|
242
|
+
if mean == 0:
|
|
243
|
+
return 0.0
|
|
244
|
+
|
|
245
|
+
variance = sum((x - mean) ** 2 for x in values) / len(values)
|
|
246
|
+
std_dev = variance**0.5
|
|
247
|
+
cv = std_dev / mean # Coefficient of variation
|
|
248
|
+
|
|
249
|
+
# Convert to consistency score (inverse of variation)
|
|
250
|
+
consistency: float = max(0.0, 1.0 - min(cv, 1.0))
|
|
251
|
+
return consistency
|
|
252
|
+
|
|
253
|
+
def _analyze_communication_patterns(
|
|
254
|
+
self,
|
|
255
|
+
interactions: list[dict[str, Any]],
|
|
256
|
+
) -> dict[str, dict[str, Any]]:
|
|
257
|
+
"""Analyze communication patterns in interactions
|
|
258
|
+
|
|
259
|
+
Returns dict of pattern_name -> {frequency, examples}
|
|
260
|
+
"""
|
|
261
|
+
patterns: dict[str, dict[str, Any]] = {}
|
|
262
|
+
total_interactions = len(interactions)
|
|
263
|
+
|
|
264
|
+
if total_interactions == 0:
|
|
265
|
+
return patterns
|
|
266
|
+
|
|
267
|
+
# Detect clarifying questions pattern
|
|
268
|
+
clarifying_count = sum(1 for i in interactions if i.get("type") == "clarifying_question")
|
|
269
|
+
if clarifying_count > 0:
|
|
270
|
+
patterns["clarifying_questions"] = {
|
|
271
|
+
"frequency": clarifying_count / total_interactions,
|
|
272
|
+
"count": clarifying_count,
|
|
273
|
+
"examples": [i for i in interactions if i.get("type") == "clarifying_question"][:3],
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
# Detect proactive suggestions pattern
|
|
277
|
+
proactive_count = sum(1 for i in interactions if i.get("type") == "proactive_suggestion")
|
|
278
|
+
if proactive_count > 0:
|
|
279
|
+
patterns["proactive_suggestions"] = {
|
|
280
|
+
"frequency": proactive_count / total_interactions,
|
|
281
|
+
"count": proactive_count,
|
|
282
|
+
"examples": [i for i in interactions if i.get("type") == "proactive_suggestion"][
|
|
283
|
+
:3
|
|
284
|
+
],
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return patterns
|
|
288
|
+
|
|
289
|
+
def get_detected_properties(self, property_type: str | None = None) -> list[EmergentProperty]:
|
|
290
|
+
"""Get all detected emergent properties, optionally filtered by type
|
|
291
|
+
|
|
292
|
+
Args:
|
|
293
|
+
property_type: Optional filter ("norm", "pattern", "behavior", "capability")
|
|
294
|
+
|
|
295
|
+
Returns:
|
|
296
|
+
List of emergent properties
|
|
297
|
+
|
|
298
|
+
"""
|
|
299
|
+
if property_type:
|
|
300
|
+
return [p for p in self.detected_properties if p.property_type == property_type]
|
|
301
|
+
return self.detected_properties
|
|
302
|
+
|
|
303
|
+
def reset(self):
|
|
304
|
+
"""Reset detector state"""
|
|
305
|
+
self.detected_properties = []
|
|
306
|
+
self.baseline_metrics = {}
|