petter2025 commited on
Commit
be28392
·
verified ·
1 Parent(s): 48d7a6c

Delete utils

Browse files
Files changed (2) hide show
  1. utils/async_runner.py +0 -243
  2. utils/installation.py +0 -195
utils/async_runner.py DELETED
@@ -1,243 +0,0 @@
1
- """
2
- Enhanced Async Utilities for Safe Async/Sync Integration
3
- FIXED VERSION: Consolidated from app.py with nest_asyncio compatibility and better error handling
4
- """
5
- import asyncio
6
- import functools
7
- import time
8
- import logging
9
- from typing import Any, Callable, Coroutine, Optional, TypeVar, Union
10
- from contextlib import contextmanager
11
-
12
- logger = logging.getLogger(__name__)
13
- T = TypeVar('T')
14
-
15
- class AsyncRunner:
16
- """
17
- Enhanced async runner with nest_asyncio compatibility and robust error handling
18
- Consolidated from app.py local implementation
19
- """
20
-
21
- @staticmethod
22
- def run_async(coro: Coroutine[Any, Any, T], timeout: Optional[float] = 30.0) -> Union[T, dict]:
23
- """
24
- FIXED: Run async coroutine in sync context with nest_asyncio compatibility
25
-
26
- Args:
27
- coro: Async coroutine to run
28
- timeout: Maximum time to wait for coroutine completion (seconds)
29
-
30
- Returns:
31
- Result of the coroutine or error dictionary if failed
32
- """
33
- start_time = time.time()
34
-
35
- try:
36
- # Try to get running loop first (nest_asyncio compatible)
37
- try:
38
- loop = asyncio.get_running_loop()
39
- logger.debug("✅ Running in existing async context (nest_asyncio detected)")
40
-
41
- # In a running loop, we need to schedule as a task
42
- # This handles the "event loop already running" case
43
- task = asyncio.create_task(coro)
44
-
45
- # For sync context, we need to run until complete
46
- # Use asyncio.run_coroutine_threadsafe for thread safety
47
- import concurrent.futures
48
- future = asyncio.run_coroutine_threadsafe(coro, loop)
49
-
50
- try:
51
- result = future.result(timeout=timeout)
52
- logger.debug(f"✅ Async execution completed in {time.time() - start_time:.2f}s")
53
- return result
54
- except concurrent.futures.TimeoutError:
55
- logger.error(f"❌ Async execution timed out after {timeout}s")
56
- future.cancel()
57
- return {
58
- "error": f"Async execution timed out after {timeout}s",
59
- "status": "failed",
60
- "timeout": True,
61
- "boundary_note": "Execution boundary reached - timeout"
62
- }
63
-
64
- except RuntimeError:
65
- # No running loop, create one
66
- logger.debug("🔄 Creating new event loop for async execution")
67
- loop = asyncio.new_event_loop()
68
- asyncio.set_event_loop(loop)
69
-
70
- try:
71
- result = loop.run_until_complete(asyncio.wait_for(coro, timeout=timeout))
72
- logger.debug(f"✅ Async execution completed in {time.time() - start_time:.2f}s")
73
- return result
74
- except asyncio.TimeoutError:
75
- logger.error(f"❌ Async execution timed out after {timeout}s")
76
- return {
77
- "error": f"Async execution timed out after {timeout}s",
78
- "status": "failed",
79
- "timeout": True,
80
- "boundary_note": "Execution boundary reached - timeout"
81
- }
82
- finally:
83
- # Clean up the loop
84
- if not loop.is_closed():
85
- loop.close()
86
-
87
- except Exception as e:
88
- logger.error(f"❌ Async execution failed: {e}", exc_info=True)
89
- return {
90
- "error": str(e),
91
- "status": "failed",
92
- "execution_time": time.time() - start_time,
93
- "boundary_note": "Execution boundary reached",
94
- "error_type": type(e).__name__
95
- }
96
-
97
- @staticmethod
98
- def async_to_sync(async_func: Callable[..., Coroutine[Any, Any, T]]) -> Callable[..., Union[T, dict]]:
99
- """
100
- FIXED: Decorator to convert async function to sync with enhanced error handling
101
-
102
- Usage:
103
- @AsyncRunner.async_to_sync
104
- async def my_async_function():
105
- ...
106
-
107
- # Can now be called synchronously
108
- result = my_async_function()
109
- """
110
- @functools.wraps(async_func)
111
- def wrapper(*args, **kwargs) -> Union[T, dict]:
112
- try:
113
- # Create the coroutine
114
- coro = async_func(*args, **kwargs)
115
-
116
- # Run it with timeout
117
- return AsyncRunner.run_async(coro)
118
-
119
- except Exception as e:
120
- logger.error(f"❌ Async to sync conversion failed: {e}", exc_info=True)
121
- return {
122
- "error": str(e),
123
- "status": "failed",
124
- "boundary_context": "OSS advisory only - execution requires Enterprise",
125
- "error_type": type(e).__name__
126
- }
127
- return wrapper
128
-
129
- @staticmethod
130
- def is_async_context() -> bool:
131
- """
132
- Check if we're currently in an async context
133
-
134
- Returns:
135
- True if in async context, False otherwise
136
- """
137
- try:
138
- asyncio.get_running_loop()
139
- return True
140
- except RuntimeError:
141
- return False
142
-
143
- # Convenience function for the decorator
144
- def async_to_sync(async_func: Callable[..., Coroutine[Any, Any, T]]) -> Callable[..., Union[T, dict]]:
145
- """
146
- Convenience decorator to convert async function to sync
147
-
148
- Usage:
149
- @async_to_sync
150
- async def my_async_function():
151
- ...
152
-
153
- # Can now be called synchronously
154
- result = my_async_function()
155
- """
156
- return AsyncRunner.async_to_sync(async_func)
157
-
158
-
159
- @contextmanager
160
- def safe_event_loop():
161
- """
162
- Context manager for safe event loop handling with cleanup
163
-
164
- Usage:
165
- with safe_event_loop() as loop:
166
- result = loop.run_until_complete(async_function())
167
- """
168
- loop = None
169
- try:
170
- # Try to get existing loop
171
- try:
172
- loop = asyncio.get_running_loop()
173
- logger.debug("Using existing event loop")
174
- yield loop
175
- return
176
- except RuntimeError:
177
- pass
178
-
179
- # Create new loop
180
- loop = asyncio.new_event_loop()
181
- asyncio.set_event_loop(loop)
182
- logger.debug("Created new event loop")
183
- yield loop
184
-
185
- finally:
186
- # Cleanup
187
- if loop and not loop.is_closed():
188
- loop.close()
189
- logger.debug("Closed event loop")
190
-
191
-
192
- class AsyncCircuitBreaker:
193
- """
194
- Circuit breaker pattern for async operations to prevent cascading failures
195
- """
196
-
197
- def __init__(self, failure_threshold: int = 5, recovery_timeout: float = 30.0):
198
- self.failure_threshold = failure_threshold
199
- self.recovery_timeout = recovery_timeout
200
- self.failure_count = 0
201
- self.last_failure_time = 0
202
- self.state = "CLOSED" # CLOSED, OPEN, HALF_OPEN
203
-
204
- def can_execute(self) -> bool:
205
- """Check if circuit breaker allows execution"""
206
- if self.state == "OPEN":
207
- # Check if recovery timeout has passed
208
- if time.time() - self.last_failure_time > self.recovery_timeout:
209
- self.state = "HALF_OPEN"
210
- logger.info("Circuit breaker moving to HALF_OPEN state")
211
- return True
212
- return False
213
- return True
214
-
215
- def record_success(self):
216
- """Record successful execution"""
217
- if self.state == "HALF_OPEN":
218
- self.state = "CLOSED"
219
- logger.info("Circuit breaker reset to CLOSED state")
220
- self.failure_count = 0
221
-
222
- def record_failure(self):
223
- """Record failed execution"""
224
- self.failure_count += 1
225
- self.last_failure_time = time.time()
226
-
227
- if self.failure_count >= self.failure_threshold:
228
- self.state = "OPEN"
229
- logger.warning(f"Circuit breaker OPENED after {self.failure_count} failures")
230
-
231
- @AsyncRunner.async_to_sync
232
- async def execute(self, coro: Coroutine) -> Any:
233
- """Execute async operation with circuit breaker protection"""
234
- if not self.can_execute():
235
- raise Exception("Circuit breaker is OPEN - operation blocked")
236
-
237
- try:
238
- result = await coro
239
- self.record_success()
240
- return result
241
- except Exception as e:
242
- self.record_failure()
243
- raise e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
utils/installation.py DELETED
@@ -1,195 +0,0 @@
1
- """
2
- Installation helper utilities for ARF Demo - FIXED VERSION
3
- Updated to v3.3.9 (actual PyPI version) - SURGICAL FIX
4
- """
5
- import subprocess
6
- import sys
7
- import logging
8
- from typing import Dict, Any, List, Optional
9
-
10
- logger = logging.getLogger(__name__)
11
-
12
-
13
- class InstallationHelper:
14
- """Helper class for ARF package installation - FIXED: No attempt to import non-existent packages"""
15
-
16
- @staticmethod
17
- def install_arf_oss() -> Dict[str, Any]:
18
- """Install ARF OSS package - FIXED: Updated to v3.3.9"""
19
- try:
20
- logger.info("Installing ARF OSS v3.3.9...") # FIXED: v3.3.7 → v3.3.9
21
- result = subprocess.run(
22
- [sys.executable, "-m", "pip", "install", "agentic-reliability-framework==3.3.9"], # FIXED: v3.3.9
23
- capture_output=True,
24
- text=True,
25
- check=True
26
- )
27
-
28
- return {
29
- "success": True,
30
- "message": "✅ ARF OSS v3.3.9 installed successfully", # FIXED: v3.3.9
31
- "output": result.stdout
32
- }
33
-
34
- except subprocess.CalledProcessError as e:
35
- return {
36
- "success": False,
37
- "message": "❌ Failed to install ARF OSS",
38
- "error": e.stderr
39
- }
40
- except Exception as e:
41
- return {
42
- "success": False,
43
- "message": f"❌ Installation error: {str(e)}",
44
- "error": str(e)
45
- }
46
-
47
- @staticmethod
48
- def check_installation() -> Dict[str, Any]:
49
- """
50
- FIXED: Check ARF package installation - no attempt to import non-existent arf_enterprise
51
- Enterprise is always simulated in this demo
52
- """
53
- try:
54
- import agentic_reliability_framework as arf_oss
55
- oss_version = getattr(arf_oss, '__version__', 'unknown')
56
- oss_installed = True
57
- logger.info(f"✅ ARF OSS v{oss_version} detected")
58
- except ImportError:
59
- oss_installed = False
60
- oss_version = None
61
- logger.info("⚠️ ARF OSS not installed - using mock mode")
62
-
63
- # FIXED: Enterprise is ALWAYS simulated - package doesn't exist
64
- enterprise_installed = False
65
- enterprise_version = "simulated"
66
- logger.info("⚠️ ARF Enterprise not installed - using simulation")
67
-
68
- return {
69
- "oss_installed": oss_installed,
70
- "oss_version": oss_version,
71
- "enterprise_installed": enterprise_installed, # Always false
72
- "enterprise_version": enterprise_version, # Always "simulated"
73
- "recommendations": InstallationHelper.get_recommendations(oss_installed, enterprise_installed),
74
- "boundaries": {
75
- "oss_can": ["advisory_analysis", "rag_search", "healing_intent"],
76
- "oss_cannot": ["execute", "modify_infra", "autonomous_healing"],
77
- "enterprise_simulated": True,
78
- "enterprise_requires_license": True,
79
- "architecture": "OSS advises → Enterprise simulates"
80
- }
81
- }
82
-
83
- @staticmethod
84
- def get_recommendations(oss_installed: bool, enterprise_installed: bool) -> List[str]:
85
- """
86
- FIXED: Get realistic installation recommendations
87
- Enterprise package doesn't exist publicly
88
- """
89
- recommendations = []
90
-
91
- if not oss_installed:
92
- recommendations.append(
93
- "Install ARF OSS: `pip install agentic-reliability-framework==3.3.9`" # FIXED: v3.3.9
94
- )
95
- recommendations.append(
96
- "💡 Without OSS, demo runs in mock mode with simulated analysis"
97
- )
98
-
99
- # FIXED: Enterprise is simulated, not installable
100
- recommendations.append(
101
- "🔒 ARF Enterprise requires commercial license - simulated in this demo"
102
- )
103
- recommendations.append(
104
- "🏢 Contact sales@arf.dev for Enterprise trial access"
105
- )
106
-
107
- return recommendations
108
-
109
- @staticmethod
110
- def get_installation_html() -> str:
111
- """Get HTML for installation status display - FIXED to show clear boundaries"""
112
- status = InstallationHelper.check_installation()
113
-
114
- if status["oss_installed"]:
115
- oss_html = f"""
116
- <div style="padding: 10px; background: linear-gradient(135deg, #10b981 0%, #0ca678 100%);
117
- color: white; border-radius: 8px; margin: 5px 0; border: 1px solid #0da271;">
118
- <div style="display: flex; align-items: center; gap: 8px;">
119
- <span style="font-size: 16px;">✅</span>
120
- <div>
121
- <div style="font-weight: 600;">ARF OSS v{status['oss_version']}</div>
122
- <div style="font-size: 12px; opacity: 0.9;">Apache 2.0 • Advisory Intelligence</div>
123
- </div>
124
- </div>
125
- </div>
126
- """
127
- else:
128
- oss_html = """
129
- <div style="padding: 10px; background: linear-gradient(135deg, #f59e0b 0%, #e0950a 100%);
130
- color: white; border-radius: 8px; margin: 5px 0; border: 1px solid #d98a09;">
131
- <div style="display: flex; align-items: center; gap: 8px;">
132
- <span style="font-size: 16px;">⚠️</span>
133
- <div>
134
- <div style="font-weight: 600;">Mock ARF</div>
135
- <div style="font-size: 12px; opacity: 0.9;">Simulated analysis only</div>
136
- </div>
137
- </div>
138
- </div>
139
- """
140
-
141
- # FIXED: Enterprise is always simulated
142
- enterprise_html = f"""
143
- <div style="padding: 10px; background: linear-gradient(135deg, #64748b 0%, #4b5563 100%);
144
- color: white; border-radius: 8px; margin: 5px 0; border: 1px dashed #9ca3af;">
145
- <div style="display: flex; align-items: center; gap: 8px;">
146
- <span style="font-size: 16px;">🔒</span>
147
- <div>
148
- <div style="font-weight: 600;">Enterprise Simulation</div>
149
- <div style="font-size: 12px; opacity: 0.9;">v{status['enterprise_version']} • Commercial License Required</div>
150
- </div>
151
- </div>
152
- </div>
153
- """
154
-
155
- # FIXED: Realistic recommendations
156
- recommendations_html = ""
157
- if status["recommendations"]:
158
- rec_list = "\n".join([f"<li style='margin-bottom: 5px;'>{rec}</li>" for rec in status["recommendations"][:3]])
159
- recommendations_html = f"""
160
- <div style="margin-top: 15px; padding: 12px; background: #f8fafc; border-radius: 8px; border: 1px solid #e2e8f0;">
161
- <div style="font-weight: 600; color: #475569; margin-bottom: 8px; display: flex; align-items: center; gap: 6px;">
162
- <span>💡</span> <span>Architecture & Recommendations</span>
163
- </div>
164
- <ul style="margin: 5px 0 0 0; padding-left: 20px; color: #64748b; font-size: 13px;">
165
- {rec_list}
166
- </ul>
167
- </div>
168
- """
169
-
170
- # FIXED: Add boundary context
171
- boundary_html = """
172
- <div style="margin-top: 10px; padding: 8px; background: #f0fdf4; border-radius: 6px; border: 1px solid #d1fae5;">
173
- <div style="font-size: 12px; color: #059669; display: flex; align-items: center; gap: 6px;">
174
- <span style="display: inline-block; width: 8px; height: 8px; background: #10b981; border-radius: 50%;"></span>
175
- <span>Boundary: OSS advises → Enterprise simulates</span>
176
- </div>
177
- </div>
178
- """
179
-
180
- return f"""
181
- <div style="border: 2px solid #e2e8f0; border-radius: 12px; padding: 15px; background: white;
182
- box-shadow: 0 1px 3px rgba(0,0,0,0.05);">
183
- <h4 style="margin: 0 0 12px 0; color: #1e293b; font-size: 16px; font-weight: 600;">
184
- 📦 ARF Installation & Boundaries
185
- </h4>
186
- {oss_html}
187
- {enterprise_html}
188
- {boundary_html}
189
- {recommendations_html}
190
- </div>
191
- """
192
-
193
-
194
- # Export singleton
195
- installation_helper = InstallationHelper()