attune-ai 2.1.5__py3-none-any.whl → 2.2.1__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/cli/__init__.py +3 -59
- attune/cli/commands/batch.py +4 -12
- attune/cli/commands/cache.py +8 -16
- attune/cli/commands/provider.py +17 -0
- attune/cli/commands/routing.py +3 -1
- attune/cli/commands/setup.py +122 -0
- attune/cli/commands/tier.py +1 -3
- attune/cli/commands/workflow.py +31 -0
- attune/cli/parsers/cache.py +1 -0
- attune/cli/parsers/help.py +1 -3
- attune/cli/parsers/provider.py +7 -0
- attune/cli/parsers/routing.py +1 -3
- attune/cli/parsers/setup.py +7 -0
- attune/cli/parsers/status.py +1 -3
- attune/cli/parsers/tier.py +1 -3
- attune/cli_minimal.py +9 -3
- attune/cli_router.py +9 -7
- attune/cli_unified.py +3 -0
- attune/dashboard/app.py +3 -1
- attune/dashboard/simple_server.py +3 -1
- attune/dashboard/standalone_server.py +7 -3
- attune/mcp/server.py +54 -102
- attune/memory/long_term.py +0 -2
- attune/memory/short_term/__init__.py +84 -0
- attune/memory/short_term/base.py +465 -0
- attune/memory/short_term/batch.py +219 -0
- attune/memory/short_term/caching.py +227 -0
- attune/memory/short_term/conflicts.py +265 -0
- attune/memory/short_term/cross_session.py +122 -0
- attune/memory/short_term/facade.py +653 -0
- attune/memory/short_term/pagination.py +207 -0
- attune/memory/short_term/patterns.py +271 -0
- attune/memory/short_term/pubsub.py +286 -0
- attune/memory/short_term/queues.py +244 -0
- attune/memory/short_term/security.py +300 -0
- attune/memory/short_term/sessions.py +250 -0
- attune/memory/short_term/streams.py +242 -0
- attune/memory/short_term/timelines.py +234 -0
- attune/memory/short_term/transactions.py +184 -0
- attune/memory/short_term/working.py +252 -0
- attune/meta_workflows/cli_commands/__init__.py +3 -0
- attune/meta_workflows/cli_commands/agent_commands.py +0 -4
- attune/meta_workflows/cli_commands/analytics_commands.py +0 -6
- attune/meta_workflows/cli_commands/config_commands.py +0 -5
- attune/meta_workflows/cli_commands/memory_commands.py +0 -5
- attune/meta_workflows/cli_commands/template_commands.py +0 -5
- attune/meta_workflows/cli_commands/workflow_commands.py +0 -6
- attune/meta_workflows/plan_generator.py +2 -4
- attune/models/adaptive_routing.py +4 -8
- attune/models/auth_cli.py +3 -9
- attune/models/auth_strategy.py +2 -4
- attune/models/telemetry/analytics.py +0 -2
- attune/models/telemetry/backend.py +0 -3
- attune/models/telemetry/storage.py +0 -2
- attune/monitoring/alerts.py +6 -10
- attune/orchestration/_strategies/__init__.py +156 -0
- attune/orchestration/_strategies/base.py +227 -0
- attune/orchestration/_strategies/conditional_strategies.py +365 -0
- attune/orchestration/_strategies/conditions.py +369 -0
- attune/orchestration/_strategies/core_strategies.py +479 -0
- attune/orchestration/_strategies/data_classes.py +64 -0
- attune/orchestration/_strategies/nesting.py +233 -0
- attune/orchestration/execution_strategies.py +58 -1567
- attune/orchestration/meta_orchestrator.py +1 -3
- attune/project_index/scanner.py +1 -3
- attune/project_index/scanner_parallel.py +7 -5
- attune/socratic/storage.py +2 -4
- attune/socratic_router.py +1 -3
- attune/telemetry/agent_coordination.py +9 -3
- attune/telemetry/agent_tracking.py +16 -3
- attune/telemetry/approval_gates.py +22 -5
- attune/telemetry/cli.py +1 -3
- attune/telemetry/commands/dashboard_commands.py +24 -8
- attune/telemetry/event_streaming.py +8 -2
- attune/telemetry/feedback_loop.py +10 -2
- attune/tools.py +2 -1
- attune/workflow_commands.py +1 -3
- attune/workflow_patterns/structural.py +4 -8
- attune/workflows/__init__.py +54 -10
- attune/workflows/autonomous_test_gen.py +158 -102
- attune/workflows/base.py +48 -672
- attune/workflows/batch_processing.py +1 -3
- attune/workflows/compat.py +156 -0
- attune/workflows/cost_mixin.py +141 -0
- attune/workflows/data_classes.py +92 -0
- attune/workflows/document_gen/workflow.py +11 -14
- attune/workflows/history.py +16 -9
- attune/workflows/llm_base.py +1 -3
- attune/workflows/migration.py +432 -0
- attune/workflows/output.py +2 -7
- attune/workflows/parsing_mixin.py +427 -0
- attune/workflows/perf_audit.py +3 -1
- attune/workflows/progress.py +9 -11
- attune/workflows/release_prep.py +5 -1
- attune/workflows/routing.py +0 -2
- attune/workflows/secure_release.py +4 -1
- attune/workflows/security_audit.py +20 -14
- attune/workflows/security_audit_phase3.py +28 -22
- attune/workflows/seo_optimization.py +27 -27
- attune/workflows/test_gen/test_templates.py +1 -4
- attune/workflows/test_gen/workflow.py +0 -2
- attune/workflows/test_gen_behavioral.py +6 -19
- attune/workflows/test_gen_parallel.py +8 -6
- {attune_ai-2.1.5.dist-info → attune_ai-2.2.1.dist-info}/METADATA +4 -3
- {attune_ai-2.1.5.dist-info → attune_ai-2.2.1.dist-info}/RECORD +121 -96
- {attune_ai-2.1.5.dist-info → attune_ai-2.2.1.dist-info}/entry_points.txt +0 -2
- attune_healthcare/monitors/monitoring/__init__.py +9 -9
- attune_llm/agent_factory/__init__.py +6 -6
- attune_llm/agent_factory/adapters/haystack_adapter.py +1 -4
- attune_llm/commands/__init__.py +10 -10
- attune_llm/commands/models.py +3 -3
- attune_llm/config/__init__.py +8 -8
- attune_llm/learning/__init__.py +3 -3
- attune_llm/learning/extractor.py +5 -3
- attune_llm/learning/storage.py +5 -3
- attune_llm/security/__init__.py +17 -17
- attune_llm/utils/tokens.py +3 -1
- attune/cli_legacy.py +0 -3978
- attune/memory/short_term.py +0 -2192
- attune/workflows/manage_docs.py +0 -87
- attune/workflows/test5.py +0 -125
- {attune_ai-2.1.5.dist-info → attune_ai-2.2.1.dist-info}/WHEEL +0 -0
- {attune_ai-2.1.5.dist-info → attune_ai-2.2.1.dist-info}/licenses/LICENSE +0 -0
- {attune_ai-2.1.5.dist-info → attune_ai-2.2.1.dist-info}/licenses/LICENSE_CHANGE_ANNOUNCEMENT.md +0 -0
- {attune_ai-2.1.5.dist-info → attune_ai-2.2.1.dist-info}/top_level.txt +0 -0
|
@@ -57,13 +57,15 @@ class EvalExecDetector(ast.NodeVisitor):
|
|
|
57
57
|
|
|
58
58
|
if func_name in ("eval", "exec"):
|
|
59
59
|
# Found a real eval/exec call!
|
|
60
|
-
self.findings.append(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
60
|
+
self.findings.append(
|
|
61
|
+
{
|
|
62
|
+
"type": "command_injection",
|
|
63
|
+
"function": func_name,
|
|
64
|
+
"line": node.lineno,
|
|
65
|
+
"col": node.col_offset,
|
|
66
|
+
"context": self._current_function,
|
|
67
|
+
}
|
|
68
|
+
)
|
|
67
69
|
|
|
68
70
|
self.generic_visit(node)
|
|
69
71
|
|
|
@@ -125,12 +127,10 @@ def is_scanner_implementation_file(file_path: str) -> bool:
|
|
|
125
127
|
"owasp",
|
|
126
128
|
"secrets_detector",
|
|
127
129
|
"pii_scrubber",
|
|
128
|
-
|
|
129
130
|
# Pattern/rule definition files
|
|
130
131
|
"patterns.py",
|
|
131
132
|
"rules.py",
|
|
132
133
|
"checks.py",
|
|
133
|
-
|
|
134
134
|
# Test files for security scanners
|
|
135
135
|
"test_bug_predict",
|
|
136
136
|
"test_security",
|
|
@@ -161,7 +161,11 @@ def is_in_docstring_or_comment(line_content: str, file_content: str, line_num: i
|
|
|
161
161
|
return True
|
|
162
162
|
|
|
163
163
|
# Check for inline comments
|
|
164
|
-
if
|
|
164
|
+
if (
|
|
165
|
+
"#" in line_content and line_content.index("#") < line_content.find("eval")
|
|
166
|
+
if "eval" in line_content
|
|
167
|
+
else True
|
|
168
|
+
):
|
|
165
169
|
return True
|
|
166
170
|
|
|
167
171
|
# Parse file as AST to find docstrings
|
|
@@ -206,8 +210,7 @@ def is_in_docstring_or_comment(line_content: str, file_content: str, line_num: i
|
|
|
206
210
|
|
|
207
211
|
|
|
208
212
|
def enhanced_command_injection_detection(
|
|
209
|
-
file_path: str,
|
|
210
|
-
original_findings: list[dict[str, Any]]
|
|
213
|
+
file_path: str, original_findings: list[dict[str, Any]]
|
|
211
214
|
) -> list[dict[str, Any]]:
|
|
212
215
|
"""Enhanced command injection detection with AST-based filtering.
|
|
213
216
|
|
|
@@ -248,21 +251,24 @@ def enhanced_command_injection_detection(
|
|
|
248
251
|
|
|
249
252
|
# Check if this is a test file (downgrade severity)
|
|
250
253
|
from .security_audit import TEST_FILE_PATTERNS
|
|
254
|
+
|
|
251
255
|
is_test_file = any(re.search(pat, file_path) for pat in TEST_FILE_PATTERNS)
|
|
252
256
|
|
|
253
257
|
# Convert AST findings to format compatible with original
|
|
254
258
|
filtered = []
|
|
255
259
|
for finding in ast_findings:
|
|
256
|
-
filtered.append(
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
260
|
+
filtered.append(
|
|
261
|
+
{
|
|
262
|
+
"type": "command_injection",
|
|
263
|
+
"file": file_path,
|
|
264
|
+
"line": finding["line"],
|
|
265
|
+
"match": f"{finding['function']}(",
|
|
266
|
+
"severity": "low" if is_test_file else "critical",
|
|
267
|
+
"owasp": "A03:2021 Injection",
|
|
268
|
+
"context": finding.get("context", ""),
|
|
269
|
+
"is_test": is_test_file,
|
|
270
|
+
}
|
|
271
|
+
)
|
|
266
272
|
|
|
267
273
|
# Keep subprocess/os.system findings (not filtered by AST)
|
|
268
274
|
filtered.extend(subprocess_findings)
|
|
@@ -244,29 +244,35 @@ class SEOOptimizationWorkflow(BaseWorkflow):
|
|
|
244
244
|
|
|
245
245
|
# Check for missing meta description
|
|
246
246
|
if "description:" not in content and "# " in content:
|
|
247
|
-
issues.append(
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
247
|
+
issues.append(
|
|
248
|
+
{
|
|
249
|
+
"file": file_path,
|
|
250
|
+
"element": "meta_description",
|
|
251
|
+
"severity": "critical",
|
|
252
|
+
"message": "Missing meta description",
|
|
253
|
+
}
|
|
254
|
+
)
|
|
253
255
|
|
|
254
256
|
# Check for H1 count
|
|
255
257
|
h1_count = content.count("# ")
|
|
256
258
|
if h1_count == 0:
|
|
257
|
-
issues.append(
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
259
|
+
issues.append(
|
|
260
|
+
{
|
|
261
|
+
"file": file_path,
|
|
262
|
+
"element": "h1_count",
|
|
263
|
+
"severity": "warning",
|
|
264
|
+
"message": "No H1 heading found",
|
|
265
|
+
}
|
|
266
|
+
)
|
|
263
267
|
elif h1_count > 1:
|
|
264
|
-
issues.append(
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
268
|
+
issues.append(
|
|
269
|
+
{
|
|
270
|
+
"file": file_path,
|
|
271
|
+
"element": "h1_count",
|
|
272
|
+
"severity": "warning",
|
|
273
|
+
"message": f"Multiple H1 headings ({h1_count})",
|
|
274
|
+
}
|
|
275
|
+
)
|
|
270
276
|
except Exception:
|
|
271
277
|
pass # Skip files that can't be read
|
|
272
278
|
|
|
@@ -310,15 +316,11 @@ class SEOOptimizationWorkflow(BaseWorkflow):
|
|
|
310
316
|
recommendations.append(recommendation)
|
|
311
317
|
else:
|
|
312
318
|
# Low confidence - flag for user clarification
|
|
313
|
-
recommendation["question"] = self._generate_clarifying_question(
|
|
314
|
-
issue, confidence
|
|
315
|
-
)
|
|
319
|
+
recommendation["question"] = self._generate_clarifying_question(issue, confidence)
|
|
316
320
|
needs_clarification.append(recommendation)
|
|
317
321
|
|
|
318
322
|
# Sort recommendations by priority and confidence
|
|
319
|
-
recommendations.sort(
|
|
320
|
-
key=lambda r: (r["priority"] == "high", r["confidence"]), reverse=True
|
|
321
|
-
)
|
|
323
|
+
recommendations.sort(key=lambda r: (r["priority"] == "high", r["confidence"]), reverse=True)
|
|
322
324
|
|
|
323
325
|
return {
|
|
324
326
|
"total_recommendations": len(recommendations),
|
|
@@ -328,9 +330,7 @@ class SEOOptimizationWorkflow(BaseWorkflow):
|
|
|
328
330
|
"clarification_needed": needs_clarification,
|
|
329
331
|
}
|
|
330
332
|
|
|
331
|
-
def _generate_clarifying_question(
|
|
332
|
-
self, issue: dict[str, Any], confidence: float
|
|
333
|
-
) -> str:
|
|
333
|
+
def _generate_clarifying_question(self, issue: dict[str, Any], confidence: float) -> str:
|
|
334
334
|
"""Generate clarifying question for low-confidence issues.
|
|
335
335
|
|
|
336
336
|
Args:
|
|
@@ -7,7 +7,6 @@ Licensed under Fair Source License 0.9
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
|
|
11
10
|
def generate_test_for_function(module: str, func: dict) -> str:
|
|
12
11
|
"""Generate executable tests for a function based on AST analysis."""
|
|
13
12
|
name = func["name"]
|
|
@@ -215,9 +214,7 @@ def generate_test_cases_for_params(params: list) -> dict:
|
|
|
215
214
|
return {
|
|
216
215
|
"valid_args": valid_args,
|
|
217
216
|
"parametrize_cases": parametrize_cases[:5], # Limit cases
|
|
218
|
-
"edge_cases": list(dict.fromkeys(edge_cases))[
|
|
219
|
-
:5
|
|
220
|
-
], # Unique edge cases (preserves order)
|
|
217
|
+
"edge_cases": list(dict.fromkeys(edge_cases))[:5], # Unique edge cases (preserves order)
|
|
221
218
|
}
|
|
222
219
|
|
|
223
220
|
|
|
@@ -122,20 +122,13 @@ class BehavioralTestLLMGenerator(LLMWorkflowGenerator):
|
|
|
122
122
|
Formatted prompt for LLM
|
|
123
123
|
"""
|
|
124
124
|
module_name = Path(module_info.file_path).stem
|
|
125
|
-
import_path = (
|
|
126
|
-
module_info.file_path.replace("src/", "").replace(".py", "").replace("/", ".")
|
|
127
|
-
)
|
|
125
|
+
import_path = module_info.file_path.replace("src/", "").replace(".py", "").replace("/", ".")
|
|
128
126
|
|
|
129
127
|
# Build context about what to test
|
|
130
128
|
classes_info = "\n".join(
|
|
131
|
-
[
|
|
132
|
-
f" - {cls['name']}: {len(cls['methods'])} methods"
|
|
133
|
-
for cls in module_info.classes
|
|
134
|
-
]
|
|
135
|
-
)
|
|
136
|
-
functions_info = "\n".join(
|
|
137
|
-
[f" - {func['name']}()" for func in module_info.functions[:10]]
|
|
129
|
+
[f" - {cls['name']}: {len(cls['methods'])} methods" for cls in module_info.classes]
|
|
138
130
|
)
|
|
131
|
+
functions_info = "\n".join([f" - {func['name']}()" for func in module_info.functions[:10]])
|
|
139
132
|
|
|
140
133
|
prompt = f"""Generate comprehensive behavioral tests for this Python module.
|
|
141
134
|
|
|
@@ -210,9 +203,7 @@ Return ONLY the complete Python test file content, no explanations."""
|
|
|
210
203
|
Template test file content
|
|
211
204
|
"""
|
|
212
205
|
module_name = Path(module_info.file_path).stem
|
|
213
|
-
import_path = (
|
|
214
|
-
module_info.file_path.replace("src/", "").replace(".py", "").replace("/", ".")
|
|
215
|
-
)
|
|
206
|
+
import_path = module_info.file_path.replace("src/", "").replace(".py", "").replace("/", ".")
|
|
216
207
|
|
|
217
208
|
lines = [
|
|
218
209
|
f'"""Behavioral tests for {module_name}.py - GENERATED BY EMPATHY FRAMEWORK.',
|
|
@@ -371,9 +362,7 @@ class BehavioralTestGenerationWorkflow(BaseWorkflow):
|
|
|
371
362
|
),
|
|
372
363
|
},
|
|
373
364
|
)
|
|
374
|
-
self.llm_generator = BehavioralTestLLMGenerator(
|
|
375
|
-
model_tier=model_tier, use_llm=use_llm
|
|
376
|
-
)
|
|
365
|
+
self.llm_generator = BehavioralTestLLMGenerator(model_tier=model_tier, use_llm=use_llm)
|
|
377
366
|
|
|
378
367
|
def analyze_module(self, file_path: Path) -> ModuleInfo:
|
|
379
368
|
"""Analyze a Python module."""
|
|
@@ -451,9 +440,7 @@ class BehavioralTestGenerationWorkflow(BaseWorkflow):
|
|
|
451
440
|
test_path = output_path / test_filename
|
|
452
441
|
|
|
453
442
|
# Generate comprehensive tests with LLM
|
|
454
|
-
template = self.generate_test_template(
|
|
455
|
-
module_info, test_path, source_code=source_code
|
|
456
|
-
)
|
|
443
|
+
template = self.generate_test_template(module_info, test_path, source_code=source_code)
|
|
457
444
|
test_path.write_text(template)
|
|
458
445
|
|
|
459
446
|
generated_files.append(str(test_path))
|
|
@@ -93,12 +93,12 @@ Generate complete, runnable tests that will increase coverage.""",
|
|
|
93
93
|
import subprocess
|
|
94
94
|
|
|
95
95
|
subprocess.run(
|
|
96
|
-
["coverage", "json", "-o", "/tmp/coverage_batch.json"],
|
|
96
|
+
["coverage", "json", "-o", "/tmp/coverage_batch.json"], # nosec B108
|
|
97
97
|
capture_output=True,
|
|
98
98
|
check=True,
|
|
99
99
|
)
|
|
100
100
|
|
|
101
|
-
with open("/tmp/coverage_batch.json") as f:
|
|
101
|
+
with open("/tmp/coverage_batch.json") as f: # nosec B108
|
|
102
102
|
data = json.load(f)
|
|
103
103
|
|
|
104
104
|
coverage_by_file = []
|
|
@@ -136,9 +136,7 @@ Generate complete, runnable tests that will increase coverage.""",
|
|
|
136
136
|
for n in node.body
|
|
137
137
|
if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef))
|
|
138
138
|
]
|
|
139
|
-
classes.append(
|
|
140
|
-
{"name": node.name, "methods": methods, "line": node.lineno}
|
|
141
|
-
)
|
|
139
|
+
classes.append({"name": node.name, "methods": methods, "line": node.lineno})
|
|
142
140
|
elif isinstance(node, ast.FunctionDef) and node.col_offset == 0:
|
|
143
141
|
functions.append({"name": node.name, "line": node.lineno})
|
|
144
142
|
|
|
@@ -278,7 +276,11 @@ Output the COMPLETE test file, no TODOs remaining."""
|
|
|
278
276
|
return content
|
|
279
277
|
|
|
280
278
|
async def execute(
|
|
281
|
-
self,
|
|
279
|
+
self,
|
|
280
|
+
top: int = 200,
|
|
281
|
+
batch_size: int = 10,
|
|
282
|
+
output_dir: str = "tests/behavioral/generated",
|
|
283
|
+
**kwargs,
|
|
282
284
|
) -> WorkflowResult:
|
|
283
285
|
"""Execute parallel test generation workflow.
|
|
284
286
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: attune-ai
|
|
3
|
-
Version: 2.1
|
|
4
|
-
Summary: AI
|
|
3
|
+
Version: 2.2.1
|
|
4
|
+
Summary: AI-powered developer workflows for Claude with cost optimization, multi-agent orchestration, and workflow automation.
|
|
5
5
|
Author-email: Patrick Roebuck <admin@smartaimemory.com>
|
|
6
6
|
Maintainer-email: Smart-AI-Memory <admin@smartaimemory.com>
|
|
7
7
|
License: Apache License
|
|
@@ -240,6 +240,7 @@ Requires-Dist: rich<14.0.0,>=13.0.0
|
|
|
240
240
|
Requires-Dist: typer<1.0.0,>=0.9.0
|
|
241
241
|
Requires-Dist: pyyaml<7.0,>=6.0
|
|
242
242
|
Requires-Dist: anthropic<1.0.0,>=0.25.0
|
|
243
|
+
Requires-Dist: redis<6.0.0,>=4.0.0
|
|
243
244
|
Provides-Extra: anthropic
|
|
244
245
|
Requires-Dist: anthropic<1.0.0,>=0.25.0; extra == "anthropic"
|
|
245
246
|
Provides-Extra: openai
|
|
@@ -428,7 +429,7 @@ Dynamic: license-file
|
|
|
428
429
|
Run code review, debugging, testing, and release workflows from your terminal or Claude Code. Smart tier routing saves 34-86% on LLM costs.
|
|
429
430
|
|
|
430
431
|
[](https://pypi.org/project/attune-ai/)
|
|
431
|
-
[](https://github.com/Smart-AI-Memory/attune-ai/actions/workflows/tests.yml)
|
|
432
433
|
[](https://www.python.org)
|
|
433
434
|
[](LICENSE)
|
|
434
435
|
|