empathy-framework 5.0.3__py3-none-any.whl → 5.1.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.
- {empathy_framework-5.0.3.dist-info → empathy_framework-5.1.1.dist-info}/METADATA +259 -142
- {empathy_framework-5.0.3.dist-info → empathy_framework-5.1.1.dist-info}/RECORD +58 -28
- empathy_framework-5.1.1.dist-info/licenses/LICENSE +201 -0
- empathy_framework-5.1.1.dist-info/licenses/LICENSE_CHANGE_ANNOUNCEMENT.md +101 -0
- empathy_os/__init__.py +1 -1
- empathy_os/cli/commands/batch.py +5 -5
- empathy_os/cli/commands/routing.py +1 -1
- empathy_os/cli/commands/workflow.py +2 -1
- empathy_os/cli/parsers/cache 2.py +65 -0
- empathy_os/cli_minimal.py +3 -3
- empathy_os/cli_router 2.py +416 -0
- empathy_os/cli_router.py +12 -0
- empathy_os/dashboard/__init__.py +1 -2
- empathy_os/dashboard/app 2.py +512 -0
- empathy_os/dashboard/app.py +1 -1
- empathy_os/dashboard/simple_server 2.py +403 -0
- empathy_os/dashboard/standalone_server 2.py +536 -0
- empathy_os/memory/types 2.py +441 -0
- empathy_os/meta_workflows/intent_detector.py +71 -0
- empathy_os/models/__init__.py +19 -0
- empathy_os/models/adaptive_routing 2.py +437 -0
- empathy_os/models/auth_cli.py +444 -0
- empathy_os/models/auth_strategy.py +450 -0
- empathy_os/project_index/scanner_parallel 2.py +291 -0
- empathy_os/telemetry/agent_coordination 2.py +478 -0
- empathy_os/telemetry/agent_coordination.py +3 -3
- empathy_os/telemetry/agent_tracking 2.py +350 -0
- empathy_os/telemetry/agent_tracking.py +1 -2
- empathy_os/telemetry/approval_gates 2.py +563 -0
- empathy_os/telemetry/event_streaming 2.py +405 -0
- empathy_os/telemetry/event_streaming.py +3 -3
- empathy_os/telemetry/feedback_loop 2.py +557 -0
- empathy_os/telemetry/feedback_loop.py +1 -1
- empathy_os/vscode_bridge 2.py +173 -0
- empathy_os/workflows/__init__.py +8 -0
- empathy_os/workflows/autonomous_test_gen.py +569 -0
- empathy_os/workflows/bug_predict.py +45 -0
- empathy_os/workflows/code_review.py +92 -22
- empathy_os/workflows/document_gen.py +594 -62
- empathy_os/workflows/llm_base.py +363 -0
- empathy_os/workflows/perf_audit.py +69 -0
- empathy_os/workflows/progressive/README 2.md +454 -0
- empathy_os/workflows/progressive/__init__ 2.py +92 -0
- empathy_os/workflows/progressive/cli 2.py +242 -0
- empathy_os/workflows/progressive/core 2.py +488 -0
- empathy_os/workflows/progressive/orchestrator 2.py +701 -0
- empathy_os/workflows/progressive/reports 2.py +528 -0
- empathy_os/workflows/progressive/telemetry 2.py +280 -0
- empathy_os/workflows/progressive/test_gen 2.py +514 -0
- empathy_os/workflows/progressive/workflow 2.py +628 -0
- empathy_os/workflows/release_prep.py +54 -0
- empathy_os/workflows/security_audit.py +154 -79
- empathy_os/workflows/test_gen.py +60 -0
- empathy_os/workflows/test_gen_behavioral.py +477 -0
- empathy_os/workflows/test_gen_parallel.py +341 -0
- empathy_framework-5.0.3.dist-info/licenses/LICENSE +0 -139
- {empathy_framework-5.0.3.dist-info → empathy_framework-5.1.1.dist-info}/WHEEL +0 -0
- {empathy_framework-5.0.3.dist-info → empathy_framework-5.1.1.dist-info}/entry_points.txt +0 -0
- {empathy_framework-5.0.3.dist-info → empathy_framework-5.1.1.dist-info}/top_level.txt +0 -0
|
@@ -60,6 +60,7 @@ class ReleasePreparationWorkflow(BaseWorkflow):
|
|
|
60
60
|
skip_approve_if_clean: bool = True,
|
|
61
61
|
use_security_crew: bool = False,
|
|
62
62
|
crew_config: dict | None = None,
|
|
63
|
+
enable_auth_strategy: bool = True,
|
|
63
64
|
**kwargs: Any,
|
|
64
65
|
):
|
|
65
66
|
"""Initialize release preparation workflow.
|
|
@@ -68,6 +69,7 @@ class ReleasePreparationWorkflow(BaseWorkflow):
|
|
|
68
69
|
skip_approve_if_clean: Skip premium approval if all checks pass
|
|
69
70
|
use_security_crew: Enable SecurityAuditCrew for comprehensive security audit
|
|
70
71
|
crew_config: Configuration dict for SecurityAuditCrew
|
|
72
|
+
enable_auth_strategy: Enable intelligent auth routing (default: True)
|
|
71
73
|
**kwargs: Additional arguments passed to BaseWorkflow
|
|
72
74
|
|
|
73
75
|
"""
|
|
@@ -75,7 +77,9 @@ class ReleasePreparationWorkflow(BaseWorkflow):
|
|
|
75
77
|
self.skip_approve_if_clean = skip_approve_if_clean
|
|
76
78
|
self.use_security_crew = use_security_crew
|
|
77
79
|
self.crew_config = crew_config or {}
|
|
80
|
+
self.enable_auth_strategy = enable_auth_strategy
|
|
78
81
|
self._has_blockers: bool = False
|
|
82
|
+
self._auth_mode_used: str | None = None
|
|
79
83
|
|
|
80
84
|
# Dynamically configure stages based on security crew setting
|
|
81
85
|
if use_security_crew:
|
|
@@ -137,6 +141,52 @@ class ReleasePreparationWorkflow(BaseWorkflow):
|
|
|
137
141
|
Executes lint, type checking, and tests.
|
|
138
142
|
"""
|
|
139
143
|
target_path = input_data.get("path", ".")
|
|
144
|
+
|
|
145
|
+
# === AUTH STRATEGY INTEGRATION ===
|
|
146
|
+
if self.enable_auth_strategy:
|
|
147
|
+
try:
|
|
148
|
+
import logging
|
|
149
|
+
from pathlib import Path
|
|
150
|
+
|
|
151
|
+
from empathy_os.models import (
|
|
152
|
+
count_lines_of_code,
|
|
153
|
+
get_auth_strategy,
|
|
154
|
+
get_module_size_category,
|
|
155
|
+
)
|
|
156
|
+
logger = logging.getLogger(__name__)
|
|
157
|
+
|
|
158
|
+
# Calculate total LOC for project/directory
|
|
159
|
+
target = Path(target_path)
|
|
160
|
+
total_lines = 0
|
|
161
|
+
if target.is_file():
|
|
162
|
+
total_lines = count_lines_of_code(target)
|
|
163
|
+
elif target.is_dir():
|
|
164
|
+
for py_file in target.rglob("*.py"):
|
|
165
|
+
try:
|
|
166
|
+
total_lines += count_lines_of_code(py_file)
|
|
167
|
+
except Exception:
|
|
168
|
+
pass
|
|
169
|
+
|
|
170
|
+
if total_lines > 0:
|
|
171
|
+
strategy = get_auth_strategy()
|
|
172
|
+
recommended_mode = strategy.get_recommended_mode(total_lines)
|
|
173
|
+
self._auth_mode_used = recommended_mode.value
|
|
174
|
+
|
|
175
|
+
size_category = get_module_size_category(total_lines)
|
|
176
|
+
logger.info(f"Release prep target: {target_path} ({total_lines:,} LOC, {size_category})")
|
|
177
|
+
logger.info(f"Recommended auth mode: {recommended_mode.value}")
|
|
178
|
+
|
|
179
|
+
cost_estimate = strategy.estimate_cost(total_lines, recommended_mode)
|
|
180
|
+
if recommended_mode.value == "subscription":
|
|
181
|
+
logger.info(f"Cost: {cost_estimate['quota_cost']}")
|
|
182
|
+
else:
|
|
183
|
+
logger.info(f"Cost: ~${cost_estimate['monetary_cost']:.4f}")
|
|
184
|
+
|
|
185
|
+
except Exception as e:
|
|
186
|
+
import logging
|
|
187
|
+
logger = logging.getLogger(__name__)
|
|
188
|
+
logger.warning(f"Auth strategy detection failed: {e}")
|
|
189
|
+
|
|
140
190
|
checks: dict[str, dict] = {}
|
|
141
191
|
|
|
142
192
|
# Lint check (ruff)
|
|
@@ -640,6 +690,10 @@ Provide a comprehensive release readiness assessment."""
|
|
|
640
690
|
"model_tier_used": tier.value,
|
|
641
691
|
}
|
|
642
692
|
|
|
693
|
+
# Include auth mode used for telemetry
|
|
694
|
+
if self._auth_mode_used:
|
|
695
|
+
result["auth_mode_used"] = self._auth_mode_used
|
|
696
|
+
|
|
643
697
|
# Merge parsed XML data if available
|
|
644
698
|
if parsed_data.get("xml_parsed"):
|
|
645
699
|
result.update(
|
|
@@ -212,6 +212,7 @@ class SecurityAuditWorkflow(BaseWorkflow):
|
|
|
212
212
|
use_crew_for_assessment: bool = True,
|
|
213
213
|
use_crew_for_remediation: bool = False,
|
|
214
214
|
crew_config: dict | None = None,
|
|
215
|
+
enable_auth_strategy: bool = True,
|
|
215
216
|
**kwargs: Any,
|
|
216
217
|
):
|
|
217
218
|
"""Initialize security audit workflow.
|
|
@@ -222,6 +223,8 @@ class SecurityAuditWorkflow(BaseWorkflow):
|
|
|
222
223
|
use_crew_for_assessment: Use SecurityAuditCrew for vulnerability assessment (default: True)
|
|
223
224
|
use_crew_for_remediation: Use SecurityAuditCrew for enhanced remediation (default: True)
|
|
224
225
|
crew_config: Configuration dict for SecurityAuditCrew
|
|
226
|
+
enable_auth_strategy: If True, use intelligent subscription vs API routing
|
|
227
|
+
based on codebase size (default: True)
|
|
225
228
|
**kwargs: Additional arguments passed to BaseWorkflow
|
|
226
229
|
|
|
227
230
|
"""
|
|
@@ -231,10 +234,12 @@ class SecurityAuditWorkflow(BaseWorkflow):
|
|
|
231
234
|
self.use_crew_for_assessment = use_crew_for_assessment
|
|
232
235
|
self.use_crew_for_remediation = use_crew_for_remediation
|
|
233
236
|
self.crew_config = crew_config or {}
|
|
237
|
+
self.enable_auth_strategy = enable_auth_strategy
|
|
234
238
|
self._has_critical: bool = False
|
|
235
239
|
self._team_decisions: dict[str, dict] = {}
|
|
236
240
|
self._crew: Any = None
|
|
237
241
|
self._crew_available = False
|
|
242
|
+
self._auth_mode_used: str | None = None # Track which auth was recommended
|
|
238
243
|
self._load_team_decisions()
|
|
239
244
|
|
|
240
245
|
def _load_team_decisions(self) -> None:
|
|
@@ -312,91 +317,101 @@ class SecurityAuditWorkflow(BaseWorkflow):
|
|
|
312
317
|
|
|
313
318
|
target = Path(target_path)
|
|
314
319
|
if target.exists():
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
320
|
+
# Handle both file and directory targets
|
|
321
|
+
files_to_scan: list[Path] = []
|
|
322
|
+
if target.is_file():
|
|
323
|
+
# Single file - check if it matches file_types
|
|
324
|
+
if any(str(target).endswith(ext) for ext in file_types):
|
|
325
|
+
files_to_scan = [target]
|
|
326
|
+
else:
|
|
327
|
+
# Directory - recursively find all matching files
|
|
328
|
+
for ext in file_types:
|
|
329
|
+
for file_path in target.rglob(f"*{ext}"):
|
|
330
|
+
# Skip excluded directories
|
|
331
|
+
if any(skip in str(file_path) for skip in SKIP_DIRECTORIES):
|
|
332
|
+
continue
|
|
333
|
+
files_to_scan.append(file_path)
|
|
334
|
+
|
|
335
|
+
for file_path in files_to_scan:
|
|
336
|
+
try:
|
|
337
|
+
content = file_path.read_text(errors="ignore")
|
|
338
|
+
lines = content.split("\n")
|
|
339
|
+
files_scanned += 1
|
|
340
|
+
|
|
341
|
+
for vuln_type, vuln_info in SECURITY_PATTERNS.items():
|
|
342
|
+
for pattern in vuln_info["patterns"]:
|
|
343
|
+
matches = list(re.finditer(pattern, content, re.IGNORECASE))
|
|
344
|
+
for match in matches:
|
|
345
|
+
# Find line number and get the line content
|
|
346
|
+
line_num = content[: match.start()].count("\n") + 1
|
|
347
|
+
line_content = (
|
|
348
|
+
lines[line_num - 1] if line_num <= len(lines) else ""
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
# Skip if file is a security example/test file
|
|
352
|
+
file_name = str(file_path)
|
|
353
|
+
if any(exp in file_name for exp in SECURITY_EXAMPLE_PATHS):
|
|
354
|
+
continue
|
|
355
|
+
|
|
356
|
+
# Skip if this looks like detection/scanning code
|
|
357
|
+
if self._is_detection_code(line_content, match.group()):
|
|
358
|
+
continue
|
|
359
|
+
|
|
360
|
+
# Phase 2: Skip safe SQL parameterization patterns
|
|
361
|
+
if vuln_type == "sql_injection":
|
|
362
|
+
if self._is_safe_sql_parameterization(
|
|
363
|
+
line_content,
|
|
364
|
+
match.group(),
|
|
365
|
+
content,
|
|
366
|
+
):
|
|
367
|
+
continue
|
|
368
|
+
|
|
369
|
+
# Skip fake/test credentials
|
|
370
|
+
if vuln_type == "hardcoded_secret":
|
|
371
|
+
if self._is_fake_credential(match.group()):
|
|
339
372
|
continue
|
|
340
373
|
|
|
341
|
-
|
|
342
|
-
|
|
374
|
+
# Phase 2: Skip safe random usage (tests, demos, documented)
|
|
375
|
+
if vuln_type == "insecure_random":
|
|
376
|
+
if self._is_safe_random_usage(
|
|
377
|
+
line_content,
|
|
378
|
+
file_name,
|
|
379
|
+
content,
|
|
380
|
+
):
|
|
343
381
|
continue
|
|
344
382
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
):
|
|
352
|
-
continue
|
|
353
|
-
|
|
354
|
-
# Skip fake/test credentials
|
|
355
|
-
if vuln_type == "hardcoded_secret":
|
|
356
|
-
if self._is_fake_credential(match.group()):
|
|
357
|
-
continue
|
|
358
|
-
|
|
359
|
-
# Phase 2: Skip safe random usage (tests, demos, documented)
|
|
360
|
-
if vuln_type == "insecure_random":
|
|
361
|
-
if self._is_safe_random_usage(
|
|
362
|
-
line_content,
|
|
363
|
-
file_name,
|
|
364
|
-
content,
|
|
365
|
-
):
|
|
366
|
-
continue
|
|
367
|
-
|
|
368
|
-
# Skip command_injection in documentation strings
|
|
369
|
-
if vuln_type == "command_injection":
|
|
370
|
-
if self._is_documentation_or_string(
|
|
371
|
-
line_content,
|
|
372
|
-
match.group(),
|
|
373
|
-
):
|
|
374
|
-
continue
|
|
375
|
-
|
|
376
|
-
# Check if this is a test file - downgrade to informational
|
|
377
|
-
is_test_file = any(
|
|
378
|
-
re.search(pat, file_name) for pat in TEST_FILE_PATTERNS
|
|
379
|
-
)
|
|
380
|
-
|
|
381
|
-
# Skip test file findings for hardcoded_secret (expected in tests)
|
|
382
|
-
if is_test_file and vuln_type == "hardcoded_secret":
|
|
383
|
+
# Skip command_injection in documentation strings
|
|
384
|
+
if vuln_type == "command_injection":
|
|
385
|
+
if self._is_documentation_or_string(
|
|
386
|
+
line_content,
|
|
387
|
+
match.group(),
|
|
388
|
+
):
|
|
383
389
|
continue
|
|
384
390
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
391
|
+
# Check if this is a test file - downgrade to informational
|
|
392
|
+
is_test_file = any(
|
|
393
|
+
re.search(pat, file_name) for pat in TEST_FILE_PATTERNS
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
# Skip test file findings for hardcoded_secret (expected in tests)
|
|
397
|
+
if is_test_file and vuln_type == "hardcoded_secret":
|
|
398
|
+
continue
|
|
399
|
+
|
|
400
|
+
findings.append(
|
|
401
|
+
{
|
|
402
|
+
"type": vuln_type,
|
|
403
|
+
"file": str(file_path),
|
|
404
|
+
"line": line_num,
|
|
405
|
+
"match": match.group()[:100],
|
|
406
|
+
"severity": (
|
|
407
|
+
"low" if is_test_file else vuln_info["severity"]
|
|
408
|
+
),
|
|
409
|
+
"owasp": vuln_info["owasp"],
|
|
410
|
+
"is_test": is_test_file,
|
|
411
|
+
},
|
|
412
|
+
)
|
|
413
|
+
except OSError:
|
|
414
|
+
continue
|
|
400
415
|
|
|
401
416
|
# Phase 3: Apply AST-based filtering for command injection
|
|
402
417
|
try:
|
|
@@ -421,6 +436,64 @@ class SecurityAuditWorkflow(BaseWorkflow):
|
|
|
421
436
|
except Exception as e:
|
|
422
437
|
logger.warning(f"Phase 3 filtering failed: {e}")
|
|
423
438
|
|
|
439
|
+
# === AUTH STRATEGY INTEGRATION ===
|
|
440
|
+
# Detect codebase size and recommend auth mode (first stage only)
|
|
441
|
+
if self.enable_auth_strategy:
|
|
442
|
+
try:
|
|
443
|
+
from empathy_os.models import (
|
|
444
|
+
count_lines_of_code,
|
|
445
|
+
get_auth_strategy,
|
|
446
|
+
get_module_size_category,
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
# Calculate codebase size
|
|
450
|
+
codebase_lines = 0
|
|
451
|
+
if target.exists():
|
|
452
|
+
if target.is_file():
|
|
453
|
+
codebase_lines = count_lines_of_code(target)
|
|
454
|
+
elif target.is_dir():
|
|
455
|
+
# Sum lines across all Python files
|
|
456
|
+
for py_file in target.rglob("*.py"):
|
|
457
|
+
try:
|
|
458
|
+
codebase_lines += count_lines_of_code(py_file)
|
|
459
|
+
except Exception:
|
|
460
|
+
pass
|
|
461
|
+
|
|
462
|
+
if codebase_lines > 0:
|
|
463
|
+
# Get auth strategy (first-time setup if needed)
|
|
464
|
+
strategy = get_auth_strategy()
|
|
465
|
+
|
|
466
|
+
# Get recommended auth mode
|
|
467
|
+
recommended_mode = strategy.get_recommended_mode(codebase_lines)
|
|
468
|
+
self._auth_mode_used = recommended_mode.value
|
|
469
|
+
|
|
470
|
+
# Get size category
|
|
471
|
+
size_category = get_module_size_category(codebase_lines)
|
|
472
|
+
|
|
473
|
+
# Log recommendation
|
|
474
|
+
logger.info(
|
|
475
|
+
f"Codebase: {target} ({codebase_lines} LOC, {size_category})"
|
|
476
|
+
)
|
|
477
|
+
logger.info(f"Recommended auth mode: {recommended_mode.value}")
|
|
478
|
+
|
|
479
|
+
# Get cost estimate
|
|
480
|
+
cost_estimate = strategy.estimate_cost(codebase_lines, recommended_mode)
|
|
481
|
+
|
|
482
|
+
if recommended_mode.value == "subscription":
|
|
483
|
+
logger.info(
|
|
484
|
+
f"Cost: {cost_estimate['quota_cost']} "
|
|
485
|
+
f"(fits in {cost_estimate['fits_in_context']} context)"
|
|
486
|
+
)
|
|
487
|
+
else: # API
|
|
488
|
+
logger.info(
|
|
489
|
+
f"Cost: ~${cost_estimate['monetary_cost']:.4f} "
|
|
490
|
+
f"(1M context window)"
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
except Exception as e:
|
|
494
|
+
# Don't fail workflow if auth strategy fails
|
|
495
|
+
logger.warning(f"Auth strategy detection failed: {e}")
|
|
496
|
+
|
|
424
497
|
input_tokens = len(str(input_data)) // 4
|
|
425
498
|
output_tokens = len(str(findings)) // 4
|
|
426
499
|
|
|
@@ -991,6 +1064,8 @@ Provide a detailed remediation plan with specific fixes."""
|
|
|
991
1064
|
"risk_level": assessment.get("risk_level", "unknown"),
|
|
992
1065
|
"model_tier_used": tier.value,
|
|
993
1066
|
"crew_enhanced": crew_enhanced,
|
|
1067
|
+
"auth_mode_used": self._auth_mode_used, # Track recommended auth mode
|
|
1068
|
+
**input_data, # Merge all previous stage data
|
|
994
1069
|
}
|
|
995
1070
|
|
|
996
1071
|
# Add crew-specific fields if enhanced
|
empathy_os/workflows/test_gen.py
CHANGED
|
@@ -391,6 +391,7 @@ class TestGenerationWorkflow(BaseWorkflow):
|
|
|
391
391
|
min_tests_for_review: int = 10,
|
|
392
392
|
write_tests: bool = False,
|
|
393
393
|
output_dir: str = "tests/generated",
|
|
394
|
+
enable_auth_strategy: bool = True,
|
|
394
395
|
**kwargs: Any,
|
|
395
396
|
):
|
|
396
397
|
"""Initialize test generation workflow.
|
|
@@ -400,6 +401,7 @@ class TestGenerationWorkflow(BaseWorkflow):
|
|
|
400
401
|
min_tests_for_review: Minimum tests generated to trigger premium review
|
|
401
402
|
write_tests: If True, write generated tests to output_dir
|
|
402
403
|
output_dir: Directory to write generated test files
|
|
404
|
+
enable_auth_strategy: Enable intelligent auth routing (default: True)
|
|
403
405
|
**kwargs: Additional arguments passed to BaseWorkflow
|
|
404
406
|
|
|
405
407
|
"""
|
|
@@ -408,8 +410,10 @@ class TestGenerationWorkflow(BaseWorkflow):
|
|
|
408
410
|
self.min_tests_for_review = min_tests_for_review
|
|
409
411
|
self.write_tests = write_tests
|
|
410
412
|
self.output_dir = output_dir
|
|
413
|
+
self.enable_auth_strategy = enable_auth_strategy
|
|
411
414
|
self._test_count: int = 0
|
|
412
415
|
self._bug_hotspots: list[str] = []
|
|
416
|
+
self._auth_mode_used: str | None = None
|
|
413
417
|
self._load_bug_hotspots()
|
|
414
418
|
|
|
415
419
|
def _load_bug_hotspots(self) -> None:
|
|
@@ -482,6 +486,57 @@ class TestGenerationWorkflow(BaseWorkflow):
|
|
|
482
486
|
target_path = input_data.get("path", ".")
|
|
483
487
|
file_types = input_data.get("file_types", [".py"])
|
|
484
488
|
|
|
489
|
+
# === AUTH STRATEGY INTEGRATION ===
|
|
490
|
+
if self.enable_auth_strategy:
|
|
491
|
+
try:
|
|
492
|
+
import logging
|
|
493
|
+
from pathlib import Path
|
|
494
|
+
|
|
495
|
+
from empathy_os.models import (
|
|
496
|
+
count_lines_of_code,
|
|
497
|
+
get_auth_strategy,
|
|
498
|
+
get_module_size_category,
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
logger = logging.getLogger(__name__)
|
|
502
|
+
|
|
503
|
+
# Calculate total LOC for the project/path
|
|
504
|
+
target = Path(target_path)
|
|
505
|
+
total_lines = 0
|
|
506
|
+
if target.is_file():
|
|
507
|
+
total_lines = count_lines_of_code(target)
|
|
508
|
+
elif target.is_dir():
|
|
509
|
+
# Estimate total lines for directory
|
|
510
|
+
for py_file in target.rglob("*.py"):
|
|
511
|
+
try:
|
|
512
|
+
total_lines += count_lines_of_code(py_file)
|
|
513
|
+
except Exception:
|
|
514
|
+
pass
|
|
515
|
+
|
|
516
|
+
if total_lines > 0:
|
|
517
|
+
strategy = get_auth_strategy()
|
|
518
|
+
recommended_mode = strategy.get_recommended_mode(total_lines)
|
|
519
|
+
self._auth_mode_used = recommended_mode.value
|
|
520
|
+
|
|
521
|
+
size_category = get_module_size_category(total_lines)
|
|
522
|
+
logger.info(
|
|
523
|
+
f"Test generation target: {target_path} "
|
|
524
|
+
f"({total_lines:,} LOC, {size_category})"
|
|
525
|
+
)
|
|
526
|
+
logger.info(f"Recommended auth mode: {recommended_mode.value}")
|
|
527
|
+
|
|
528
|
+
cost_estimate = strategy.estimate_cost(total_lines, recommended_mode)
|
|
529
|
+
if recommended_mode.value == "subscription":
|
|
530
|
+
logger.info(f"Cost: {cost_estimate['quota_cost']}")
|
|
531
|
+
else:
|
|
532
|
+
logger.info(f"Cost: ~${cost_estimate['monetary_cost']:.4f}")
|
|
533
|
+
|
|
534
|
+
except Exception as e:
|
|
535
|
+
import logging
|
|
536
|
+
|
|
537
|
+
logger = logging.getLogger(__name__)
|
|
538
|
+
logger.warning(f"Auth strategy detection failed: {e}")
|
|
539
|
+
|
|
485
540
|
# Parse configurable limits with sensible defaults
|
|
486
541
|
max_files_to_scan = input_data.get("max_files_to_scan", 1000)
|
|
487
542
|
max_file_size_kb = input_data.get("max_file_size_kb", 200)
|
|
@@ -1452,6 +1507,11 @@ END OF REQUIRED FORMAT - output nothing after recommendations."""
|
|
|
1452
1507
|
|
|
1453
1508
|
# Replace the previous analysis with the final, accurate report
|
|
1454
1509
|
input_data["analysis_report"] = report
|
|
1510
|
+
|
|
1511
|
+
# Include auth mode used for telemetry
|
|
1512
|
+
if self._auth_mode_used:
|
|
1513
|
+
input_data["auth_mode_used"] = self._auth_mode_used
|
|
1514
|
+
|
|
1455
1515
|
return input_data, total_in, total_out
|
|
1456
1516
|
|
|
1457
1517
|
def _response_contains_questions(self, response: str) -> bool:
|