attune-ai 2.1.4__py3-none-any.whl → 2.2.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.
Files changed (123) hide show
  1. attune/cli/__init__.py +3 -55
  2. attune/cli/commands/batch.py +4 -12
  3. attune/cli/commands/cache.py +7 -15
  4. attune/cli/commands/provider.py +17 -0
  5. attune/cli/commands/routing.py +3 -1
  6. attune/cli/commands/setup.py +122 -0
  7. attune/cli/commands/tier.py +1 -3
  8. attune/cli/commands/workflow.py +31 -0
  9. attune/cli/parsers/cache.py +1 -0
  10. attune/cli/parsers/help.py +1 -3
  11. attune/cli/parsers/provider.py +7 -0
  12. attune/cli/parsers/routing.py +1 -3
  13. attune/cli/parsers/setup.py +7 -0
  14. attune/cli/parsers/status.py +1 -3
  15. attune/cli/parsers/tier.py +1 -3
  16. attune/cli_minimal.py +34 -28
  17. attune/cli_router.py +9 -7
  18. attune/cli_unified.py +3 -0
  19. attune/core.py +190 -0
  20. attune/dashboard/app.py +4 -2
  21. attune/dashboard/simple_server.py +3 -1
  22. attune/dashboard/standalone_server.py +7 -3
  23. attune/mcp/server.py +54 -102
  24. attune/memory/long_term.py +0 -2
  25. attune/memory/short_term/__init__.py +84 -0
  26. attune/memory/short_term/base.py +467 -0
  27. attune/memory/short_term/batch.py +219 -0
  28. attune/memory/short_term/caching.py +227 -0
  29. attune/memory/short_term/conflicts.py +265 -0
  30. attune/memory/short_term/cross_session.py +122 -0
  31. attune/memory/short_term/facade.py +655 -0
  32. attune/memory/short_term/pagination.py +215 -0
  33. attune/memory/short_term/patterns.py +271 -0
  34. attune/memory/short_term/pubsub.py +286 -0
  35. attune/memory/short_term/queues.py +244 -0
  36. attune/memory/short_term/security.py +300 -0
  37. attune/memory/short_term/sessions.py +250 -0
  38. attune/memory/short_term/streams.py +249 -0
  39. attune/memory/short_term/timelines.py +234 -0
  40. attune/memory/short_term/transactions.py +186 -0
  41. attune/memory/short_term/working.py +252 -0
  42. attune/meta_workflows/cli_commands/__init__.py +3 -0
  43. attune/meta_workflows/cli_commands/agent_commands.py +0 -4
  44. attune/meta_workflows/cli_commands/analytics_commands.py +0 -6
  45. attune/meta_workflows/cli_commands/config_commands.py +0 -5
  46. attune/meta_workflows/cli_commands/memory_commands.py +0 -5
  47. attune/meta_workflows/cli_commands/template_commands.py +0 -5
  48. attune/meta_workflows/cli_commands/workflow_commands.py +0 -6
  49. attune/meta_workflows/workflow.py +1 -1
  50. attune/models/adaptive_routing.py +4 -8
  51. attune/models/auth_cli.py +3 -9
  52. attune/models/auth_strategy.py +2 -4
  53. attune/models/provider_config.py +20 -1
  54. attune/models/telemetry/analytics.py +0 -2
  55. attune/models/telemetry/backend.py +0 -3
  56. attune/models/telemetry/storage.py +0 -2
  57. attune/orchestration/_strategies/__init__.py +156 -0
  58. attune/orchestration/_strategies/base.py +231 -0
  59. attune/orchestration/_strategies/conditional_strategies.py +373 -0
  60. attune/orchestration/_strategies/conditions.py +369 -0
  61. attune/orchestration/_strategies/core_strategies.py +491 -0
  62. attune/orchestration/_strategies/data_classes.py +64 -0
  63. attune/orchestration/_strategies/nesting.py +233 -0
  64. attune/orchestration/execution_strategies.py +58 -1567
  65. attune/orchestration/meta_orchestrator.py +1 -3
  66. attune/project_index/scanner.py +1 -3
  67. attune/project_index/scanner_parallel.py +7 -5
  68. attune/socratic_router.py +1 -3
  69. attune/telemetry/agent_coordination.py +9 -3
  70. attune/telemetry/agent_tracking.py +16 -3
  71. attune/telemetry/approval_gates.py +22 -5
  72. attune/telemetry/cli.py +3 -3
  73. attune/telemetry/commands/dashboard_commands.py +24 -8
  74. attune/telemetry/event_streaming.py +8 -2
  75. attune/telemetry/feedback_loop.py +10 -2
  76. attune/tools.py +1 -0
  77. attune/workflow_commands.py +1 -3
  78. attune/workflows/__init__.py +53 -10
  79. attune/workflows/autonomous_test_gen.py +160 -104
  80. attune/workflows/base.py +48 -664
  81. attune/workflows/batch_processing.py +2 -4
  82. attune/workflows/compat.py +156 -0
  83. attune/workflows/cost_mixin.py +141 -0
  84. attune/workflows/data_classes.py +92 -0
  85. attune/workflows/document_gen/workflow.py +11 -14
  86. attune/workflows/history.py +62 -37
  87. attune/workflows/llm_base.py +2 -4
  88. attune/workflows/migration.py +422 -0
  89. attune/workflows/output.py +3 -9
  90. attune/workflows/parsing_mixin.py +427 -0
  91. attune/workflows/perf_audit.py +3 -1
  92. attune/workflows/progress.py +10 -13
  93. attune/workflows/release_prep.py +5 -1
  94. attune/workflows/routing.py +0 -2
  95. attune/workflows/secure_release.py +2 -1
  96. attune/workflows/security_audit.py +19 -14
  97. attune/workflows/security_audit_phase3.py +28 -22
  98. attune/workflows/seo_optimization.py +29 -29
  99. attune/workflows/test_gen/test_templates.py +1 -4
  100. attune/workflows/test_gen/workflow.py +0 -2
  101. attune/workflows/test_gen_behavioral.py +7 -20
  102. attune/workflows/test_gen_parallel.py +6 -4
  103. {attune_ai-2.1.4.dist-info → attune_ai-2.2.0.dist-info}/METADATA +4 -3
  104. {attune_ai-2.1.4.dist-info → attune_ai-2.2.0.dist-info}/RECORD +119 -94
  105. {attune_ai-2.1.4.dist-info → attune_ai-2.2.0.dist-info}/entry_points.txt +0 -2
  106. attune_healthcare/monitors/monitoring/__init__.py +9 -9
  107. attune_llm/agent_factory/__init__.py +6 -6
  108. attune_llm/commands/__init__.py +10 -10
  109. attune_llm/commands/models.py +3 -3
  110. attune_llm/config/__init__.py +8 -8
  111. attune_llm/learning/__init__.py +3 -3
  112. attune_llm/learning/extractor.py +5 -3
  113. attune_llm/learning/storage.py +5 -3
  114. attune_llm/security/__init__.py +17 -17
  115. attune_llm/utils/tokens.py +3 -1
  116. attune/cli_legacy.py +0 -3957
  117. attune/memory/short_term.py +0 -2192
  118. attune/workflows/manage_docs.py +0 -87
  119. attune/workflows/test5.py +0 -125
  120. {attune_ai-2.1.4.dist-info → attune_ai-2.2.0.dist-info}/WHEEL +0 -0
  121. {attune_ai-2.1.4.dist-info → attune_ai-2.2.0.dist-info}/licenses/LICENSE +0 -0
  122. {attune_ai-2.1.4.dist-info → attune_ai-2.2.0.dist-info}/licenses/LICENSE_CHANGE_ANNOUNCEMENT.md +0 -0
  123. {attune_ai-2.1.4.dist-info → attune_ai-2.2.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,233 @@
1
+ """Workflow nesting support for recursive composition.
2
+
3
+ This module enables "sentences within sentences" - workflows that invoke
4
+ other workflows. Supports both registered workflow IDs and inline definitions.
5
+
6
+ Key concepts:
7
+ - WorkflowReference: Points to a nested workflow (by ID or inline)
8
+ - InlineWorkflow: Defines a sub-workflow directly within parent
9
+ - NestingContext: Tracks depth and prevents infinite recursion
10
+ - WorkflowDefinition: A registered workflow that can be referenced by ID
11
+
12
+ Security:
13
+ - Cycle detection prevents infinite loops
14
+ - Max depth limit prevents stack overflow
15
+ - No eval() or exec() usage
16
+
17
+ Copyright 2025 Smart-AI-Memory
18
+ Licensed under Fair Source License 0.9
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import logging
24
+ from dataclasses import dataclass, field
25
+ from typing import TYPE_CHECKING, Any
26
+
27
+ if TYPE_CHECKING:
28
+ from ..agent_templates import AgentTemplate
29
+
30
+ logger = logging.getLogger(__name__)
31
+
32
+
33
+ # =============================================================================
34
+ # Nested Sentence Types (Phase 2 - Recursive Composition)
35
+ # =============================================================================
36
+
37
+
38
+ @dataclass
39
+ class WorkflowReference:
40
+ """Reference to a workflow for nested composition.
41
+
42
+ Enables "sentences within sentences" - workflows that invoke other workflows.
43
+ Supports both registered workflow IDs and inline definitions.
44
+
45
+ Attributes:
46
+ workflow_id: ID of registered workflow (mutually exclusive with inline)
47
+ inline: Inline workflow definition (mutually exclusive with workflow_id)
48
+ context_mapping: Optional mapping of parent context fields to child
49
+ result_key: Key to store nested workflow result in parent context
50
+
51
+ Example (by ID):
52
+ >>> ref = WorkflowReference(
53
+ ... workflow_id="security-audit-team",
54
+ ... result_key="security_result"
55
+ ... )
56
+
57
+ Example (inline):
58
+ >>> ref = WorkflowReference(
59
+ ... inline=InlineWorkflow(
60
+ ... agents=[agent1, agent2],
61
+ ... strategy="parallel"
62
+ ... ),
63
+ ... result_key="analysis_result"
64
+ ... )
65
+ """
66
+
67
+ workflow_id: str = ""
68
+ inline: InlineWorkflow | None = None
69
+ context_mapping: dict[str, str] = field(default_factory=dict)
70
+ result_key: str = "nested_result"
71
+
72
+ def __post_init__(self):
73
+ """Validate that exactly one reference type is provided."""
74
+ if bool(self.workflow_id) == bool(self.inline):
75
+ raise ValueError("WorkflowReference must have exactly one of: workflow_id or inline")
76
+
77
+
78
+ @dataclass
79
+ class InlineWorkflow:
80
+ """Inline workflow definition for nested composition.
81
+
82
+ Allows defining a sub-workflow directly within a parent workflow,
83
+ without requiring registration.
84
+
85
+ Attributes:
86
+ agents: Agents to execute
87
+ strategy: Strategy name (from STRATEGY_REGISTRY)
88
+ description: Human-readable description
89
+
90
+ Example:
91
+ >>> inline = InlineWorkflow(
92
+ ... agents=[analyzer, reviewer],
93
+ ... strategy="sequential",
94
+ ... description="Code review sub-workflow"
95
+ ... )
96
+ """
97
+
98
+ agents: list[AgentTemplate]
99
+ strategy: str = "sequential"
100
+ description: str = ""
101
+
102
+
103
+ class NestingContext:
104
+ """Tracks nesting depth and prevents infinite recursion.
105
+
106
+ Attributes:
107
+ current_depth: Current nesting level (0 = root)
108
+ max_depth: Maximum allowed nesting depth
109
+ workflow_stack: Stack of workflow IDs for cycle detection
110
+ """
111
+
112
+ CONTEXT_KEY = "_nesting"
113
+ DEFAULT_MAX_DEPTH = 3
114
+
115
+ def __init__(self, max_depth: int = DEFAULT_MAX_DEPTH):
116
+ """Initialize nesting context.
117
+
118
+ Args:
119
+ max_depth: Maximum allowed nesting depth
120
+ """
121
+ self.current_depth = 0
122
+ self.max_depth = max_depth
123
+ self.workflow_stack: list[str] = []
124
+
125
+ @classmethod
126
+ def from_context(cls, context: dict[str, Any]) -> NestingContext:
127
+ """Extract or create NestingContext from execution context.
128
+
129
+ Args:
130
+ context: Execution context dict
131
+
132
+ Returns:
133
+ NestingContext instance
134
+ """
135
+ if cls.CONTEXT_KEY in context:
136
+ return context[cls.CONTEXT_KEY]
137
+ return cls()
138
+
139
+ def can_nest(self, workflow_id: str = "") -> bool:
140
+ """Check if another nesting level is allowed.
141
+
142
+ Args:
143
+ workflow_id: ID of workflow to nest (for cycle detection)
144
+
145
+ Returns:
146
+ True if nesting is allowed
147
+ """
148
+ if self.current_depth >= self.max_depth:
149
+ return False
150
+ if workflow_id and workflow_id in self.workflow_stack:
151
+ return False # Cycle detected
152
+ return True
153
+
154
+ def enter(self, workflow_id: str = "") -> NestingContext:
155
+ """Create a child context for nested execution.
156
+
157
+ Args:
158
+ workflow_id: ID of workflow being entered
159
+
160
+ Returns:
161
+ New NestingContext with incremented depth
162
+ """
163
+ child = NestingContext(self.max_depth)
164
+ child.current_depth = self.current_depth + 1
165
+ child.workflow_stack = self.workflow_stack.copy()
166
+ if workflow_id:
167
+ child.workflow_stack.append(workflow_id)
168
+ return child
169
+
170
+ def to_context(self, context: dict[str, Any]) -> dict[str, Any]:
171
+ """Add nesting context to execution context.
172
+
173
+ Args:
174
+ context: Execution context dict
175
+
176
+ Returns:
177
+ Updated context with nesting info
178
+ """
179
+ context = context.copy()
180
+ context[self.CONTEXT_KEY] = self
181
+ return context
182
+
183
+
184
+ # Registry for named workflows (populated at runtime)
185
+ WORKFLOW_REGISTRY: dict[str, WorkflowDefinition] = {}
186
+
187
+
188
+ @dataclass
189
+ class WorkflowDefinition:
190
+ """A registered workflow definition.
191
+
192
+ Workflows can be registered and referenced by ID in nested compositions.
193
+
194
+ Attributes:
195
+ id: Unique workflow identifier
196
+ agents: Agents in the workflow
197
+ strategy: Composition strategy name
198
+ description: Human-readable description
199
+ """
200
+
201
+ id: str
202
+ agents: list[AgentTemplate]
203
+ strategy: str = "sequential"
204
+ description: str = ""
205
+
206
+
207
+ def register_workflow(workflow: WorkflowDefinition) -> None:
208
+ """Register a workflow for nested references.
209
+
210
+ Args:
211
+ workflow: Workflow definition to register
212
+ """
213
+ WORKFLOW_REGISTRY[workflow.id] = workflow
214
+ logger.info(f"Registered workflow: {workflow.id}")
215
+
216
+
217
+ def get_workflow(workflow_id: str) -> WorkflowDefinition:
218
+ """Get a registered workflow by ID.
219
+
220
+ Args:
221
+ workflow_id: Workflow identifier
222
+
223
+ Returns:
224
+ WorkflowDefinition
225
+
226
+ Raises:
227
+ ValueError: If workflow is not registered
228
+ """
229
+ if workflow_id not in WORKFLOW_REGISTRY:
230
+ raise ValueError(
231
+ f"Unknown workflow: {workflow_id}. Available: {list(WORKFLOW_REGISTRY.keys())}"
232
+ )
233
+ return WORKFLOW_REGISTRY[workflow_id]