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,794 @@
|
|
|
1
|
+
"""Socratic Workflow Builder - Main Engine
|
|
2
|
+
|
|
3
|
+
The core engine that orchestrates Socratic questioning for agent generation.
|
|
4
|
+
|
|
5
|
+
Flow:
|
|
6
|
+
1. User provides initial goal (free text)
|
|
7
|
+
2. Engine analyzes goal and generates clarifying questions
|
|
8
|
+
3. User answers questions (forms)
|
|
9
|
+
4. Engine determines if more clarification needed
|
|
10
|
+
5. When ready, generates optimized workflow with agents
|
|
11
|
+
6. Defines success criteria for measuring completion
|
|
12
|
+
|
|
13
|
+
Copyright 2026 Smart-AI-Memory
|
|
14
|
+
Licensed under Fair Source License 0.9
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import logging
|
|
20
|
+
import re
|
|
21
|
+
from dataclasses import dataclass
|
|
22
|
+
from typing import Any
|
|
23
|
+
|
|
24
|
+
from .forms import (
|
|
25
|
+
FieldOption,
|
|
26
|
+
FieldType,
|
|
27
|
+
FieldValidation,
|
|
28
|
+
Form,
|
|
29
|
+
FormField,
|
|
30
|
+
create_additional_context_field,
|
|
31
|
+
create_automation_level_field,
|
|
32
|
+
create_goal_text_field,
|
|
33
|
+
create_language_field,
|
|
34
|
+
create_quality_focus_field,
|
|
35
|
+
create_team_size_field,
|
|
36
|
+
)
|
|
37
|
+
from .generator import AgentGenerator, GeneratedWorkflow
|
|
38
|
+
from .session import GoalAnalysis, SessionState, SocraticSession
|
|
39
|
+
from .success import (
|
|
40
|
+
MetricType,
|
|
41
|
+
SuccessCriteria,
|
|
42
|
+
SuccessMetric,
|
|
43
|
+
code_review_criteria,
|
|
44
|
+
security_audit_criteria,
|
|
45
|
+
test_generation_criteria,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
logger = logging.getLogger(__name__)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# =============================================================================
|
|
52
|
+
# DOMAIN DETECTION
|
|
53
|
+
# =============================================================================
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class DomainPattern:
|
|
58
|
+
"""Pattern for detecting user intent domain."""
|
|
59
|
+
|
|
60
|
+
domain: str
|
|
61
|
+
keywords: list[str]
|
|
62
|
+
phrases: list[str]
|
|
63
|
+
weight: float = 1.0
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
DOMAIN_PATTERNS = [
|
|
67
|
+
DomainPattern(
|
|
68
|
+
domain="code_review",
|
|
69
|
+
keywords=["review", "pr", "pull request", "merge", "diff", "changes"],
|
|
70
|
+
phrases=["code review", "review code", "check my code", "review my"],
|
|
71
|
+
weight=1.0,
|
|
72
|
+
),
|
|
73
|
+
DomainPattern(
|
|
74
|
+
domain="security",
|
|
75
|
+
keywords=["security", "vulnerability", "secure", "exploit", "attack", "owasp"],
|
|
76
|
+
phrases=["security audit", "find vulnerabilities", "security check", "penetration"],
|
|
77
|
+
weight=1.2,
|
|
78
|
+
),
|
|
79
|
+
DomainPattern(
|
|
80
|
+
domain="testing",
|
|
81
|
+
keywords=["test", "coverage", "unit test", "integration", "pytest", "jest"],
|
|
82
|
+
phrases=["write tests", "generate tests", "test coverage", "increase coverage"],
|
|
83
|
+
weight=1.0,
|
|
84
|
+
),
|
|
85
|
+
DomainPattern(
|
|
86
|
+
domain="documentation",
|
|
87
|
+
keywords=["document", "docstring", "readme", "api docs", "comment"],
|
|
88
|
+
phrases=["write documentation", "generate docs", "add docstrings"],
|
|
89
|
+
weight=0.9,
|
|
90
|
+
),
|
|
91
|
+
DomainPattern(
|
|
92
|
+
domain="performance",
|
|
93
|
+
keywords=["performance", "optimize", "speed", "slow", "memory", "efficient"],
|
|
94
|
+
phrases=["improve performance", "optimize code", "make faster", "reduce memory"],
|
|
95
|
+
weight=1.0,
|
|
96
|
+
),
|
|
97
|
+
DomainPattern(
|
|
98
|
+
domain="refactoring",
|
|
99
|
+
keywords=["refactor", "clean", "restructure", "simplify", "modular"],
|
|
100
|
+
phrases=["refactor code", "clean up", "improve structure"],
|
|
101
|
+
weight=0.9,
|
|
102
|
+
),
|
|
103
|
+
]
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def detect_domain(goal: str) -> tuple[str, float]:
|
|
107
|
+
"""Detect the domain from goal text.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
Tuple of (domain, confidence)
|
|
111
|
+
"""
|
|
112
|
+
goal_lower = goal.lower()
|
|
113
|
+
scores: dict[str, float] = {}
|
|
114
|
+
|
|
115
|
+
for pattern in DOMAIN_PATTERNS:
|
|
116
|
+
score = 0.0
|
|
117
|
+
|
|
118
|
+
# Check keywords
|
|
119
|
+
for keyword in pattern.keywords:
|
|
120
|
+
if keyword in goal_lower:
|
|
121
|
+
score += 1.0 * pattern.weight
|
|
122
|
+
|
|
123
|
+
# Check phrases (higher weight)
|
|
124
|
+
for phrase in pattern.phrases:
|
|
125
|
+
if phrase in goal_lower:
|
|
126
|
+
score += 2.0 * pattern.weight
|
|
127
|
+
|
|
128
|
+
if score > 0:
|
|
129
|
+
scores[pattern.domain] = score
|
|
130
|
+
|
|
131
|
+
if not scores:
|
|
132
|
+
return "general", 0.5
|
|
133
|
+
|
|
134
|
+
best_domain = max(scores, key=lambda k: scores[k])
|
|
135
|
+
max_score = scores[best_domain]
|
|
136
|
+
|
|
137
|
+
# Normalize confidence (cap at 1.0)
|
|
138
|
+
confidence = min(max_score / 5.0, 1.0)
|
|
139
|
+
|
|
140
|
+
return best_domain, confidence
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def extract_keywords(goal: str) -> list[str]:
|
|
144
|
+
"""Extract important keywords from goal."""
|
|
145
|
+
# Remove common words
|
|
146
|
+
stop_words = {
|
|
147
|
+
"i",
|
|
148
|
+
"want",
|
|
149
|
+
"to",
|
|
150
|
+
"the",
|
|
151
|
+
"a",
|
|
152
|
+
"an",
|
|
153
|
+
"my",
|
|
154
|
+
"our",
|
|
155
|
+
"for",
|
|
156
|
+
"with",
|
|
157
|
+
"that",
|
|
158
|
+
"this",
|
|
159
|
+
"is",
|
|
160
|
+
"are",
|
|
161
|
+
"be",
|
|
162
|
+
"will",
|
|
163
|
+
"would",
|
|
164
|
+
"could",
|
|
165
|
+
"should",
|
|
166
|
+
"can",
|
|
167
|
+
"help",
|
|
168
|
+
"me",
|
|
169
|
+
"us",
|
|
170
|
+
"please",
|
|
171
|
+
"need",
|
|
172
|
+
"like",
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
# Extract words
|
|
176
|
+
words = re.findall(r"\b\w+\b", goal.lower())
|
|
177
|
+
keywords = [w for w in words if w not in stop_words and len(w) > 2]
|
|
178
|
+
|
|
179
|
+
# Return unique keywords preserving order
|
|
180
|
+
return list(dict.fromkeys(keywords))
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def identify_ambiguities(goal: str, domain: str) -> list[str]:
|
|
184
|
+
"""Identify ambiguities in the goal that need clarification."""
|
|
185
|
+
ambiguities = []
|
|
186
|
+
|
|
187
|
+
# Check for missing specifics
|
|
188
|
+
if not any(
|
|
189
|
+
lang in goal.lower()
|
|
190
|
+
for lang in ["python", "javascript", "typescript", "java", "go", "rust"]
|
|
191
|
+
):
|
|
192
|
+
ambiguities.append("Programming language not specified")
|
|
193
|
+
|
|
194
|
+
# Check for vague scope
|
|
195
|
+
vague_terms = ["some", "various", "different", "several", "multiple"]
|
|
196
|
+
for term in vague_terms:
|
|
197
|
+
if term in goal.lower():
|
|
198
|
+
ambiguities.append(f"Vague scope indicator: '{term}'")
|
|
199
|
+
break
|
|
200
|
+
|
|
201
|
+
# Domain-specific ambiguities
|
|
202
|
+
if domain == "code_review":
|
|
203
|
+
if "security" not in goal.lower() and "style" not in goal.lower():
|
|
204
|
+
ambiguities.append("Review focus areas not specified")
|
|
205
|
+
|
|
206
|
+
if domain == "testing":
|
|
207
|
+
if "unit" not in goal.lower() and "integration" not in goal.lower():
|
|
208
|
+
ambiguities.append("Test type not specified")
|
|
209
|
+
|
|
210
|
+
return ambiguities
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def identify_assumptions(goal: str, domain: str) -> list[str]:
|
|
214
|
+
"""Identify assumptions we're making from the goal."""
|
|
215
|
+
assumptions = []
|
|
216
|
+
|
|
217
|
+
# Common assumptions
|
|
218
|
+
if domain == "code_review":
|
|
219
|
+
assumptions.append("Assuming code is version-controlled (git)")
|
|
220
|
+
assumptions.append("Assuming PR/diff-based review workflow")
|
|
221
|
+
|
|
222
|
+
if domain == "testing":
|
|
223
|
+
assumptions.append("Assuming existing test framework in project")
|
|
224
|
+
|
|
225
|
+
if domain == "security":
|
|
226
|
+
assumptions.append("Assuming standard web application security model")
|
|
227
|
+
|
|
228
|
+
return assumptions
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
# =============================================================================
|
|
232
|
+
# QUESTION GENERATION
|
|
233
|
+
# =============================================================================
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def generate_initial_questions(
|
|
237
|
+
goal_analysis: GoalAnalysis,
|
|
238
|
+
session: SocraticSession,
|
|
239
|
+
) -> Form:
|
|
240
|
+
"""Generate the first round of questions based on goal analysis."""
|
|
241
|
+
fields: list[FormField] = []
|
|
242
|
+
|
|
243
|
+
# Always ask about languages if not detected
|
|
244
|
+
if "Programming language not specified" in goal_analysis.ambiguities:
|
|
245
|
+
fields.append(create_language_field(required=True))
|
|
246
|
+
|
|
247
|
+
# Ask about quality focus
|
|
248
|
+
fields.append(create_quality_focus_field(required=True))
|
|
249
|
+
|
|
250
|
+
# Domain-specific questions
|
|
251
|
+
if goal_analysis.domain == "code_review":
|
|
252
|
+
fields.append(
|
|
253
|
+
FormField(
|
|
254
|
+
id="review_scope",
|
|
255
|
+
field_type=FieldType.SINGLE_SELECT,
|
|
256
|
+
label="What scope of review do you need?",
|
|
257
|
+
options=[
|
|
258
|
+
FieldOption(
|
|
259
|
+
"pr",
|
|
260
|
+
"Pull Request/Diff",
|
|
261
|
+
description="Review specific changes",
|
|
262
|
+
recommended=True,
|
|
263
|
+
),
|
|
264
|
+
FieldOption("file", "Single File", description="Deep review of one file"),
|
|
265
|
+
FieldOption(
|
|
266
|
+
"directory", "Directory/Module", description="Review entire module"
|
|
267
|
+
),
|
|
268
|
+
FieldOption(
|
|
269
|
+
"project", "Full Project", description="Comprehensive codebase review"
|
|
270
|
+
),
|
|
271
|
+
],
|
|
272
|
+
validation=FieldValidation(required=True),
|
|
273
|
+
category="scope",
|
|
274
|
+
)
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
if goal_analysis.domain == "security":
|
|
278
|
+
fields.append(
|
|
279
|
+
FormField(
|
|
280
|
+
id="security_focus",
|
|
281
|
+
field_type=FieldType.MULTI_SELECT,
|
|
282
|
+
label="What security aspects are most important?",
|
|
283
|
+
options=[
|
|
284
|
+
FieldOption("owasp", "OWASP Top 10", description="Common web vulnerabilities"),
|
|
285
|
+
FieldOption("injection", "Injection Attacks", description="SQL, command, XSS"),
|
|
286
|
+
FieldOption(
|
|
287
|
+
"auth", "Authentication/Authorization", description="Access control issues"
|
|
288
|
+
),
|
|
289
|
+
FieldOption(
|
|
290
|
+
"crypto", "Cryptography", description="Encryption, hashing, secrets"
|
|
291
|
+
),
|
|
292
|
+
FieldOption("deps", "Dependencies", description="Vulnerable dependencies"),
|
|
293
|
+
],
|
|
294
|
+
validation=FieldValidation(required=True),
|
|
295
|
+
category="security",
|
|
296
|
+
)
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
if goal_analysis.domain == "testing":
|
|
300
|
+
fields.append(
|
|
301
|
+
FormField(
|
|
302
|
+
id="test_type",
|
|
303
|
+
field_type=FieldType.MULTI_SELECT,
|
|
304
|
+
label="What types of tests do you need?",
|
|
305
|
+
options=[
|
|
306
|
+
FieldOption(
|
|
307
|
+
"unit",
|
|
308
|
+
"Unit Tests",
|
|
309
|
+
description="Test individual functions",
|
|
310
|
+
recommended=True,
|
|
311
|
+
),
|
|
312
|
+
FieldOption(
|
|
313
|
+
"integration",
|
|
314
|
+
"Integration Tests",
|
|
315
|
+
description="Test component interactions",
|
|
316
|
+
),
|
|
317
|
+
FieldOption("e2e", "End-to-End Tests", description="Test full user flows"),
|
|
318
|
+
FieldOption("edge", "Edge Cases", description="Test boundary conditions"),
|
|
319
|
+
],
|
|
320
|
+
validation=FieldValidation(required=True),
|
|
321
|
+
category="testing",
|
|
322
|
+
)
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
# Automation level (always relevant)
|
|
326
|
+
fields.append(create_automation_level_field())
|
|
327
|
+
|
|
328
|
+
# Team context (helps calibration)
|
|
329
|
+
fields.append(create_team_size_field())
|
|
330
|
+
|
|
331
|
+
# Additional context (optional)
|
|
332
|
+
fields.append(create_additional_context_field())
|
|
333
|
+
|
|
334
|
+
return Form(
|
|
335
|
+
id=f"round_{session.current_round + 1}",
|
|
336
|
+
title="Help Us Understand Your Needs",
|
|
337
|
+
description=f'Based on your goal: "{goal_analysis.raw_goal[:100]}..."',
|
|
338
|
+
fields=fields,
|
|
339
|
+
round_number=session.current_round + 1,
|
|
340
|
+
progress=0.3,
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def generate_followup_questions(
|
|
345
|
+
session: SocraticSession,
|
|
346
|
+
) -> Form | None:
|
|
347
|
+
"""Generate follow-up questions based on previous answers."""
|
|
348
|
+
# Check if we need more clarification
|
|
349
|
+
if session.requirements.completeness_score() >= 0.8:
|
|
350
|
+
return None # Ready to generate
|
|
351
|
+
|
|
352
|
+
if session.current_round >= session.max_rounds:
|
|
353
|
+
return None # Max rounds reached
|
|
354
|
+
|
|
355
|
+
fields: list[FormField] = []
|
|
356
|
+
|
|
357
|
+
# Check what's still missing
|
|
358
|
+
reqs = session.requirements
|
|
359
|
+
|
|
360
|
+
# If no must-haves, ask for priorities
|
|
361
|
+
if not reqs.must_have:
|
|
362
|
+
fields.append(
|
|
363
|
+
FormField(
|
|
364
|
+
id="priorities",
|
|
365
|
+
field_type=FieldType.TEXT_AREA,
|
|
366
|
+
label="What are your top 3 priorities for this workflow?",
|
|
367
|
+
help_text="Be as specific as possible about what success looks like.",
|
|
368
|
+
validation=FieldValidation(required=True, min_length=20),
|
|
369
|
+
category="priorities",
|
|
370
|
+
)
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
# If technical constraints missing
|
|
374
|
+
if not reqs.technical_constraints.get("languages"):
|
|
375
|
+
fields.append(create_language_field(required=True))
|
|
376
|
+
|
|
377
|
+
# Domain-specific follow-ups
|
|
378
|
+
if session.goal_analysis and session.goal_analysis.domain == "code_review":
|
|
379
|
+
if (
|
|
380
|
+
"review_depth" not in [r.get("id") for r in session.question_rounds[-1]["questions"]]
|
|
381
|
+
if session.question_rounds
|
|
382
|
+
else True
|
|
383
|
+
):
|
|
384
|
+
fields.append(
|
|
385
|
+
FormField(
|
|
386
|
+
id="review_depth",
|
|
387
|
+
field_type=FieldType.SINGLE_SELECT,
|
|
388
|
+
label="How thorough should the review be?",
|
|
389
|
+
options=[
|
|
390
|
+
FieldOption(
|
|
391
|
+
"quick", "Quick Scan", description="Fast, surface-level review"
|
|
392
|
+
),
|
|
393
|
+
FieldOption(
|
|
394
|
+
"standard", "Standard", description="Balanced depth", recommended=True
|
|
395
|
+
),
|
|
396
|
+
FieldOption("deep", "Deep Dive", description="Thorough, detailed analysis"),
|
|
397
|
+
],
|
|
398
|
+
category="depth",
|
|
399
|
+
)
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
if not fields:
|
|
403
|
+
return None
|
|
404
|
+
|
|
405
|
+
return Form(
|
|
406
|
+
id=f"round_{session.current_round + 1}",
|
|
407
|
+
title="A Few More Questions",
|
|
408
|
+
description="Help us fine-tune your workflow.",
|
|
409
|
+
fields=fields,
|
|
410
|
+
round_number=session.current_round + 1,
|
|
411
|
+
progress=0.3 + (session.current_round * 0.2),
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
# =============================================================================
|
|
416
|
+
# MAIN ENGINE
|
|
417
|
+
# =============================================================================
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
class SocraticWorkflowBuilder:
|
|
421
|
+
"""Main engine for Socratic agent/workflow generation.
|
|
422
|
+
|
|
423
|
+
Example:
|
|
424
|
+
>>> builder = SocraticWorkflowBuilder()
|
|
425
|
+
>>>
|
|
426
|
+
>>> # Start with a goal
|
|
427
|
+
>>> session = builder.start_session("I want to automate security reviews")
|
|
428
|
+
>>>
|
|
429
|
+
>>> # Get questions
|
|
430
|
+
>>> form = builder.get_next_questions(session)
|
|
431
|
+
>>> print(form.title)
|
|
432
|
+
>>>
|
|
433
|
+
>>> # Submit answers
|
|
434
|
+
>>> session = builder.submit_answers(session, {
|
|
435
|
+
... "languages": ["python"],
|
|
436
|
+
... "quality_focus": ["security"],
|
|
437
|
+
... "automation_level": "semi_auto"
|
|
438
|
+
... })
|
|
439
|
+
>>>
|
|
440
|
+
>>> # Check if ready
|
|
441
|
+
>>> if builder.is_ready_to_generate(session):
|
|
442
|
+
... workflow = builder.generate_workflow(session)
|
|
443
|
+
... print(workflow.describe())
|
|
444
|
+
"""
|
|
445
|
+
|
|
446
|
+
def __init__(self):
|
|
447
|
+
"""Initialize the builder."""
|
|
448
|
+
self.generator = AgentGenerator()
|
|
449
|
+
self._sessions: dict[str, SocraticSession] = {}
|
|
450
|
+
|
|
451
|
+
def start_session(self, goal: str = "") -> SocraticSession:
|
|
452
|
+
"""Start a new Socratic session.
|
|
453
|
+
|
|
454
|
+
Args:
|
|
455
|
+
goal: Optional initial goal (can be set later)
|
|
456
|
+
|
|
457
|
+
Returns:
|
|
458
|
+
New SocraticSession
|
|
459
|
+
"""
|
|
460
|
+
session = SocraticSession()
|
|
461
|
+
|
|
462
|
+
if goal:
|
|
463
|
+
session = self.set_goal(session, goal)
|
|
464
|
+
|
|
465
|
+
self._sessions[session.session_id] = session
|
|
466
|
+
return session
|
|
467
|
+
|
|
468
|
+
def get_session(self, session_id: str) -> SocraticSession | None:
|
|
469
|
+
"""Retrieve a session by ID."""
|
|
470
|
+
return self._sessions.get(session_id)
|
|
471
|
+
|
|
472
|
+
def set_goal(self, session: SocraticSession, goal: str) -> SocraticSession:
|
|
473
|
+
"""Set or update the session goal.
|
|
474
|
+
|
|
475
|
+
Args:
|
|
476
|
+
session: The session to update
|
|
477
|
+
goal: The user's goal statement
|
|
478
|
+
|
|
479
|
+
Returns:
|
|
480
|
+
Updated session with goal analysis
|
|
481
|
+
"""
|
|
482
|
+
session.goal = goal
|
|
483
|
+
session.state = SessionState.ANALYZING_GOAL
|
|
484
|
+
session.touch()
|
|
485
|
+
|
|
486
|
+
# Analyze the goal
|
|
487
|
+
domain, confidence = detect_domain(goal)
|
|
488
|
+
keywords = extract_keywords(goal)
|
|
489
|
+
ambiguities = identify_ambiguities(goal, domain)
|
|
490
|
+
assumptions = identify_assumptions(goal, domain)
|
|
491
|
+
|
|
492
|
+
session.goal_analysis = GoalAnalysis(
|
|
493
|
+
raw_goal=goal,
|
|
494
|
+
intent=self._extract_intent(goal, domain),
|
|
495
|
+
domain=domain,
|
|
496
|
+
confidence=confidence,
|
|
497
|
+
ambiguities=ambiguities,
|
|
498
|
+
assumptions=assumptions,
|
|
499
|
+
keywords=keywords,
|
|
500
|
+
)
|
|
501
|
+
|
|
502
|
+
# Transition to awaiting answers
|
|
503
|
+
session.state = SessionState.AWAITING_ANSWERS
|
|
504
|
+
return session
|
|
505
|
+
|
|
506
|
+
def _extract_intent(self, goal: str, domain: str) -> str:
|
|
507
|
+
"""Extract the core intent from the goal."""
|
|
508
|
+
intent_patterns = {
|
|
509
|
+
"code_review": "Automated code review",
|
|
510
|
+
"security": "Security vulnerability analysis",
|
|
511
|
+
"testing": "Automated test generation",
|
|
512
|
+
"documentation": "Documentation generation",
|
|
513
|
+
"performance": "Performance optimization",
|
|
514
|
+
"refactoring": "Code refactoring",
|
|
515
|
+
"general": "Code analysis and improvement",
|
|
516
|
+
}
|
|
517
|
+
return intent_patterns.get(domain, "Code analysis")
|
|
518
|
+
|
|
519
|
+
def get_initial_form(self) -> Form:
|
|
520
|
+
"""Get the initial goal capture form."""
|
|
521
|
+
return Form(
|
|
522
|
+
id="initial_goal",
|
|
523
|
+
title="What would you like to accomplish?",
|
|
524
|
+
description=(
|
|
525
|
+
"Describe your goal in your own words. Be as specific as you like - "
|
|
526
|
+
"we'll ask clarifying questions to understand exactly what you need."
|
|
527
|
+
),
|
|
528
|
+
fields=[create_goal_text_field()],
|
|
529
|
+
round_number=0,
|
|
530
|
+
progress=0.1,
|
|
531
|
+
)
|
|
532
|
+
|
|
533
|
+
def get_next_questions(self, session: SocraticSession) -> Form | None:
|
|
534
|
+
"""Get the next set of questions for a session.
|
|
535
|
+
|
|
536
|
+
Args:
|
|
537
|
+
session: The current session
|
|
538
|
+
|
|
539
|
+
Returns:
|
|
540
|
+
Form with questions, or None if ready to generate
|
|
541
|
+
"""
|
|
542
|
+
if session.state == SessionState.AWAITING_GOAL:
|
|
543
|
+
return self.get_initial_form()
|
|
544
|
+
|
|
545
|
+
if session.state == SessionState.READY_TO_GENERATE:
|
|
546
|
+
return None
|
|
547
|
+
|
|
548
|
+
if session.state == SessionState.COMPLETED:
|
|
549
|
+
return None
|
|
550
|
+
|
|
551
|
+
if session.goal_analysis is None:
|
|
552
|
+
return self.get_initial_form()
|
|
553
|
+
|
|
554
|
+
# First round of questions
|
|
555
|
+
if session.current_round == 0:
|
|
556
|
+
return generate_initial_questions(session.goal_analysis, session)
|
|
557
|
+
|
|
558
|
+
# Follow-up questions
|
|
559
|
+
return generate_followup_questions(session)
|
|
560
|
+
|
|
561
|
+
def submit_answers(
|
|
562
|
+
self,
|
|
563
|
+
session: SocraticSession,
|
|
564
|
+
answers: dict[str, Any],
|
|
565
|
+
) -> SocraticSession:
|
|
566
|
+
"""Submit answers to the current questions.
|
|
567
|
+
|
|
568
|
+
Args:
|
|
569
|
+
session: The current session
|
|
570
|
+
answers: Dictionary mapping field IDs to values
|
|
571
|
+
|
|
572
|
+
Returns:
|
|
573
|
+
Updated session
|
|
574
|
+
"""
|
|
575
|
+
session.state = SessionState.PROCESSING_ANSWERS
|
|
576
|
+
session.touch()
|
|
577
|
+
|
|
578
|
+
# Record this round
|
|
579
|
+
current_form = self.get_next_questions(session)
|
|
580
|
+
questions_data = []
|
|
581
|
+
if current_form:
|
|
582
|
+
questions_data = [{"id": f.id, "label": f.label} for f in current_form.fields]
|
|
583
|
+
|
|
584
|
+
session.add_question_round(questions_data, answers)
|
|
585
|
+
|
|
586
|
+
# Update requirements from answers
|
|
587
|
+
self._update_requirements(session, answers)
|
|
588
|
+
|
|
589
|
+
# Check if we can generate
|
|
590
|
+
if session.can_generate():
|
|
591
|
+
session.state = SessionState.READY_TO_GENERATE
|
|
592
|
+
else:
|
|
593
|
+
session.state = SessionState.AWAITING_ANSWERS
|
|
594
|
+
|
|
595
|
+
return session
|
|
596
|
+
|
|
597
|
+
def _update_requirements(
|
|
598
|
+
self,
|
|
599
|
+
session: SocraticSession,
|
|
600
|
+
answers: dict[str, Any],
|
|
601
|
+
) -> None:
|
|
602
|
+
"""Update session requirements from answers."""
|
|
603
|
+
reqs = session.requirements
|
|
604
|
+
|
|
605
|
+
# Languages
|
|
606
|
+
if "languages" in answers:
|
|
607
|
+
reqs.technical_constraints["languages"] = answers["languages"]
|
|
608
|
+
|
|
609
|
+
# Quality focus
|
|
610
|
+
if "quality_focus" in answers:
|
|
611
|
+
reqs.quality_attributes = dict.fromkeys(answers["quality_focus"], 1.0)
|
|
612
|
+
# Add to must-haves
|
|
613
|
+
for quality in answers["quality_focus"]:
|
|
614
|
+
req = f"Optimize for {quality}"
|
|
615
|
+
if req not in reqs.must_have:
|
|
616
|
+
reqs.must_have.append(req)
|
|
617
|
+
|
|
618
|
+
# Automation level
|
|
619
|
+
if "automation_level" in answers:
|
|
620
|
+
reqs.preferences["automation_level"] = answers["automation_level"]
|
|
621
|
+
|
|
622
|
+
# Team size
|
|
623
|
+
if "team_size" in answers:
|
|
624
|
+
reqs.preferences["team_size"] = answers["team_size"]
|
|
625
|
+
|
|
626
|
+
# Domain-specific
|
|
627
|
+
if "review_scope" in answers:
|
|
628
|
+
reqs.domain_specific["review_scope"] = answers["review_scope"]
|
|
629
|
+
|
|
630
|
+
if "security_focus" in answers:
|
|
631
|
+
reqs.domain_specific["security_focus"] = answers["security_focus"]
|
|
632
|
+
|
|
633
|
+
if "test_type" in answers:
|
|
634
|
+
reqs.domain_specific["test_type"] = answers["test_type"]
|
|
635
|
+
|
|
636
|
+
# Additional context
|
|
637
|
+
if "additional_context" in answers and answers["additional_context"]:
|
|
638
|
+
reqs.domain_specific["additional_context"] = answers["additional_context"]
|
|
639
|
+
|
|
640
|
+
# Priorities
|
|
641
|
+
if "priorities" in answers:
|
|
642
|
+
priorities = answers["priorities"]
|
|
643
|
+
if isinstance(priorities, str):
|
|
644
|
+
# Parse priorities from text
|
|
645
|
+
for line in priorities.split("\n"):
|
|
646
|
+
line = line.strip()
|
|
647
|
+
if line and line not in reqs.must_have:
|
|
648
|
+
reqs.must_have.append(line)
|
|
649
|
+
|
|
650
|
+
def is_ready_to_generate(self, session: SocraticSession) -> bool:
|
|
651
|
+
"""Check if session is ready for workflow generation."""
|
|
652
|
+
return session.state == SessionState.READY_TO_GENERATE or session.can_generate()
|
|
653
|
+
|
|
654
|
+
def generate_workflow(
|
|
655
|
+
self,
|
|
656
|
+
session: SocraticSession,
|
|
657
|
+
) -> GeneratedWorkflow:
|
|
658
|
+
"""Generate workflow from the session.
|
|
659
|
+
|
|
660
|
+
Args:
|
|
661
|
+
session: Session with complete requirements
|
|
662
|
+
|
|
663
|
+
Returns:
|
|
664
|
+
Generated workflow ready for execution
|
|
665
|
+
|
|
666
|
+
Raises:
|
|
667
|
+
ValueError: If session not ready for generation
|
|
668
|
+
"""
|
|
669
|
+
if not self.is_ready_to_generate(session):
|
|
670
|
+
raise ValueError("Session not ready for generation. Answer more questions.")
|
|
671
|
+
|
|
672
|
+
session.state = SessionState.GENERATING
|
|
673
|
+
session.touch()
|
|
674
|
+
|
|
675
|
+
# Build requirements dict for generator
|
|
676
|
+
reqs = session.requirements
|
|
677
|
+
requirements = {
|
|
678
|
+
"quality_focus": list(reqs.quality_attributes.keys()),
|
|
679
|
+
"languages": reqs.technical_constraints.get("languages", []),
|
|
680
|
+
"automation_level": reqs.preferences.get("automation_level", "semi_auto"),
|
|
681
|
+
"domain": session.goal_analysis.domain if session.goal_analysis else "general",
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
# Generate agents
|
|
685
|
+
agents = self.generator.generate_agents_for_requirements(requirements)
|
|
686
|
+
|
|
687
|
+
# Determine workflow name
|
|
688
|
+
domain = session.goal_analysis.domain if session.goal_analysis else "general"
|
|
689
|
+
name = self._generate_workflow_name(domain, requirements)
|
|
690
|
+
|
|
691
|
+
# Generate success criteria
|
|
692
|
+
success_criteria = self._generate_success_criteria(domain, requirements)
|
|
693
|
+
|
|
694
|
+
# Create blueprint
|
|
695
|
+
blueprint = self.generator.create_workflow_blueprint(
|
|
696
|
+
name=name,
|
|
697
|
+
description=session.goal or "Generated workflow",
|
|
698
|
+
agents=agents,
|
|
699
|
+
quality_focus=requirements["quality_focus"],
|
|
700
|
+
automation_level=requirements["automation_level"],
|
|
701
|
+
success_criteria=success_criteria,
|
|
702
|
+
)
|
|
703
|
+
|
|
704
|
+
blueprint.source_session_id = session.session_id
|
|
705
|
+
blueprint.supported_languages = requirements["languages"]
|
|
706
|
+
|
|
707
|
+
# Generate workflow
|
|
708
|
+
workflow = self.generator.generate_workflow(blueprint)
|
|
709
|
+
|
|
710
|
+
# Update session
|
|
711
|
+
session.blueprint = blueprint
|
|
712
|
+
session.workflow = workflow
|
|
713
|
+
session.state = SessionState.COMPLETED
|
|
714
|
+
session.touch()
|
|
715
|
+
|
|
716
|
+
return workflow
|
|
717
|
+
|
|
718
|
+
def _generate_workflow_name(
|
|
719
|
+
self,
|
|
720
|
+
domain: str,
|
|
721
|
+
requirements: dict[str, Any],
|
|
722
|
+
) -> str:
|
|
723
|
+
"""Generate a descriptive workflow name."""
|
|
724
|
+
domain_names = {
|
|
725
|
+
"code_review": "Code Review",
|
|
726
|
+
"security": "Security Audit",
|
|
727
|
+
"testing": "Test Generation",
|
|
728
|
+
"documentation": "Documentation",
|
|
729
|
+
"performance": "Performance Analysis",
|
|
730
|
+
"refactoring": "Refactoring",
|
|
731
|
+
"general": "Code Analysis",
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
base_name = domain_names.get(domain, "Custom Workflow")
|
|
735
|
+
|
|
736
|
+
# Add qualifier
|
|
737
|
+
qualities = requirements.get("quality_focus", [])
|
|
738
|
+
if qualities:
|
|
739
|
+
qualifier = qualities[0].title()
|
|
740
|
+
return f"{qualifier}-Focused {base_name}"
|
|
741
|
+
|
|
742
|
+
return f"Automated {base_name}"
|
|
743
|
+
|
|
744
|
+
def _generate_success_criteria(
|
|
745
|
+
self,
|
|
746
|
+
domain: str,
|
|
747
|
+
requirements: dict[str, Any],
|
|
748
|
+
) -> SuccessCriteria:
|
|
749
|
+
"""Generate appropriate success criteria for the workflow."""
|
|
750
|
+
# Use predefined criteria based on domain
|
|
751
|
+
if domain == "code_review":
|
|
752
|
+
return code_review_criteria()
|
|
753
|
+
elif domain == "security":
|
|
754
|
+
return security_audit_criteria()
|
|
755
|
+
elif domain == "testing":
|
|
756
|
+
return test_generation_criteria()
|
|
757
|
+
else:
|
|
758
|
+
# Generic criteria
|
|
759
|
+
return SuccessCriteria(
|
|
760
|
+
id=f"{domain}_success",
|
|
761
|
+
name=f"{domain.title()} Success Criteria",
|
|
762
|
+
metrics=[
|
|
763
|
+
SuccessMetric(
|
|
764
|
+
id="task_completed",
|
|
765
|
+
name="Task Completed",
|
|
766
|
+
metric_type=MetricType.BOOLEAN,
|
|
767
|
+
is_primary=True,
|
|
768
|
+
extraction_path="success",
|
|
769
|
+
),
|
|
770
|
+
SuccessMetric(
|
|
771
|
+
id="findings_count",
|
|
772
|
+
name="Findings",
|
|
773
|
+
metric_type=MetricType.COUNT,
|
|
774
|
+
extraction_path="findings_count",
|
|
775
|
+
),
|
|
776
|
+
],
|
|
777
|
+
success_threshold=0.7,
|
|
778
|
+
)
|
|
779
|
+
|
|
780
|
+
def get_session_summary(self, session: SocraticSession) -> dict[str, Any]:
|
|
781
|
+
"""Get a summary of the session state for display."""
|
|
782
|
+
return {
|
|
783
|
+
"session_id": session.session_id,
|
|
784
|
+
"state": session.state.value,
|
|
785
|
+
"goal": session.goal,
|
|
786
|
+
"domain": session.goal_analysis.domain if session.goal_analysis else None,
|
|
787
|
+
"confidence": session.goal_analysis.confidence if session.goal_analysis else 0,
|
|
788
|
+
"rounds_completed": session.current_round,
|
|
789
|
+
"requirements_completeness": session.requirements.completeness_score(),
|
|
790
|
+
"ready_to_generate": self.is_ready_to_generate(session),
|
|
791
|
+
"ambiguities_remaining": (
|
|
792
|
+
len(session.goal_analysis.ambiguities) if session.goal_analysis else 0
|
|
793
|
+
),
|
|
794
|
+
}
|