gitarsenal-cli 1.9.26 ā 1.9.27
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.
- package/.venv_status.json +1 -1
- package/package.json +1 -1
- package/python/__pycache__/fetch_modal_tokens.cpython-313.pyc +0 -0
- package/python/command_manager.py +31 -48
- package/python/credentials_manager.py +45 -2
- package/python/fetch_modal_tokens.py +45 -31
- package/python/fix_modal_token.py +1 -1
- package/python/llm_debugging.py +77 -9
- package/python/modal_container.py +103 -7
- package/python/requirements.txt +2 -1
- package/python/setup.py +2 -1
- package/python/test_modalSandboxScript.py +106 -64
package/.venv_status.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"created":"2025-08-
|
|
1
|
+
{"created":"2025-08-08T04:25:52.914Z","packages":["modal","gitingest","requests","anthropic"],"uv_version":"uv 0.8.4 (Homebrew 2025-07-30)"}
|
package/package.json
CHANGED
|
Binary file
|
|
@@ -255,7 +255,7 @@ class CommandListManager:
|
|
|
255
255
|
|
|
256
256
|
return all_commands
|
|
257
257
|
|
|
258
|
-
def analyze_failed_commands_with_llm(self, api_key=None, current_dir=None, sandbox=None):
|
|
258
|
+
def analyze_failed_commands_with_llm(self, api_key=None, current_dir=None, sandbox=None, use_web_search=False):
|
|
259
259
|
"""Analyze all failed commands using LLM and add suggested fixes."""
|
|
260
260
|
failed_commands = self.get_failed_commands_for_llm()
|
|
261
261
|
|
|
@@ -266,7 +266,7 @@ class CommandListManager:
|
|
|
266
266
|
print(f"š Analyzing {len(failed_commands)} failed commands with LLM...")
|
|
267
267
|
|
|
268
268
|
# Use unified batch debugging for efficiency
|
|
269
|
-
fixes = call_llm_for_batch_debug(failed_commands, api_key, current_dir, sandbox)
|
|
269
|
+
fixes = call_llm_for_batch_debug(failed_commands, api_key, current_dir, sandbox, use_web_search)
|
|
270
270
|
|
|
271
271
|
# Add the fixes to the command list
|
|
272
272
|
added_fixes = []
|
|
@@ -296,18 +296,17 @@ class CommandListManager:
|
|
|
296
296
|
tuple: (should_skip, reason)
|
|
297
297
|
"""
|
|
298
298
|
try:
|
|
299
|
+
# Import required helpers once for this function scope
|
|
300
|
+
from llm_debugging import get_current_debug_model, get_api_key, make_api_request
|
|
301
|
+
|
|
299
302
|
# Get API key if not provided
|
|
300
303
|
if not api_key:
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
key_file = os.path.expanduser("~/.gitarsenal/openai_key")
|
|
305
|
-
if os.path.exists(key_file):
|
|
306
|
-
with open(key_file, "r") as f:
|
|
307
|
-
api_key = f.read().strip()
|
|
304
|
+
# Use the same API key retrieval logic as the debugging functions
|
|
305
|
+
current_model = get_current_debug_model()
|
|
306
|
+
api_key = get_api_key(current_model)
|
|
308
307
|
|
|
309
308
|
if not api_key:
|
|
310
|
-
print("ā ļø No
|
|
309
|
+
print(f"ā ļø No {current_model} API key available for command list analysis")
|
|
311
310
|
return False, "No API key available"
|
|
312
311
|
|
|
313
312
|
# Get all commands for context
|
|
@@ -342,23 +341,15 @@ class CommandListManager:
|
|
|
342
341
|
RUN: <reason>
|
|
343
342
|
"""
|
|
344
343
|
|
|
345
|
-
|
|
346
|
-
import openai
|
|
347
|
-
client = openai.OpenAI(api_key=api_key)
|
|
344
|
+
current_model = get_current_debug_model()
|
|
348
345
|
|
|
349
|
-
print("š Analyzing if original command should be skipped...")
|
|
346
|
+
print(f"š Analyzing if original command should be skipped using {current_model}...")
|
|
350
347
|
|
|
351
|
-
|
|
352
|
-
model="gpt-3.5-turbo",
|
|
353
|
-
messages=[
|
|
354
|
-
{"role": "system", "content": "You are a helpful assistant that analyzes command execution."},
|
|
355
|
-
{"role": "user", "content": prompt}
|
|
356
|
-
],
|
|
357
|
-
max_tokens=100,
|
|
358
|
-
temperature=0.3
|
|
359
|
-
)
|
|
348
|
+
response_text = make_api_request(current_model, api_key, prompt)
|
|
360
349
|
|
|
361
|
-
|
|
350
|
+
if not response_text:
|
|
351
|
+
print(f"ā ļø Failed to get response from {current_model}")
|
|
352
|
+
return False, f"Failed to get response from {current_model}"
|
|
362
353
|
|
|
363
354
|
# Parse the response
|
|
364
355
|
if response_text.startswith("SKIP:"):
|
|
@@ -421,18 +412,16 @@ class CommandListManager:
|
|
|
421
412
|
bool: True if the list was updated, False otherwise
|
|
422
413
|
"""
|
|
423
414
|
try:
|
|
415
|
+
from llm_debugging import get_current_debug_model, get_api_key, make_api_request
|
|
424
416
|
# Get API key if not provided
|
|
425
417
|
if not api_key:
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
if os.path.exists(key_file):
|
|
431
|
-
with open(key_file, "r") as f:
|
|
432
|
-
api_key = f.read().strip()
|
|
418
|
+
# Use the same API key retrieval logic as the debugging functions
|
|
419
|
+
from llm_debugging import get_current_debug_model, get_api_key
|
|
420
|
+
current_model = get_current_debug_model()
|
|
421
|
+
api_key = get_api_key(current_model)
|
|
433
422
|
|
|
434
423
|
if not api_key:
|
|
435
|
-
print("ā ļø No
|
|
424
|
+
print(f"ā ļø No {current_model} API key available for command list analysis")
|
|
436
425
|
return False
|
|
437
426
|
|
|
438
427
|
# Get all commands for context
|
|
@@ -486,24 +475,18 @@ class CommandListManager:
|
|
|
486
475
|
Only include commands that need changes (SKIP, MODIFY, ADD_AFTER), not KEEP actions.
|
|
487
476
|
"""
|
|
488
477
|
|
|
489
|
-
#
|
|
490
|
-
import
|
|
478
|
+
# Use the unified LLM API call
|
|
479
|
+
from llm_debugging import make_api_request
|
|
491
480
|
import json
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
print("š Analyzing command list for optimizations...")
|
|
495
|
-
|
|
496
|
-
response = client.chat.completions.create(
|
|
497
|
-
model="gpt-4o-mini", # Use a more capable model for this complex task
|
|
498
|
-
messages=[
|
|
499
|
-
{"role": "system", "content": "You are a helpful assistant that analyzes and optimizes command lists."},
|
|
500
|
-
{"role": "user", "content": prompt}
|
|
501
|
-
],
|
|
502
|
-
max_tokens=1000,
|
|
503
|
-
temperature=0.2
|
|
504
|
-
)
|
|
481
|
+
current_model = get_current_debug_model()
|
|
505
482
|
|
|
506
|
-
|
|
483
|
+
print(f"š Analyzing command list for optimizations using {current_model}...")
|
|
484
|
+
|
|
485
|
+
response_text = make_api_request(current_model, api_key, prompt)
|
|
486
|
+
|
|
487
|
+
if not response_text:
|
|
488
|
+
print(f"ā ļø Failed to get response from {current_model}")
|
|
489
|
+
return False
|
|
507
490
|
|
|
508
491
|
# Extract JSON from the response
|
|
509
492
|
try:
|
|
@@ -145,7 +145,7 @@ class CredentialsManager:
|
|
|
145
145
|
# First try to fetch from server using fetch_modal_tokens (GitArsenal's key)
|
|
146
146
|
try:
|
|
147
147
|
from fetch_modal_tokens import get_tokens
|
|
148
|
-
_, _, api_key, _ = get_tokens()
|
|
148
|
+
_, _, api_key, _, _ = get_tokens()
|
|
149
149
|
if api_key and validate_openai_key(api_key):
|
|
150
150
|
# Set in environment for future use
|
|
151
151
|
os.environ["OPENAI_API_KEY"] = api_key
|
|
@@ -240,6 +240,48 @@ class CredentialsManager:
|
|
|
240
240
|
|
|
241
241
|
prompt = "An Anthropic API key is required.\nYou can get your API key from: https://console.anthropic.com/"
|
|
242
242
|
return self.get_credential("anthropic_api_key", prompt, is_password=True, validate_func=validate_anthropic_key)
|
|
243
|
+
|
|
244
|
+
def get_groq_api_key(self):
|
|
245
|
+
"""Get Groq API key with validation"""
|
|
246
|
+
def validate_groq_key(key):
|
|
247
|
+
# Groq keys are non-empty; basic length check
|
|
248
|
+
return bool(key) and len(key) > 20
|
|
249
|
+
|
|
250
|
+
# First check stored credentials
|
|
251
|
+
credentials = self.load_credentials()
|
|
252
|
+
if "groq_api_key" in credentials:
|
|
253
|
+
stored_key = credentials["groq_api_key"]
|
|
254
|
+
if validate_groq_key(stored_key):
|
|
255
|
+
return stored_key
|
|
256
|
+
|
|
257
|
+
# Then check environment variable
|
|
258
|
+
env_key = os.environ.get("GROQ_API_KEY")
|
|
259
|
+
if env_key and validate_groq_key(env_key):
|
|
260
|
+
return env_key
|
|
261
|
+
|
|
262
|
+
prompt = "A Groq API key is required for Groq models.\nYou can get your key from: https://console.groq.com/keys"
|
|
263
|
+
return self.get_credential("groq_api_key", prompt, is_password=True, validate_func=validate_groq_key)
|
|
264
|
+
|
|
265
|
+
def get_exa_api_key(self):
|
|
266
|
+
"""Get Exa API key with validation"""
|
|
267
|
+
def validate_exa_key(key):
|
|
268
|
+
# Exa API keys are typically 32+ characters
|
|
269
|
+
return len(key) >= 32
|
|
270
|
+
|
|
271
|
+
# First check stored credentials
|
|
272
|
+
credentials = self.load_credentials()
|
|
273
|
+
if "exa_api_key" in credentials:
|
|
274
|
+
stored_key = credentials["exa_api_key"]
|
|
275
|
+
if validate_exa_key(stored_key):
|
|
276
|
+
return stored_key
|
|
277
|
+
|
|
278
|
+
# Then check environment variable
|
|
279
|
+
env_key = os.environ.get("EXA_API_KEY")
|
|
280
|
+
if env_key and validate_exa_key(env_key):
|
|
281
|
+
return env_key
|
|
282
|
+
|
|
283
|
+
prompt = "An Exa API key is required for web search functionality.\nYou can get your API key from: https://exa.ai/"
|
|
284
|
+
return self.get_credential("exa_api_key", prompt, is_password=True, validate_func=validate_exa_key)
|
|
243
285
|
|
|
244
286
|
def clear_credential(self, key):
|
|
245
287
|
"""Remove a specific credential"""
|
|
@@ -285,7 +327,8 @@ class CredentialsManager:
|
|
|
285
327
|
"WANDB_API_KEY",
|
|
286
328
|
"MODAL_TOKEN_ID",
|
|
287
329
|
"MODAL_TOKEN",
|
|
288
|
-
"MODAL_TOKEN_SECRET"
|
|
330
|
+
"MODAL_TOKEN_SECRET",
|
|
331
|
+
"GROQ_API_KEY"
|
|
289
332
|
]
|
|
290
333
|
|
|
291
334
|
for var in security_vars:
|
|
@@ -17,7 +17,7 @@ def fetch_default_tokens_from_gitarsenal():
|
|
|
17
17
|
Fetch default Modal tokens and OpenAI API key from gitarsenal.dev API.
|
|
18
18
|
|
|
19
19
|
Returns:
|
|
20
|
-
tuple: (token_id, token_secret, openai_api_key) if successful, (None, None, None) otherwise
|
|
20
|
+
tuple: (token_id, token_secret, openai_api_key, anthropic_api_key, groq_api_key) if successful, (None, None, None, None, None) otherwise
|
|
21
21
|
"""
|
|
22
22
|
endpoint = "https://gitarsenal.dev/api/credentials"
|
|
23
23
|
|
|
@@ -40,29 +40,30 @@ def fetch_default_tokens_from_gitarsenal():
|
|
|
40
40
|
token_secret = data.get("modalTokenSecret")
|
|
41
41
|
openai_api_key = data.get("openaiApiKey")
|
|
42
42
|
anthropic_api_key = data.get("anthropicApiKey")
|
|
43
|
+
groq_api_key = data.get("groqApiKey")
|
|
43
44
|
|
|
44
45
|
if token_id and token_secret:
|
|
45
46
|
# print("ā
Successfully fetched default tokens from gitarsenal.dev")
|
|
46
|
-
return token_id, token_secret, openai_api_key, anthropic_api_key
|
|
47
|
+
return token_id, token_secret, openai_api_key, anthropic_api_key, groq_api_key
|
|
47
48
|
else:
|
|
48
49
|
print("ā Modal tokens not found in gitarsenal.dev response")
|
|
49
|
-
return None, None, None, None
|
|
50
|
+
return None, None, None, None, None
|
|
50
51
|
except json.JSONDecodeError:
|
|
51
52
|
print("ā Invalid JSON response from gitarsenal.dev")
|
|
52
|
-
return None, None, None, None
|
|
53
|
+
return None, None, None, None, None
|
|
53
54
|
else:
|
|
54
55
|
print(f"ā Failed to fetch from gitarsenal.dev: {response.status_code} - {response.text[:200]}")
|
|
55
|
-
return None, None, None, None
|
|
56
|
+
return None, None, None, None, None
|
|
56
57
|
|
|
57
58
|
except requests.exceptions.Timeout:
|
|
58
59
|
print("ā Request timeout when fetching from gitarsenal.dev")
|
|
59
|
-
return None, None, None, None
|
|
60
|
+
return None, None, None, None, None
|
|
60
61
|
except requests.exceptions.ConnectionError:
|
|
61
62
|
print("ā Connection failed to gitarsenal.dev")
|
|
62
|
-
return None, None, None, None
|
|
63
|
+
return None, None, None, None, None
|
|
63
64
|
except requests.exceptions.RequestException as e:
|
|
64
65
|
print(f"ā Request failed to gitarsenal.dev: {e}")
|
|
65
|
-
return None, None, None, None
|
|
66
|
+
return None, None, None, None, None
|
|
66
67
|
|
|
67
68
|
def fetch_tokens_from_proxy(proxy_url=None, api_key=None):
|
|
68
69
|
"""
|
|
@@ -73,7 +74,7 @@ def fetch_tokens_from_proxy(proxy_url=None, api_key=None):
|
|
|
73
74
|
api_key: API key for authentication
|
|
74
75
|
|
|
75
76
|
Returns:
|
|
76
|
-
tuple: (token_id, token_secret, openai_api_key) if successful, (None, None, None) otherwise
|
|
77
|
+
tuple: (token_id, token_secret, openai_api_key, anthropic_api_key, groq_api_key) if successful, (None, None, None, None, None) otherwise
|
|
77
78
|
"""
|
|
78
79
|
# Use environment variables if not provided
|
|
79
80
|
if not proxy_url:
|
|
@@ -90,12 +91,12 @@ def fetch_tokens_from_proxy(proxy_url=None, api_key=None):
|
|
|
90
91
|
if not proxy_url:
|
|
91
92
|
# print("ā No proxy URL provided or found in environment")
|
|
92
93
|
print("š” Set MODAL_PROXY_URL environment variable or use --proxy-url argument")
|
|
93
|
-
return None, None, None, None
|
|
94
|
+
return None, None, None, None, None
|
|
94
95
|
|
|
95
96
|
if not api_key:
|
|
96
97
|
print("ā No API key provided or found in environment")
|
|
97
98
|
print("š” Set MODAL_PROXY_API_KEY environment variable or use --proxy-api-key argument")
|
|
98
|
-
return None, None, None, None
|
|
99
|
+
return None, None, None, None, None
|
|
99
100
|
|
|
100
101
|
# Ensure the URL ends with a slash
|
|
101
102
|
if not proxy_url.endswith("/"):
|
|
@@ -119,48 +120,41 @@ def fetch_tokens_from_proxy(proxy_url=None, api_key=None):
|
|
|
119
120
|
token_secret = data.get("token_secret")
|
|
120
121
|
openai_api_key = data.get("openai_api_key")
|
|
121
122
|
anthropic_api_key = data.get("anthropic_api_key")
|
|
122
|
-
|
|
123
|
+
groq_api_key = data.get("groq_api_key")
|
|
123
124
|
if token_id and token_secret:
|
|
124
125
|
print("ā
Successfully fetched tokens from proxy server")
|
|
125
|
-
return token_id, token_secret, openai_api_key, anthropic_api_key
|
|
126
|
+
return token_id, token_secret, openai_api_key, anthropic_api_key, groq_api_key
|
|
126
127
|
else:
|
|
127
128
|
print("ā Tokens not found in response")
|
|
128
|
-
return None, None, None, None
|
|
129
|
+
return None, None, None, None, None
|
|
129
130
|
else:
|
|
130
131
|
print(f"ā Failed to fetch tokens: {response.status_code} - {response.text}")
|
|
131
|
-
return None, None, None, None
|
|
132
|
+
return None, None, None, None, None
|
|
132
133
|
except Exception as e:
|
|
133
134
|
print(f"ā Error fetching tokens: {e}")
|
|
134
|
-
return None, None, None, None
|
|
135
|
+
return None, None, None, None, None
|
|
135
136
|
|
|
136
137
|
def get_tokens():
|
|
137
138
|
"""
|
|
138
|
-
Get Modal tokens, OpenAI API key, and
|
|
139
|
+
Get Modal tokens, OpenAI API key, Anthropic API key, and Groq API key, trying to fetch from the proxy server first.
|
|
139
140
|
Also sets the tokens in environment variables.
|
|
140
141
|
|
|
141
142
|
Returns:
|
|
142
|
-
tuple: (token_id, token_secret, openai_api_key, anthropic_api_key)
|
|
143
|
+
tuple: (token_id, token_secret, openai_api_key, anthropic_api_key, groq_api_key)
|
|
143
144
|
"""
|
|
144
145
|
# Try to fetch from the proxy server
|
|
145
|
-
token_id, token_secret, openai_api_key, anthropic_api_key = fetch_tokens_from_proxy()
|
|
146
|
+
token_id, token_secret, openai_api_key, anthropic_api_key, groq_api_key = fetch_tokens_from_proxy()
|
|
146
147
|
|
|
147
148
|
# If we couldn't fetch from the server, try to get default tokens from gitarsenal.dev
|
|
148
149
|
if not token_id or not token_secret:
|
|
149
150
|
# print("ā ļø Proxy server failed, trying to fetch default tokens from gitarsenal.dev")
|
|
150
|
-
token_id, token_secret, openai_api_key, anthropic_api_key = fetch_default_tokens_from_gitarsenal()
|
|
151
|
+
token_id, token_secret, openai_api_key, anthropic_api_key, groq_api_key = fetch_default_tokens_from_gitarsenal()
|
|
151
152
|
|
|
152
153
|
# If we still don't have tokens, we can't proceed
|
|
153
154
|
if not token_id or not token_secret:
|
|
154
155
|
print("ā Failed to fetch tokens from both proxy server and gitarsenal.dev")
|
|
155
156
|
print("š” Please check your network connection and API endpoints")
|
|
156
|
-
return None, None, None, None
|
|
157
|
-
|
|
158
|
-
# Debug print the full token values
|
|
159
|
-
# print("\nš DEBUG: FULL TOKEN VALUES:")
|
|
160
|
-
# print(f"š DEBUG: MODAL_TOKEN_ID: {token_id}")
|
|
161
|
-
# print(f"š DEBUG: MODAL_TOKEN_SECRET: {token_secret}")
|
|
162
|
-
# print(f"š DEBUG: OPENAI_API_KEY: {openai_api_key}")
|
|
163
|
-
# print("š DEBUG: END OF TOKEN VALUES\n")
|
|
157
|
+
return None, None, None, None, None
|
|
164
158
|
|
|
165
159
|
# Set the tokens in environment variables
|
|
166
160
|
os.environ["MODAL_TOKEN_ID"] = token_id
|
|
@@ -175,7 +169,11 @@ def get_tokens():
|
|
|
175
169
|
if anthropic_api_key:
|
|
176
170
|
os.environ["ANTHROPIC_API_KEY"] = anthropic_api_key
|
|
177
171
|
|
|
178
|
-
|
|
172
|
+
# Set Groq API key if available
|
|
173
|
+
if groq_api_key:
|
|
174
|
+
os.environ["GROQ_API_KEY"] = groq_api_key
|
|
175
|
+
|
|
176
|
+
return token_id, token_secret, openai_api_key, anthropic_api_key, groq_api_key
|
|
179
177
|
|
|
180
178
|
if __name__ == "__main__":
|
|
181
179
|
# Parse command-line arguments if run directly
|
|
@@ -196,11 +194,12 @@ if __name__ == "__main__":
|
|
|
196
194
|
print(f"ā
Set MODAL_PROXY_API_KEY from command line")
|
|
197
195
|
|
|
198
196
|
# Get tokens
|
|
199
|
-
token_id, token_secret, openai_api_key, anthropic_api_key = get_tokens()
|
|
197
|
+
token_id, token_secret, openai_api_key, anthropic_api_key, groq_api_key = get_tokens()
|
|
200
198
|
print(f"Token ID: {token_id}")
|
|
201
199
|
print(f"Token Secret: {token_secret}")
|
|
202
200
|
print(f"OpenAI API Key: {openai_api_key[:5] + '...' if openai_api_key else None}")
|
|
203
201
|
print(f"Anthropic API Key: {anthropic_api_key[:5] + '...' if anthropic_api_key else None}")
|
|
202
|
+
print(f"Groq API Key: {groq_api_key[:5] + '...' if groq_api_key else None}")
|
|
204
203
|
|
|
205
204
|
# Check if tokens are set in environment variables
|
|
206
205
|
print(f"\nš DEBUG: Checking environment variables")
|
|
@@ -208,6 +207,7 @@ if __name__ == "__main__":
|
|
|
208
207
|
print(f"š MODAL_TOKEN_SECRET exists: {'Yes' if os.environ.get('MODAL_TOKEN_SECRET') else 'No'}")
|
|
209
208
|
print(f"š OPENAI_API_KEY exists: {'Yes' if os.environ.get('OPENAI_API_KEY') else 'No'}")
|
|
210
209
|
print(f"š ANTHROPIC_API_KEY exists: {'Yes' if os.environ.get('ANTHROPIC_API_KEY') else 'No'}")
|
|
210
|
+
print(f"š GROQ_API_KEY exists: {'Yes' if os.environ.get('GROQ_API_KEY') else 'No'}")
|
|
211
211
|
if os.environ.get('MODAL_TOKEN_ID'):
|
|
212
212
|
print(f"š MODAL_TOKEN_ID length: {len(os.environ.get('MODAL_TOKEN_ID'))}")
|
|
213
213
|
if os.environ.get('MODAL_TOKEN_SECRET'):
|
|
@@ -216,6 +216,8 @@ if __name__ == "__main__":
|
|
|
216
216
|
print(f"š OPENAI_API_KEY length: {len(os.environ.get('OPENAI_API_KEY'))}")
|
|
217
217
|
if os.environ.get('ANTHROPIC_API_KEY'):
|
|
218
218
|
print(f"š ANTHROPIC_API_KEY length: {len(os.environ.get('ANTHROPIC_API_KEY'))}")
|
|
219
|
+
if os.environ.get('GROQ_API_KEY'):
|
|
220
|
+
print(f"š GROQ_API_KEY length: {len(os.environ.get('GROQ_API_KEY'))}")
|
|
219
221
|
|
|
220
222
|
# Write the tokens to a file for use by other scripts
|
|
221
223
|
tokens_file = Path(__file__).parent / "modal_tokens.json"
|
|
@@ -224,7 +226,8 @@ if __name__ == "__main__":
|
|
|
224
226
|
"token_id": token_id,
|
|
225
227
|
"token_secret": token_secret,
|
|
226
228
|
"openai_api_key": openai_api_key,
|
|
227
|
-
"anthropic_api_key": anthropic_api_key
|
|
229
|
+
"anthropic_api_key": anthropic_api_key,
|
|
230
|
+
"groq_api_key": groq_api_key
|
|
228
231
|
}, f)
|
|
229
232
|
print(f"\nā
Tokens written to {tokens_file}")
|
|
230
233
|
|
|
@@ -279,6 +282,17 @@ if __name__ == "__main__":
|
|
|
279
282
|
with open(env_file, 'w') as f:
|
|
280
283
|
f.write(env_content)
|
|
281
284
|
print(f"ā
Updated Anthropic API key in {env_file}")
|
|
285
|
+
|
|
286
|
+
# Update or add GROQ_API_KEY
|
|
287
|
+
if groq_api_key:
|
|
288
|
+
if "GROQ_API_KEY" in env_content:
|
|
289
|
+
import re
|
|
290
|
+
env_content = re.sub(r'GROQ_API_KEY=.*\n', f'GROQ_API_KEY={groq_api_key}\n', env_content)
|
|
291
|
+
else:
|
|
292
|
+
env_content += f'\nGROQ_API_KEY={groq_api_key}\n'
|
|
293
|
+
with open(env_file, 'w') as f:
|
|
294
|
+
f.write(env_content)
|
|
295
|
+
print(f"ā
Updated Groq API key in {env_file}")
|
|
282
296
|
|
|
283
297
|
# Try to use the Modal CLI to set the token
|
|
284
298
|
try:
|
|
@@ -42,7 +42,7 @@ except Exception as e:
|
|
|
42
42
|
try:
|
|
43
43
|
# First, try to import the fetch_modal_tokens module
|
|
44
44
|
from fetch_modal_tokens import get_tokens
|
|
45
|
-
TOKEN_ID, TOKEN_SECRET, _, _ = get_tokens()
|
|
45
|
+
TOKEN_ID, TOKEN_SECRET, _, _, _ = get_tokens()
|
|
46
46
|
|
|
47
47
|
# Check if we got valid tokens
|
|
48
48
|
if TOKEN_ID is None or TOKEN_SECRET is None:
|
package/python/llm_debugging.py
CHANGED
|
@@ -55,19 +55,22 @@ def get_api_key(provider):
|
|
|
55
55
|
env_var_map = {
|
|
56
56
|
"openai": "OPENAI_API_KEY",
|
|
57
57
|
"anthropic": "ANTHROPIC_API_KEY",
|
|
58
|
-
"openrouter": "OPENROUTER_API_KEY"
|
|
58
|
+
"openrouter": "OPENROUTER_API_KEY",
|
|
59
|
+
"groq": "GROQ_API_KEY"
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
key_file_map = {
|
|
62
63
|
"openai": "openai_key",
|
|
63
64
|
"anthropic": "anthropic_key",
|
|
64
|
-
"openrouter": "openrouter_key"
|
|
65
|
+
"openrouter": "openrouter_key",
|
|
66
|
+
"groq": "groq_key"
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
token_index_map = {
|
|
68
70
|
"openai": 2,
|
|
69
71
|
"anthropic": 3,
|
|
70
|
-
"openrouter": 4
|
|
72
|
+
"openrouter": 4,
|
|
73
|
+
"groq": 5,
|
|
71
74
|
}
|
|
72
75
|
|
|
73
76
|
env_var = env_var_map.get(provider)
|
|
@@ -124,7 +127,8 @@ def save_api_key(provider, api_key):
|
|
|
124
127
|
key_file_map = {
|
|
125
128
|
"openai": "openai_key",
|
|
126
129
|
"anthropic": "anthropic_key",
|
|
127
|
-
"openrouter": "openrouter_key"
|
|
130
|
+
"openrouter": "openrouter_key",
|
|
131
|
+
"groq": "groq_key"
|
|
128
132
|
}
|
|
129
133
|
|
|
130
134
|
try:
|
|
@@ -329,6 +333,8 @@ def make_api_request(provider, api_key, prompt, retries=2):
|
|
|
329
333
|
return make_anthropic_request(api_key, prompt, retries)
|
|
330
334
|
elif provider == "openrouter":
|
|
331
335
|
return make_openrouter_request(api_key, prompt, retries)
|
|
336
|
+
elif provider == "groq":
|
|
337
|
+
return make_groq_request(api_key, prompt, retries)
|
|
332
338
|
else:
|
|
333
339
|
return None
|
|
334
340
|
|
|
@@ -341,7 +347,7 @@ def make_openai_request(api_key, prompt, retries=2):
|
|
|
341
347
|
}
|
|
342
348
|
|
|
343
349
|
payload = {
|
|
344
|
-
"model": os.environ.get("OPENAI_MODEL", "gpt-
|
|
350
|
+
"model": os.environ.get("OPENAI_MODEL", "gpt-5-mini"),
|
|
345
351
|
"messages": [
|
|
346
352
|
{"role": "system", "content": "You are a debugging assistant. Provide only the terminal command to fix the issue."},
|
|
347
353
|
{"role": "user", "content": prompt}
|
|
@@ -438,7 +444,7 @@ def make_openrouter_request(api_key, prompt, retries=2):
|
|
|
438
444
|
}
|
|
439
445
|
|
|
440
446
|
payload = {
|
|
441
|
-
"model": "openai/gpt-
|
|
447
|
+
"model": "openai/gpt-5-mini",
|
|
442
448
|
"max_tokens": 300,
|
|
443
449
|
"messages": [{"role": "user", "content": prompt}]
|
|
444
450
|
}
|
|
@@ -476,7 +482,59 @@ def make_openrouter_request(api_key, prompt, retries=2):
|
|
|
476
482
|
return None
|
|
477
483
|
|
|
478
484
|
|
|
479
|
-
def
|
|
485
|
+
def make_groq_request(api_key, prompt, retries=2):
|
|
486
|
+
"""Make request to Groq API (OpenAI-compatible endpoint)"""
|
|
487
|
+
headers = {
|
|
488
|
+
"Content-Type": "application/json",
|
|
489
|
+
"Authorization": f"Bearer {api_key}"
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
payload = {
|
|
493
|
+
"model": os.environ.get("GROQ_MODEL", "openai/gpt-oss-20b"),
|
|
494
|
+
"messages": [
|
|
495
|
+
{"role": "system", "content": "You are a debugging assistant. Provide only the terminal command to fix the issue."},
|
|
496
|
+
{"role": "user", "content": prompt}
|
|
497
|
+
],
|
|
498
|
+
"temperature": 0.2,
|
|
499
|
+
"max_tokens": 300
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
endpoint = os.environ.get("GROQ_BASE_URL", "https://api.groq.com/openai/v1/chat/completions")
|
|
503
|
+
|
|
504
|
+
for attempt in range(retries + 1):
|
|
505
|
+
try:
|
|
506
|
+
if attempt > 0:
|
|
507
|
+
time.sleep(1.5 * (2 ** (attempt - 1)))
|
|
508
|
+
|
|
509
|
+
response = requests.post(
|
|
510
|
+
endpoint,
|
|
511
|
+
headers=headers,
|
|
512
|
+
json=payload,
|
|
513
|
+
timeout=45
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
if response.status_code == 200:
|
|
517
|
+
result = response.json()
|
|
518
|
+
return result["choices"][0]["message"]["content"]
|
|
519
|
+
elif response.status_code == 401:
|
|
520
|
+
print("ā Invalid Groq API key")
|
|
521
|
+
return None
|
|
522
|
+
elif response.status_code in [429, 500]:
|
|
523
|
+
continue # Retry
|
|
524
|
+
else:
|
|
525
|
+
print(f"ā ļø Groq API error: {response.status_code}")
|
|
526
|
+
return None
|
|
527
|
+
|
|
528
|
+
except (requests.exceptions.Timeout, requests.exceptions.ConnectionError):
|
|
529
|
+
continue # Retry
|
|
530
|
+
except Exception as e:
|
|
531
|
+
print(f"ā ļø Groq request error: {e}")
|
|
532
|
+
return None
|
|
533
|
+
|
|
534
|
+
return None
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
def call_llm_for_debug(command, error_output, api_key=None, current_dir=None, sandbox=None, use_web_search=False):
|
|
480
538
|
"""Unified function to call LLM for debugging"""
|
|
481
539
|
# Skip debugging for test commands
|
|
482
540
|
if command.strip().startswith("test "):
|
|
@@ -531,7 +589,7 @@ def call_llm_for_debug(command, error_output, api_key=None, current_dir=None, sa
|
|
|
531
589
|
return fix_command
|
|
532
590
|
|
|
533
591
|
|
|
534
|
-
def call_llm_for_batch_debug(failed_commands, api_key=None, current_dir=None, sandbox=None):
|
|
592
|
+
def call_llm_for_batch_debug(failed_commands, api_key=None, current_dir=None, sandbox=None, use_web_search=False):
|
|
535
593
|
"""Call LLM for batch debugging of multiple failed commands"""
|
|
536
594
|
if not failed_commands:
|
|
537
595
|
return []
|
|
@@ -584,7 +642,7 @@ Guidelines:
|
|
|
584
642
|
- Keep each fix command simple and focused on the specific error
|
|
585
643
|
|
|
586
644
|
Provide fixes for all {len(failed_commands)} failed commands:"""
|
|
587
|
-
|
|
645
|
+
|
|
588
646
|
print(f"š¤ Calling {current_model} for batch debugging of {len(failed_commands)} commands...")
|
|
589
647
|
response_text = make_api_request(current_model, api_key, prompt)
|
|
590
648
|
|
|
@@ -647,4 +705,14 @@ def call_anthropic_for_batch_debug(failed_commands, api_key=None, current_dir=No
|
|
|
647
705
|
|
|
648
706
|
def call_openrouter_for_batch_debug(failed_commands, api_key=None, current_dir=None, sandbox=None):
|
|
649
707
|
"""Legacy OpenRouter batch function - now routes to unified function"""
|
|
708
|
+
return call_llm_for_batch_debug(failed_commands, api_key, current_dir, sandbox)
|
|
709
|
+
|
|
710
|
+
|
|
711
|
+
def call_groq_for_debug(command, error_output, api_key=None, current_dir=None, sandbox=None):
|
|
712
|
+
"""Legacy Groq-specific function - now routes to unified function"""
|
|
713
|
+
return call_llm_for_debug(command, error_output, api_key, current_dir, sandbox)
|
|
714
|
+
|
|
715
|
+
|
|
716
|
+
def call_groq_for_batch_debug(failed_commands, api_key=None, current_dir=None, sandbox=None):
|
|
717
|
+
"""Legacy Groq batch function - now routes to unified function"""
|
|
650
718
|
return call_llm_for_batch_debug(failed_commands, api_key, current_dir, sandbox)
|
|
@@ -408,7 +408,10 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
|
408
408
|
fix_command = call_llm_for_debug(cmd_text, stderr, current_dir=current_dir, sandbox=shell)
|
|
409
409
|
|
|
410
410
|
if fix_command:
|
|
411
|
-
|
|
411
|
+
# Get the current debug model to show the correct provider name
|
|
412
|
+
from llm_debugging import get_current_debug_model
|
|
413
|
+
current_model = get_current_debug_model()
|
|
414
|
+
print(f"š§ {current_model.capitalize()} suggested fix command: {fix_command}")
|
|
412
415
|
|
|
413
416
|
# Add the fix to the command list manager
|
|
414
417
|
fix_index = cmd_manager.add_suggested_fix(cmd_text, fix_command, "LLM suggested fix")
|
|
@@ -416,7 +419,7 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
|
416
419
|
# Execute the fix command
|
|
417
420
|
print(f"š Running suggested fix command: {fix_command}")
|
|
418
421
|
fix_start_time = time.time()
|
|
419
|
-
fix_success, fix_stdout, fix_stderr = shell.execute(fix_command, timeout=
|
|
422
|
+
fix_success, fix_stdout, fix_stderr = shell.execute(fix_command, timeout=120)
|
|
420
423
|
fix_execution_time = time.time() - fix_start_time
|
|
421
424
|
|
|
422
425
|
# Mark fix command as executed
|
|
@@ -472,9 +475,98 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
|
472
475
|
print(f"ā ļø Original command still failed after fix, continuing...")
|
|
473
476
|
else:
|
|
474
477
|
print(f"ā Fix command failed: {fix_stderr}")
|
|
475
|
-
print(f"
|
|
478
|
+
print(f"š First fix attempt failed, trying with web search...")
|
|
479
|
+
|
|
480
|
+
# Retry with web search enabled
|
|
481
|
+
try:
|
|
482
|
+
retry_fix_command = call_llm_for_debug(
|
|
483
|
+
cmd_text, stderr,
|
|
484
|
+
current_dir=current_dir,
|
|
485
|
+
sandbox=shell,
|
|
486
|
+
use_web_search=True
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
if retry_fix_command:
|
|
490
|
+
# Get the current debug model to show the correct provider name
|
|
491
|
+
from llm_debugging import get_current_debug_model
|
|
492
|
+
current_model = get_current_debug_model()
|
|
493
|
+
print(f"š§ {current_model.capitalize()} suggested web-enhanced fix: {retry_fix_command}")
|
|
494
|
+
|
|
495
|
+
# Add the web-enhanced fix to the command list manager
|
|
496
|
+
retry_fix_index = cmd_manager.add_suggested_fix(cmd_text, retry_fix_command, "Web-enhanced LLM fix")
|
|
497
|
+
|
|
498
|
+
# Execute the web-enhanced fix command
|
|
499
|
+
print(f"š Running web-enhanced fix command: {retry_fix_command}")
|
|
500
|
+
retry_fix_start_time = time.time()
|
|
501
|
+
retry_fix_success, retry_fix_stdout, retry_fix_stderr = shell.execute(retry_fix_command, timeout=120)
|
|
502
|
+
retry_fix_execution_time = time.time() - retry_fix_start_time
|
|
503
|
+
|
|
504
|
+
# Mark web-enhanced fix command as executed
|
|
505
|
+
cmd_manager.mark_command_executed(
|
|
506
|
+
retry_fix_index, 'fix', retry_fix_success, retry_fix_stdout, retry_fix_stderr, retry_fix_execution_time
|
|
507
|
+
)
|
|
508
|
+
|
|
509
|
+
if retry_fix_success:
|
|
510
|
+
print(f"ā
Web-enhanced fix command succeeded!")
|
|
511
|
+
|
|
512
|
+
# Check if we should skip the original command
|
|
513
|
+
api_key = os.environ.get("OPENAI_API_KEY")
|
|
514
|
+
should_skip, skip_reason = cmd_manager.should_skip_original_command(
|
|
515
|
+
cmd_text, retry_fix_command, retry_fix_stdout, retry_fix_stderr, api_key
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
if should_skip:
|
|
519
|
+
print(f"š Skipping original command: {skip_reason}")
|
|
520
|
+
|
|
521
|
+
# Mark the original command as successful without running it
|
|
522
|
+
cmd_manager.mark_command_executed(
|
|
523
|
+
cmd_index, 'main', True,
|
|
524
|
+
f"Command skipped after successful web-enhanced fix: {skip_reason}",
|
|
525
|
+
"", time.time() - start_time
|
|
526
|
+
)
|
|
527
|
+
|
|
528
|
+
print(f"ā
Original command marked as successful (skipped)")
|
|
529
|
+
|
|
530
|
+
# After a successful web-enhanced fix and skipping the original command,
|
|
531
|
+
# analyze and update the entire command list
|
|
532
|
+
print("\nš Analyzing and updating remaining commands based on web-enhanced fix results...")
|
|
533
|
+
cmd_manager.update_command_list_with_llm(api_key)
|
|
534
|
+
else:
|
|
535
|
+
# Retry the original command
|
|
536
|
+
print(f"š Retrying original command: {cmd_text}")
|
|
537
|
+
retry_start_time = time.time()
|
|
538
|
+
retry_success, retry_stdout, retry_stderr = shell.execute(cmd_text, timeout=300)
|
|
539
|
+
retry_execution_time = time.time() - retry_start_time
|
|
540
|
+
|
|
541
|
+
# Update the original command status
|
|
542
|
+
cmd_manager.mark_command_executed(
|
|
543
|
+
cmd_index, 'main', retry_success, retry_stdout, retry_stderr, retry_execution_time
|
|
544
|
+
)
|
|
545
|
+
|
|
546
|
+
if retry_success:
|
|
547
|
+
print(f"ā
Original command succeeded after web-enhanced fix!")
|
|
548
|
+
|
|
549
|
+
# After a successful web-enhanced fix and successful retry,
|
|
550
|
+
# analyze and update the entire command list
|
|
551
|
+
print("\nš Analyzing and updating remaining commands based on web-enhanced fix results...")
|
|
552
|
+
cmd_manager.update_command_list_with_llm(api_key)
|
|
553
|
+
else:
|
|
554
|
+
print(f"ā ļø Original command still failed after web-enhanced fix, continuing...")
|
|
555
|
+
else:
|
|
556
|
+
print(f"ā Web-enhanced fix command also failed: {retry_fix_stderr}")
|
|
557
|
+
print(f"ā ļø Continuing with remaining commands...")
|
|
558
|
+
else:
|
|
559
|
+
print(f"ā No web-enhanced fix suggested")
|
|
560
|
+
print(f"ā ļø Continuing with remaining commands...")
|
|
561
|
+
|
|
562
|
+
except Exception as web_debug_e:
|
|
563
|
+
print(f"ā Web-enhanced debugging failed: {web_debug_e}")
|
|
564
|
+
print(f"ā ļø Continuing with remaining commands...")
|
|
476
565
|
else:
|
|
477
|
-
|
|
566
|
+
# Get the current debug model to show the correct provider name
|
|
567
|
+
from llm_debugging import get_current_debug_model
|
|
568
|
+
current_model = get_current_debug_model()
|
|
569
|
+
print(f"ā No fix suggested by {current_model.capitalize()}")
|
|
478
570
|
print(f"ā ļø Continuing with remaining commands...")
|
|
479
571
|
|
|
480
572
|
except Exception as debug_e:
|
|
@@ -516,10 +608,14 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
|
516
608
|
if failed_commands:
|
|
517
609
|
print(f"\nš Final batch analysis of {len(failed_commands)} failed commands...")
|
|
518
610
|
current_dir = shell.get_cwd()
|
|
519
|
-
api_key = os.environ.get("OPENAI_API_KEY")
|
|
520
611
|
|
|
521
|
-
#
|
|
522
|
-
|
|
612
|
+
# Get the correct API key for the current debug model
|
|
613
|
+
from llm_debugging import get_current_debug_model, get_api_key
|
|
614
|
+
current_model = get_current_debug_model()
|
|
615
|
+
api_key = get_api_key(current_model)
|
|
616
|
+
|
|
617
|
+
# Use batch analysis to get additional fixes with web search enabled
|
|
618
|
+
additional_fixes = cmd_manager.analyze_failed_commands_with_llm(api_key, current_dir, shell, use_web_search=True)
|
|
523
619
|
|
|
524
620
|
if additional_fixes:
|
|
525
621
|
print(f"š§ Executing {len(additional_fixes)} additional fix commands...")
|
package/python/requirements.txt
CHANGED
package/python/setup.py
CHANGED
|
@@ -45,7 +45,7 @@ if args.proxy_api_key:
|
|
|
45
45
|
# Import the fetch_modal_tokens module
|
|
46
46
|
# print("š Fetching tokens from proxy server...")
|
|
47
47
|
from fetch_modal_tokens import get_tokens
|
|
48
|
-
token_id, token_secret, openai_api_key,
|
|
48
|
+
token_id, token_secret, openai_api_key, anthropic_api_key, groq_api_key = get_tokens()
|
|
49
49
|
|
|
50
50
|
# Check if we got valid tokens
|
|
51
51
|
if token_id is None or token_secret is None:
|
|
@@ -156,61 +156,7 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
|
156
156
|
print("ā
Found token in environment variable")
|
|
157
157
|
os.environ["MODAL_TOKEN_ID"] = modal_token
|
|
158
158
|
modal_token_id = modal_token
|
|
159
|
-
print(f"ā
Set token (length: {len(modal_token)})")
|
|
160
|
-
|
|
161
|
-
if modal_token_id:
|
|
162
|
-
print(f"ā
token found (length: {len(modal_token_id)})")
|
|
163
|
-
|
|
164
|
-
# Use the comprehensive fix_modal_token script
|
|
165
|
-
try:
|
|
166
|
-
# Execute the fix_modal_token.py script
|
|
167
|
-
import subprocess
|
|
168
|
-
print(f"š Running fix_modal_token.py to set up Modal token...")
|
|
169
|
-
result = subprocess.run(
|
|
170
|
-
["python", os.path.join(os.path.dirname(__file__), "fix_modal_token.py")],
|
|
171
|
-
capture_output=True,
|
|
172
|
-
text=True
|
|
173
|
-
)
|
|
174
|
-
|
|
175
|
-
# Print the output
|
|
176
|
-
print(result.stdout)
|
|
177
|
-
|
|
178
|
-
if result.returncode != 0:
|
|
179
|
-
print(f"ā ļø Warning: fix_modal_token.py exited with code {result.returncode}")
|
|
180
|
-
if result.stderr:
|
|
181
|
-
print(f"Error: {result.stderr}")
|
|
182
|
-
|
|
183
|
-
print(f"ā
token setup completed")
|
|
184
|
-
except Exception as e:
|
|
185
|
-
print(f"ā ļø Error running fix_modal_token.py: {e}")
|
|
186
|
-
else:
|
|
187
|
-
print("ā No token found in environment variables")
|
|
188
|
-
# Try to get from file as a last resort
|
|
189
|
-
try:
|
|
190
|
-
home_dir = os.path.expanduser("~")
|
|
191
|
-
modal_dir = os.path.join(home_dir, ".modal")
|
|
192
|
-
token_file = os.path.join(modal_dir, "token.json")
|
|
193
|
-
if os.path.exists(token_file):
|
|
194
|
-
print(f"š Found Modal token file at {token_file}")
|
|
195
|
-
with open(token_file, 'r') as f:
|
|
196
|
-
import json
|
|
197
|
-
token_data = json.load(f)
|
|
198
|
-
if "token_id" in token_data:
|
|
199
|
-
modal_token_id = token_data["token_id"]
|
|
200
|
-
os.environ["MODAL_TOKEN_ID"] = modal_token_id
|
|
201
|
-
os.environ["MODAL_TOKEN"] = modal_token_id
|
|
202
|
-
print(f"ā
Loaded token from file (length: {len(modal_token_id)})")
|
|
203
|
-
else:
|
|
204
|
-
print("ā Token file does not contain token_id")
|
|
205
|
-
else:
|
|
206
|
-
print("ā token file not found")
|
|
207
|
-
except Exception as e:
|
|
208
|
-
print(f"ā Error loading token from file: {e}")
|
|
209
|
-
|
|
210
|
-
if not os.environ.get("MODAL_TOKEN_ID"):
|
|
211
|
-
print("ā Could not find Modal token in any location")
|
|
212
|
-
return None
|
|
213
|
-
|
|
159
|
+
print(f"ā
Set token (length: {len(modal_token)})")
|
|
214
160
|
except Exception as e:
|
|
215
161
|
print(f"ā ļø Error checking Modal token: {e}")
|
|
216
162
|
# Try to use the token from environment
|
|
@@ -311,7 +257,7 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
|
311
257
|
"python3", "python3-pip", "build-essential", "tmux", "screen", "nano",
|
|
312
258
|
"gpg", "ca-certificates", "software-properties-common"
|
|
313
259
|
)
|
|
314
|
-
.uv_pip_install("uv", "modal", "requests", "openai", "anthropic") # Remove problematic CUDA packages
|
|
260
|
+
.uv_pip_install("uv", "modal", "requests", "openai", "anthropic", "exa-py") # Remove problematic CUDA packages
|
|
315
261
|
.run_commands(
|
|
316
262
|
# Create SSH directory
|
|
317
263
|
"mkdir -p /var/run/sshd",
|
|
@@ -527,7 +473,10 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
|
527
473
|
fix_command = call_llm_for_debug(cmd_text, stderr, current_dir=current_dir, sandbox=shell)
|
|
528
474
|
|
|
529
475
|
if fix_command:
|
|
530
|
-
|
|
476
|
+
# Get the current debug model to show the correct provider name
|
|
477
|
+
from llm_debugging import get_current_debug_model
|
|
478
|
+
current_model = get_current_debug_model()
|
|
479
|
+
print(f"š§ {current_model.capitalize()} suggested fix command: {fix_command}")
|
|
531
480
|
|
|
532
481
|
# Add the fix to the command list manager
|
|
533
482
|
fix_index = cmd_manager.add_suggested_fix(cmd_text, fix_command, "LLM suggested fix")
|
|
@@ -535,7 +484,7 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
|
535
484
|
# Execute the fix command
|
|
536
485
|
print(f"š Running suggested fix command: {fix_command}")
|
|
537
486
|
fix_start_time = time.time()
|
|
538
|
-
fix_success, fix_stdout, fix_stderr = shell.execute(fix_command, timeout=
|
|
487
|
+
fix_success, fix_stdout, fix_stderr = shell.execute(fix_command, timeout=120)
|
|
539
488
|
fix_execution_time = time.time() - fix_start_time
|
|
540
489
|
|
|
541
490
|
# Mark fix command as executed
|
|
@@ -591,9 +540,98 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
|
591
540
|
print(f"ā ļø Original command still failed after fix, continuing...")
|
|
592
541
|
else:
|
|
593
542
|
print(f"ā Fix command failed: {fix_stderr}")
|
|
594
|
-
print(f"
|
|
543
|
+
print(f"š First fix attempt failed, trying with web search...")
|
|
544
|
+
|
|
545
|
+
# Retry with web search enabled
|
|
546
|
+
try:
|
|
547
|
+
retry_fix_command = call_llm_for_debug(
|
|
548
|
+
cmd_text, stderr,
|
|
549
|
+
current_dir=current_dir,
|
|
550
|
+
sandbox=shell,
|
|
551
|
+
use_web_search=True
|
|
552
|
+
)
|
|
553
|
+
|
|
554
|
+
if retry_fix_command:
|
|
555
|
+
# Get the current debug model to show the correct provider name
|
|
556
|
+
from llm_debugging import get_current_debug_model
|
|
557
|
+
current_model = get_current_debug_model()
|
|
558
|
+
print(f"š§ {current_model.capitalize()} suggested web-enhanced fix: {retry_fix_command}")
|
|
559
|
+
|
|
560
|
+
# Add the web-enhanced fix to the command list manager
|
|
561
|
+
retry_fix_index = cmd_manager.add_suggested_fix(cmd_text, retry_fix_command, "Web-enhanced LLM fix")
|
|
562
|
+
|
|
563
|
+
# Execute the web-enhanced fix command
|
|
564
|
+
print(f"š Running web-enhanced fix command: {retry_fix_command}")
|
|
565
|
+
retry_fix_start_time = time.time()
|
|
566
|
+
retry_fix_success, retry_fix_stdout, retry_fix_stderr = shell.execute(retry_fix_command, timeout=120)
|
|
567
|
+
retry_fix_execution_time = time.time() - retry_fix_start_time
|
|
568
|
+
|
|
569
|
+
# Mark web-enhanced fix command as executed
|
|
570
|
+
cmd_manager.mark_command_executed(
|
|
571
|
+
retry_fix_index, 'fix', retry_fix_success, retry_fix_stdout, retry_fix_stderr, retry_fix_execution_time
|
|
572
|
+
)
|
|
573
|
+
|
|
574
|
+
if retry_fix_success:
|
|
575
|
+
print(f"ā
Web-enhanced fix command succeeded!")
|
|
576
|
+
|
|
577
|
+
# Check if we should skip the original command
|
|
578
|
+
api_key = os.environ.get("OPENAI_API_KEY")
|
|
579
|
+
should_skip, skip_reason = cmd_manager.should_skip_original_command(
|
|
580
|
+
cmd_text, retry_fix_command, retry_fix_stdout, retry_fix_stderr, api_key
|
|
581
|
+
)
|
|
582
|
+
|
|
583
|
+
if should_skip:
|
|
584
|
+
print(f"š Skipping original command: {skip_reason}")
|
|
585
|
+
|
|
586
|
+
# Mark the original command as successful without running it
|
|
587
|
+
cmd_manager.mark_command_executed(
|
|
588
|
+
cmd_index, 'main', True,
|
|
589
|
+
f"Command skipped after successful web-enhanced fix: {skip_reason}",
|
|
590
|
+
"", time.time() - start_time
|
|
591
|
+
)
|
|
592
|
+
|
|
593
|
+
print(f"ā
Original command marked as successful (skipped)")
|
|
594
|
+
|
|
595
|
+
# After a successful web-enhanced fix and skipping the original command,
|
|
596
|
+
# analyze and update the entire command list
|
|
597
|
+
print("\nš Analyzing and updating remaining commands based on web-enhanced fix results...")
|
|
598
|
+
cmd_manager.update_command_list_with_llm(api_key)
|
|
599
|
+
else:
|
|
600
|
+
# Retry the original command
|
|
601
|
+
print(f"š Retrying original command: {cmd_text}")
|
|
602
|
+
retry_start_time = time.time()
|
|
603
|
+
retry_success, retry_stdout, retry_stderr = shell.execute(cmd_text, timeout=300)
|
|
604
|
+
retry_execution_time = time.time() - retry_start_time
|
|
605
|
+
|
|
606
|
+
# Update the original command status
|
|
607
|
+
cmd_manager.mark_command_executed(
|
|
608
|
+
cmd_index, 'main', retry_success, retry_stdout, retry_stderr, retry_execution_time
|
|
609
|
+
)
|
|
610
|
+
|
|
611
|
+
if retry_success:
|
|
612
|
+
print(f"ā
Original command succeeded after web-enhanced fix!")
|
|
613
|
+
|
|
614
|
+
# After a successful web-enhanced fix and successful retry,
|
|
615
|
+
# analyze and update the entire command list
|
|
616
|
+
print("\nš Analyzing and updating remaining commands based on web-enhanced fix results...")
|
|
617
|
+
cmd_manager.update_command_list_with_llm(api_key)
|
|
618
|
+
else:
|
|
619
|
+
print(f"ā ļø Original command still failed after web-enhanced fix, continuing...")
|
|
620
|
+
else:
|
|
621
|
+
print(f"ā Web-enhanced fix command also failed: {retry_fix_stderr}")
|
|
622
|
+
print(f"ā ļø Continuing with remaining commands...")
|
|
623
|
+
else:
|
|
624
|
+
print(f"ā No web-enhanced fix suggested")
|
|
625
|
+
print(f"ā ļø Continuing with remaining commands...")
|
|
626
|
+
|
|
627
|
+
except Exception as web_debug_e:
|
|
628
|
+
print(f"ā Web-enhanced debugging failed: {web_debug_e}")
|
|
629
|
+
print(f"ā ļø Continuing with remaining commands...")
|
|
595
630
|
else:
|
|
596
|
-
|
|
631
|
+
# Get the current debug model to show the correct provider name
|
|
632
|
+
from llm_debugging import get_current_debug_model
|
|
633
|
+
current_model = get_current_debug_model()
|
|
634
|
+
print(f"ā No fix suggested by {current_model.capitalize()}")
|
|
597
635
|
print(f"ā ļø Continuing with remaining commands...")
|
|
598
636
|
|
|
599
637
|
except Exception as debug_e:
|
|
@@ -635,10 +673,14 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
|
635
673
|
if failed_commands:
|
|
636
674
|
print(f"\nš Final batch analysis of {len(failed_commands)} failed commands...")
|
|
637
675
|
current_dir = shell.get_cwd()
|
|
638
|
-
api_key = os.environ.get("OPENAI_API_KEY")
|
|
639
676
|
|
|
640
|
-
#
|
|
641
|
-
|
|
677
|
+
# Get the correct API key for the current debug model
|
|
678
|
+
from llm_debugging import get_current_debug_model, get_api_key
|
|
679
|
+
current_model = get_current_debug_model()
|
|
680
|
+
api_key = get_api_key(current_model)
|
|
681
|
+
|
|
682
|
+
# Use batch analysis to get additional fixes with web search enabled
|
|
683
|
+
additional_fixes = cmd_manager.analyze_failed_commands_with_llm(api_key, current_dir, shell, use_web_search=True)
|
|
642
684
|
|
|
643
685
|
if additional_fixes:
|
|
644
686
|
print(f"š§ Executing {len(additional_fixes)} additional fix commands...")
|