gitarsenal-cli 1.4.9 ā 1.4.11
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/bin/gitarsenal.js +1 -18
- package/package.json +1 -1
- package/python/fix_commands.py +71 -0
- package/python/fix_setup_commands.py +116 -0
- package/python/patch_modal_script.py +75 -0
- package/python/test_llm_debug.py +120 -0
- package/python/test_modalSandboxScript.py +408 -20
package/bin/gitarsenal.js
CHANGED
@@ -225,24 +225,7 @@ async function runContainerCommand(options) {
|
|
225
225
|
}
|
226
226
|
}
|
227
227
|
|
228
|
-
//
|
229
|
-
console.log(chalk.bold('\nš Container Configuration:'));
|
230
|
-
console.log(chalk.cyan('Repository URL: ') + repoUrl);
|
231
|
-
console.log(chalk.cyan('GPU Type: ') + gpuType);
|
232
|
-
console.log(chalk.cyan('Volume: ') + (volumeName || 'None'));
|
233
|
-
|
234
|
-
if (useApi) {
|
235
|
-
console.log(chalk.cyan('Setup Commands: ') + 'Auto-detect from repository');
|
236
|
-
} else if (setupCommands.length > 0) {
|
237
|
-
console.log(chalk.cyan('Setup Commands:'));
|
238
|
-
setupCommands.forEach((cmd, i) => {
|
239
|
-
console.log(` ${i + 1}. ${cmd}`);
|
240
|
-
});
|
241
|
-
} else {
|
242
|
-
console.log(chalk.cyan('Setup Commands: ') + 'None');
|
243
|
-
}
|
244
|
-
|
245
|
-
// Confirm settings
|
228
|
+
// Confirm settings (configuration will be shown by Python script after GPU selection)
|
246
229
|
if (!skipConfirmation) {
|
247
230
|
const confirmAnswers = await inquirer.prompt([
|
248
231
|
{
|
package/package.json
CHANGED
@@ -0,0 +1,71 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Fix commands for Modal SSH container
|
4
|
+
This script fixes common issues with commands in the Modal SSH container
|
5
|
+
"""
|
6
|
+
|
7
|
+
import os
|
8
|
+
import sys
|
9
|
+
import subprocess
|
10
|
+
|
11
|
+
def fix_command(cmd):
|
12
|
+
"""Fix common issues with commands"""
|
13
|
+
# Replace 'source' with '.' for sh shell
|
14
|
+
if 'source' in cmd:
|
15
|
+
cmd = cmd.replace('source', '.')
|
16
|
+
print(f"ā
Fixed: Replaced 'source' with '.' for sh shell")
|
17
|
+
|
18
|
+
# Fix uv venv and activation in one command
|
19
|
+
if 'uv venv' in cmd and '&&' in cmd:
|
20
|
+
# The command is already trying to create and activate a venv
|
21
|
+
# Just need to make sure 'source' is replaced with '.'
|
22
|
+
pass
|
23
|
+
|
24
|
+
# Fix uv pip install without active venv
|
25
|
+
if 'uv pip install' in cmd and 'venv' not in cmd:
|
26
|
+
# Create and activate a venv first
|
27
|
+
cmd = f"uv venv .venv && . .venv/bin/activate && {cmd}"
|
28
|
+
print(f"ā
Fixed: Added venv creation and activation before pip install")
|
29
|
+
|
30
|
+
return cmd
|
31
|
+
|
32
|
+
def run_fixed_command(cmd):
|
33
|
+
"""Run the fixed command"""
|
34
|
+
fixed_cmd = fix_command(cmd)
|
35
|
+
print(f"\nš§ Running fixed command: {fixed_cmd}")
|
36
|
+
|
37
|
+
# Run the command
|
38
|
+
result = subprocess.run(fixed_cmd, shell=True, text=True, capture_output=True)
|
39
|
+
|
40
|
+
# Print the output
|
41
|
+
if result.stdout:
|
42
|
+
print(f"\n--- Output ---\n{result.stdout}")
|
43
|
+
if result.stderr:
|
44
|
+
print(f"\n--- Error ---\n{result.stderr}")
|
45
|
+
|
46
|
+
# Return the result
|
47
|
+
return result.returncode == 0
|
48
|
+
|
49
|
+
def main():
|
50
|
+
"""Main function"""
|
51
|
+
if len(sys.argv) < 2:
|
52
|
+
print("Usage: python fix_commands.py 'command to fix'")
|
53
|
+
return 1
|
54
|
+
|
55
|
+
# Get the command from command line
|
56
|
+
cmd = ' '.join(sys.argv[1:])
|
57
|
+
print(f"š Original command: {cmd}")
|
58
|
+
|
59
|
+
# Run the fixed command
|
60
|
+
success = run_fixed_command(cmd)
|
61
|
+
|
62
|
+
# Print the result
|
63
|
+
if success:
|
64
|
+
print("\nā
Command executed successfully")
|
65
|
+
else:
|
66
|
+
print("\nā Command failed")
|
67
|
+
|
68
|
+
return 0 if success else 1
|
69
|
+
|
70
|
+
if __name__ == "__main__":
|
71
|
+
sys.exit(main())
|
@@ -0,0 +1,116 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Fix setup commands for open-r1 repository
|
4
|
+
"""
|
5
|
+
|
6
|
+
import os
|
7
|
+
import sys
|
8
|
+
import subprocess
|
9
|
+
|
10
|
+
# Original problematic commands
|
11
|
+
original_commands = [
|
12
|
+
"git clone https://github.com/huggingface/open-r1.git",
|
13
|
+
"cd open-r1",
|
14
|
+
"uv venv openr1 --python 3.11 && source openr1/bin/activate && uv pip install --upgrade pip",
|
15
|
+
"uv pip install vllm==0.8.5.post1",
|
16
|
+
"uv pip install setuptools && uv pip install flash-attn --no-build-isolation"
|
17
|
+
]
|
18
|
+
|
19
|
+
# Fixed commands
|
20
|
+
fixed_commands = [
|
21
|
+
"git clone https://github.com/huggingface/open-r1.git",
|
22
|
+
"cd open-r1",
|
23
|
+
"uv venv openr1 --python 3.11 && . openr1/bin/activate && uv pip install --upgrade pip",
|
24
|
+
". openr1/bin/activate && uv pip install vllm==0.8.5.post1",
|
25
|
+
". openr1/bin/activate && uv pip install setuptools && uv pip install flash-attn --no-build-isolation"
|
26
|
+
]
|
27
|
+
|
28
|
+
def run_command(cmd, cwd=None):
|
29
|
+
"""Run a command and return the result"""
|
30
|
+
print(f"\nā¶ {cmd}")
|
31
|
+
|
32
|
+
try:
|
33
|
+
result = subprocess.run(
|
34
|
+
cmd,
|
35
|
+
shell=True,
|
36
|
+
text=True,
|
37
|
+
capture_output=True,
|
38
|
+
cwd=cwd
|
39
|
+
)
|
40
|
+
|
41
|
+
# Print output
|
42
|
+
if result.stdout:
|
43
|
+
print(result.stdout)
|
44
|
+
|
45
|
+
# Print error
|
46
|
+
if result.stderr:
|
47
|
+
print(f"Error: {result.stderr}")
|
48
|
+
|
49
|
+
# Return success/failure
|
50
|
+
return result.returncode == 0, result.stdout, result.stderr
|
51
|
+
except Exception as e:
|
52
|
+
print(f"Error executing command: {e}")
|
53
|
+
return False, "", str(e)
|
54
|
+
|
55
|
+
def main():
|
56
|
+
"""Main function"""
|
57
|
+
print("š§ Fixing setup commands for open-r1 repository")
|
58
|
+
|
59
|
+
# Check if we're in the right directory
|
60
|
+
cwd = os.getcwd()
|
61
|
+
print(f"š Current directory: {cwd}")
|
62
|
+
|
63
|
+
# Check if we need to change directory
|
64
|
+
if not cwd.endswith('open-r1'):
|
65
|
+
parent_dir = cwd
|
66
|
+
# Check if open-r1 exists in the current directory
|
67
|
+
if os.path.exists(os.path.join(cwd, 'open-r1')):
|
68
|
+
print(f"š Found open-r1 directory, changing to it")
|
69
|
+
os.chdir(os.path.join(cwd, 'open-r1'))
|
70
|
+
cwd = os.getcwd()
|
71
|
+
print(f"š New current directory: {cwd}")
|
72
|
+
|
73
|
+
# Run the fixed commands
|
74
|
+
for i, cmd in enumerate(fixed_commands):
|
75
|
+
print(f"\nš Running command {i+1}/{len(fixed_commands)}: {cmd}")
|
76
|
+
|
77
|
+
# Skip git clone if the directory already exists
|
78
|
+
if cmd.startswith("git clone") and os.path.exists("open-r1"):
|
79
|
+
print("ā
Repository already cloned, skipping")
|
80
|
+
continue
|
81
|
+
|
82
|
+
# Skip cd if we're already in the right directory
|
83
|
+
if cmd.startswith("cd "):
|
84
|
+
target_dir = cmd.split(" ", 1)[1]
|
85
|
+
if cwd.endswith(target_dir):
|
86
|
+
print(f"ā
Already in {target_dir}, skipping")
|
87
|
+
continue
|
88
|
+
|
89
|
+
# Run the command
|
90
|
+
success, stdout, stderr = run_command(cmd)
|
91
|
+
|
92
|
+
# Check if the command succeeded
|
93
|
+
if not success:
|
94
|
+
print(f"ā Command failed: {cmd}")
|
95
|
+
print(f"ā Error: {stderr}")
|
96
|
+
|
97
|
+
# If this is a cd command, try to continue
|
98
|
+
if cmd.startswith("cd "):
|
99
|
+
print("ā ļø Directory change failed, but continuing with next command")
|
100
|
+
continue
|
101
|
+
|
102
|
+
# For other commands, ask if the user wants to continue
|
103
|
+
try:
|
104
|
+
choice = input("Continue with next command? (y/n): ").strip().lower()
|
105
|
+
if choice != 'y':
|
106
|
+
print("š Stopping execution")
|
107
|
+
return 1
|
108
|
+
except:
|
109
|
+
print("š Stopping execution due to error")
|
110
|
+
return 1
|
111
|
+
|
112
|
+
print("\nā
All commands executed")
|
113
|
+
return 0
|
114
|
+
|
115
|
+
if __name__ == "__main__":
|
116
|
+
sys.exit(main())
|
@@ -0,0 +1,75 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Patch the test_modalSandboxScript.py file to fix issues with LLM debugging
|
4
|
+
"""
|
5
|
+
|
6
|
+
import os
|
7
|
+
import sys
|
8
|
+
import re
|
9
|
+
|
10
|
+
def read_file(filename):
|
11
|
+
"""Read a file and return its contents"""
|
12
|
+
with open(filename, 'r') as f:
|
13
|
+
return f.read()
|
14
|
+
|
15
|
+
def write_file(filename, content):
|
16
|
+
"""Write content to a file"""
|
17
|
+
with open(filename, 'w') as f:
|
18
|
+
f.write(content)
|
19
|
+
|
20
|
+
def patch_file(filename):
|
21
|
+
"""Patch the file to fix issues with LLM debugging"""
|
22
|
+
print(f"š§ Patching {filename}...")
|
23
|
+
|
24
|
+
# Read the file
|
25
|
+
content = read_file(filename)
|
26
|
+
|
27
|
+
# Make a backup
|
28
|
+
backup_filename = f"{filename}.bak"
|
29
|
+
write_file(backup_filename, content)
|
30
|
+
print(f"ā
Created backup: {backup_filename}")
|
31
|
+
|
32
|
+
# Fix 1: Replace 'source' with '.' in commands
|
33
|
+
pattern1 = r'(uv venv.*?)source(.*?activate)'
|
34
|
+
replacement1 = r'\1.\2'
|
35
|
+
content = re.sub(pattern1, replacement1, content)
|
36
|
+
print("ā
Fixed 'source' commands")
|
37
|
+
|
38
|
+
# Fix 2: Add environment activation before uv pip commands
|
39
|
+
pattern2 = r'(uv pip install.*?)(?!\. .*?activate)'
|
40
|
+
replacement2 = r'. openr1/bin/activate && \1'
|
41
|
+
content = re.sub(pattern2, replacement2, content)
|
42
|
+
print("ā
Added environment activation before pip commands")
|
43
|
+
|
44
|
+
# Write the patched file
|
45
|
+
write_file(filename, content)
|
46
|
+
print(f"ā
Patched file written: {filename}")
|
47
|
+
|
48
|
+
return True
|
49
|
+
|
50
|
+
def main():
|
51
|
+
"""Main function"""
|
52
|
+
# Get the file to patch
|
53
|
+
if len(sys.argv) > 1:
|
54
|
+
filename = sys.argv[1]
|
55
|
+
else:
|
56
|
+
# Default to the test_modalSandboxScript.py in the current directory
|
57
|
+
filename = "test_modalSandboxScript.py"
|
58
|
+
|
59
|
+
# Check if the file exists
|
60
|
+
if not os.path.exists(filename):
|
61
|
+
print(f"ā File not found: {filename}")
|
62
|
+
return 1
|
63
|
+
|
64
|
+
# Patch the file
|
65
|
+
success = patch_file(filename)
|
66
|
+
|
67
|
+
if success:
|
68
|
+
print("\nā
Patching completed successfully")
|
69
|
+
return 0
|
70
|
+
else:
|
71
|
+
print("\nā Patching failed")
|
72
|
+
return 1
|
73
|
+
|
74
|
+
if __name__ == "__main__":
|
75
|
+
sys.exit(main())
|
@@ -0,0 +1,120 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Test script to verify LLM debugging functionality
|
4
|
+
"""
|
5
|
+
|
6
|
+
import os
|
7
|
+
import sys
|
8
|
+
import requests
|
9
|
+
import getpass
|
10
|
+
|
11
|
+
def test_openai_connection():
|
12
|
+
"""Test if we can connect to OpenAI API"""
|
13
|
+
print("š Testing OpenAI API connection...")
|
14
|
+
|
15
|
+
# Try to get API key
|
16
|
+
api_key = os.environ.get("OPENAI_API_KEY")
|
17
|
+
if not api_key:
|
18
|
+
print("ā No OPENAI_API_KEY environment variable found")
|
19
|
+
print("š” Please set your OpenAI API key:")
|
20
|
+
print(" export OPENAI_API_KEY='your-api-key-here'")
|
21
|
+
return False
|
22
|
+
|
23
|
+
print(f"ā
Found API key (length: {len(api_key)})")
|
24
|
+
|
25
|
+
# Test API connection
|
26
|
+
headers = {
|
27
|
+
"Content-Type": "application/json",
|
28
|
+
"Authorization": f"Bearer {api_key}"
|
29
|
+
}
|
30
|
+
|
31
|
+
payload = {
|
32
|
+
"model": "gpt-4o-mini",
|
33
|
+
"messages": [
|
34
|
+
{"role": "user", "content": "Say 'Hello, LLM debugging is working!'"}
|
35
|
+
],
|
36
|
+
"max_tokens": 50
|
37
|
+
}
|
38
|
+
|
39
|
+
try:
|
40
|
+
print("š¤ Testing API call...")
|
41
|
+
response = requests.post(
|
42
|
+
"https://api.openai.com/v1/chat/completions",
|
43
|
+
headers=headers,
|
44
|
+
json=payload,
|
45
|
+
timeout=30
|
46
|
+
)
|
47
|
+
|
48
|
+
if response.status_code == 200:
|
49
|
+
result = response.json()
|
50
|
+
message = result["choices"][0]["message"]["content"]
|
51
|
+
print(f"ā
API test successful: {message}")
|
52
|
+
return True
|
53
|
+
else:
|
54
|
+
print(f"ā API test failed with status code: {response.status_code}")
|
55
|
+
print(f"Response: {response.text}")
|
56
|
+
return False
|
57
|
+
|
58
|
+
except Exception as e:
|
59
|
+
print(f"ā API test failed with exception: {e}")
|
60
|
+
return False
|
61
|
+
|
62
|
+
def test_llm_debug_function():
|
63
|
+
"""Test the LLM debug function from the main script"""
|
64
|
+
print("\nš Testing LLM debug function...")
|
65
|
+
|
66
|
+
# Import the function from the main script
|
67
|
+
try:
|
68
|
+
# Add the current directory to Python path
|
69
|
+
sys.path.insert(0, os.path.dirname(__file__))
|
70
|
+
|
71
|
+
# Import the function
|
72
|
+
from test_modalSandboxScript import call_openai_for_debug
|
73
|
+
|
74
|
+
# Test with a simple command failure
|
75
|
+
test_command = "ls /nonexistent/directory"
|
76
|
+
test_error = "ls: cannot access '/nonexistent/directory': No such file or directory"
|
77
|
+
|
78
|
+
print(f"š§Ŗ Testing with command: {test_command}")
|
79
|
+
print(f"š§Ŗ Error: {test_error}")
|
80
|
+
|
81
|
+
result = call_openai_for_debug(test_command, test_error)
|
82
|
+
|
83
|
+
if result:
|
84
|
+
print(f"ā
LLM debug function returned: {result}")
|
85
|
+
return True
|
86
|
+
else:
|
87
|
+
print("ā LLM debug function returned None")
|
88
|
+
return False
|
89
|
+
|
90
|
+
except Exception as e:
|
91
|
+
print(f"ā Error testing LLM debug function: {e}")
|
92
|
+
return False
|
93
|
+
|
94
|
+
if __name__ == "__main__":
|
95
|
+
print("š§Ŗ Testing LLM debugging functionality...")
|
96
|
+
print("=" * 60)
|
97
|
+
|
98
|
+
# Test 1: OpenAI API connection
|
99
|
+
api_ok = test_openai_connection()
|
100
|
+
|
101
|
+
# Test 2: LLM debug function
|
102
|
+
if api_ok:
|
103
|
+
debug_ok = test_llm_debug_function()
|
104
|
+
else:
|
105
|
+
debug_ok = False
|
106
|
+
|
107
|
+
print("\n" + "=" * 60)
|
108
|
+
print("š Test Results:")
|
109
|
+
print(f" OpenAI API Connection: {'ā
PASS' if api_ok else 'ā FAIL'}")
|
110
|
+
print(f" LLM Debug Function: {'ā
PASS' if debug_ok else 'ā FAIL'}")
|
111
|
+
|
112
|
+
if api_ok and debug_ok:
|
113
|
+
print("\nš All tests passed! LLM debugging should work.")
|
114
|
+
else:
|
115
|
+
print("\nā ļø Some tests failed. LLM debugging may not work properly.")
|
116
|
+
if not api_ok:
|
117
|
+
print("š” To fix OpenAI API issues:")
|
118
|
+
print(" 1. Get an API key from https://platform.openai.com/api-keys")
|
119
|
+
print(" 2. Set it as environment variable: export OPENAI_API_KEY='your-key'")
|
120
|
+
print(" 3. Run this test again")
|
@@ -331,6 +331,12 @@ def handle_interactive_command(cmd, sandbox, current_dir):
|
|
331
331
|
|
332
332
|
def call_openai_for_debug(command, error_output, api_key=None, current_dir=None, sandbox=None):
|
333
333
|
"""Call OpenAI to debug a failed command and suggest a fix"""
|
334
|
+
print("\nš DEBUG: Starting OpenAI LLM debugging...")
|
335
|
+
print(f"š DEBUG: Command: {command}")
|
336
|
+
print(f"š DEBUG: Error output length: {len(error_output) if error_output else 0}")
|
337
|
+
print(f"š DEBUG: Current directory: {current_dir}")
|
338
|
+
print(f"š DEBUG: Sandbox available: {sandbox is not None}")
|
339
|
+
|
334
340
|
# Define _to_str function locally to avoid NameError
|
335
341
|
def _to_str(maybe_bytes):
|
336
342
|
try:
|
@@ -359,8 +365,11 @@ def call_openai_for_debug(command, error_output, api_key=None, current_dir=None,
|
|
359
365
|
|
360
366
|
# Try to get API key from multiple sources
|
361
367
|
if not api_key:
|
368
|
+
print("š DEBUG: No API key provided, searching for one...")
|
369
|
+
|
362
370
|
# First try environment variable
|
363
371
|
api_key = os.environ.get("OPENAI_API_KEY")
|
372
|
+
print(f"š DEBUG: API key from environment: {'Found' if api_key else 'Not found'}")
|
364
373
|
|
365
374
|
# Store the API key in a persistent file if found
|
366
375
|
if api_key:
|
@@ -376,28 +385,38 @@ def call_openai_for_debug(command, error_output, api_key=None, current_dir=None,
|
|
376
385
|
if not api_key:
|
377
386
|
try:
|
378
387
|
key_file = os.path.expanduser("~/.gitarsenal/openai_key")
|
388
|
+
print(f"š DEBUG: Checking for saved API key at: {key_file}")
|
379
389
|
if os.path.exists(key_file):
|
380
390
|
with open(key_file, "r") as f:
|
381
391
|
api_key = f.read().strip()
|
382
392
|
if api_key:
|
383
393
|
print("ā
Loaded OpenAI API key from saved file")
|
394
|
+
print(f"š DEBUG: API key length: {len(api_key)}")
|
384
395
|
# Also set in environment for this session
|
385
396
|
os.environ["OPENAI_API_KEY"] = api_key
|
397
|
+
else:
|
398
|
+
print("š DEBUG: Saved file exists but is empty")
|
399
|
+
else:
|
400
|
+
print("š DEBUG: No saved API key file found")
|
386
401
|
except Exception as e:
|
387
402
|
print(f"ā ļø Could not load saved API key: {e}")
|
388
403
|
|
389
404
|
# Then try credentials manager
|
390
405
|
if not api_key:
|
406
|
+
print("š DEBUG: Trying credentials manager...")
|
391
407
|
try:
|
392
408
|
from credentials_manager import CredentialsManager
|
393
409
|
credentials_manager = CredentialsManager()
|
394
410
|
api_key = credentials_manager.get_openai_api_key()
|
395
|
-
|
411
|
+
print(f"š DEBUG: API key from credentials manager: {'Found' if api_key else 'Not found'}")
|
412
|
+
except ImportError as e:
|
413
|
+
print(f"š DEBUG: Credentials manager not available: {e}")
|
396
414
|
# Fall back to direct input if credentials_manager is not available
|
397
415
|
pass
|
398
416
|
|
399
417
|
# Finally, prompt the user if still no API key
|
400
418
|
if not api_key:
|
419
|
+
print("š DEBUG: No API key found in any source, prompting user...")
|
401
420
|
print("\n" + "="*60)
|
402
421
|
print("š OPENAI API KEY REQUIRED FOR DEBUGGING")
|
403
422
|
print("="*60)
|
@@ -421,6 +440,14 @@ def call_openai_for_debug(command, error_output, api_key=None, current_dir=None,
|
|
421
440
|
print(f"ā Error getting API key: {e}")
|
422
441
|
return None
|
423
442
|
|
443
|
+
# If we still don't have an API key, we can't proceed
|
444
|
+
if not api_key:
|
445
|
+
print("ā No OpenAI API key available. Cannot perform LLM debugging.")
|
446
|
+
print("š” To enable LLM debugging, set the OPENAI_API_KEY environment variable")
|
447
|
+
return None
|
448
|
+
|
449
|
+
print(f"ā
OpenAI API key available (length: {len(api_key)})")
|
450
|
+
|
424
451
|
# Gather additional context to help with debugging
|
425
452
|
directory_context = ""
|
426
453
|
system_info = ""
|
@@ -606,6 +633,8 @@ Do not provide any explanations, just the exact command to run.
|
|
606
633
|
"""
|
607
634
|
|
608
635
|
# Prepare the API request payload
|
636
|
+
print("š DEBUG: Preparing API request...")
|
637
|
+
|
609
638
|
# Try to use GPT-4 first, but fall back to other models if needed
|
610
639
|
models_to_try = [
|
611
640
|
"gpt-4o-mini", # First choice: GPT-4o (most widely available)
|
@@ -620,9 +649,16 @@ Do not provide any explanations, just the exact command to run.
|
|
620
649
|
|
621
650
|
# Remove duplicates while preserving order
|
622
651
|
models_to_try = list(dict.fromkeys(models_to_try))
|
652
|
+
print(f"š DEBUG: Models to try: {models_to_try}")
|
623
653
|
|
624
654
|
# Function to make the API call with a specific model
|
625
655
|
def try_api_call(model_name, retries=2, backoff_factor=1.5):
|
656
|
+
print(f"š DEBUG: Attempting API call with model: {model_name}")
|
657
|
+
print(f"š DEBUG: API key available: {'Yes' if api_key else 'No'}")
|
658
|
+
if api_key:
|
659
|
+
print(f"š DEBUG: API key length: {len(api_key)}")
|
660
|
+
print(f"š DEBUG: API key starts with: {api_key[:10]}...")
|
661
|
+
|
626
662
|
payload = {
|
627
663
|
"model": model_name,
|
628
664
|
"messages": [
|
@@ -633,6 +669,8 @@ Do not provide any explanations, just the exact command to run.
|
|
633
669
|
"max_tokens": 300
|
634
670
|
}
|
635
671
|
|
672
|
+
print(f"š DEBUG: Payload prepared, prompt length: {len(prompt)}")
|
673
|
+
|
636
674
|
# Add specific handling for common errors
|
637
675
|
last_error = None
|
638
676
|
for attempt in range(retries + 1):
|
@@ -644,6 +682,7 @@ Do not provide any explanations, just the exact command to run.
|
|
644
682
|
time.sleep(wait_time)
|
645
683
|
|
646
684
|
print(f"š¤ Calling OpenAI with {model_name} model to debug the failed command...")
|
685
|
+
print(f"š DEBUG: Making POST request to OpenAI API...")
|
647
686
|
response = requests.post(
|
648
687
|
"https://api.openai.com/v1/chat/completions",
|
649
688
|
headers=headers,
|
@@ -651,29 +690,36 @@ Do not provide any explanations, just the exact command to run.
|
|
651
690
|
timeout=45 # Increased timeout for reliability
|
652
691
|
)
|
653
692
|
|
693
|
+
print(f"š DEBUG: Response received, status code: {response.status_code}")
|
694
|
+
|
654
695
|
# Handle specific status codes
|
655
696
|
if response.status_code == 200:
|
697
|
+
print(f"š DEBUG: Success! Response length: {len(response.text)}")
|
656
698
|
return response.json(), None
|
657
699
|
elif response.status_code == 401:
|
658
700
|
error_msg = "Authentication error: Invalid API key"
|
659
701
|
print(f"ā {error_msg}")
|
702
|
+
print(f"š DEBUG: Response text: {response.text}")
|
660
703
|
# Don't retry auth errors
|
661
704
|
return None, error_msg
|
662
705
|
elif response.status_code == 429:
|
663
706
|
error_msg = "Rate limit exceeded or quota reached"
|
664
707
|
print(f"ā ļø {error_msg}")
|
708
|
+
print(f"š DEBUG: Response text: {response.text}")
|
665
709
|
# Always retry rate limit errors with increasing backoff
|
666
710
|
last_error = error_msg
|
667
711
|
continue
|
668
712
|
elif response.status_code == 500:
|
669
713
|
error_msg = "OpenAI server error"
|
670
714
|
print(f"ā ļø {error_msg}")
|
715
|
+
print(f"š DEBUG: Response text: {response.text}")
|
671
716
|
# Retry server errors
|
672
717
|
last_error = error_msg
|
673
718
|
continue
|
674
719
|
else:
|
675
720
|
error_msg = f"Status code: {response.status_code}, Response: {response.text}"
|
676
721
|
print(f"ā ļø OpenAI API error: {error_msg}")
|
722
|
+
print(f"š DEBUG: Full response text: {response.text}")
|
677
723
|
last_error = error_msg
|
678
724
|
# Only retry if we have attempts left
|
679
725
|
if attempt < retries:
|
@@ -682,18 +728,22 @@ Do not provide any explanations, just the exact command to run.
|
|
682
728
|
except requests.exceptions.Timeout:
|
683
729
|
error_msg = "Request timed out"
|
684
730
|
print(f"ā ļø {error_msg}")
|
731
|
+
print(f"š DEBUG: Timeout after 45 seconds")
|
685
732
|
last_error = error_msg
|
686
733
|
# Always retry timeouts
|
687
734
|
continue
|
688
735
|
except requests.exceptions.ConnectionError:
|
689
736
|
error_msg = "Connection error"
|
690
737
|
print(f"ā ļø {error_msg}")
|
738
|
+
print(f"š DEBUG: Connection failed to api.openai.com")
|
691
739
|
last_error = error_msg
|
692
740
|
# Always retry connection errors
|
693
741
|
continue
|
694
742
|
except Exception as e:
|
695
743
|
error_msg = str(e)
|
696
744
|
print(f"ā ļø Unexpected error: {error_msg}")
|
745
|
+
print(f"š DEBUG: Exception type: {type(e).__name__}")
|
746
|
+
print(f"š DEBUG: Exception details: {str(e)}")
|
697
747
|
last_error = error_msg
|
698
748
|
# Only retry if we have attempts left
|
699
749
|
if attempt < retries:
|
@@ -722,7 +772,12 @@ Do not provide any explanations, just the exact command to run.
|
|
722
772
|
|
723
773
|
# Process the response
|
724
774
|
try:
|
775
|
+
print(f"š DEBUG: Processing OpenAI response...")
|
776
|
+
print(f"š DEBUG: Response structure: {list(result.keys())}")
|
777
|
+
print(f"š DEBUG: Choices count: {len(result.get('choices', []))}")
|
778
|
+
|
725
779
|
fix_command = result["choices"][0]["message"]["content"].strip()
|
780
|
+
print(f"š DEBUG: Raw response content: {fix_command}")
|
726
781
|
|
727
782
|
# Save the original response for debugging
|
728
783
|
original_response = fix_command
|
@@ -798,9 +853,12 @@ Do not provide any explanations, just the exact command to run.
|
|
798
853
|
print("ā ļø Using best guess for command")
|
799
854
|
|
800
855
|
print(f"š§ Suggested fix: {fix_command}")
|
856
|
+
print(f"š DEBUG: Returning fix command: {fix_command}")
|
801
857
|
return fix_command
|
802
858
|
except Exception as e:
|
803
859
|
print(f"ā Error processing OpenAI response: {e}")
|
860
|
+
print(f"š DEBUG: Exception type: {type(e).__name__}")
|
861
|
+
print(f"š DEBUG: Exception details: {str(e)}")
|
804
862
|
return None
|
805
863
|
|
806
864
|
def prompt_for_hf_token():
|
@@ -1646,6 +1704,11 @@ cd "{current_dir}"
|
|
1646
1704
|
# If command failed and we're debugging with LLM
|
1647
1705
|
if debug_with_llm:
|
1648
1706
|
print("š Attempting to debug the failed command with OpenAI...")
|
1707
|
+
print(f"š DEBUG: Command that failed: {cmd_to_execute}")
|
1708
|
+
print(f"š DEBUG: Exit code: {exit_code}")
|
1709
|
+
print(f"š DEBUG: stderr length: {len(stderr_buffer)}")
|
1710
|
+
print(f"š DEBUG: stdout length: {len(stdout_buffer)}")
|
1711
|
+
|
1649
1712
|
# Ensure we have a non-empty error message to debug
|
1650
1713
|
if not stderr_buffer.strip() and stdout_buffer.strip():
|
1651
1714
|
print("ā ļø stderr is empty but stdout contains content, using stdout for debugging")
|
@@ -1848,8 +1911,17 @@ cd "{current_dir}"
|
|
1848
1911
|
print(debug_output if debug_output else "[EMPTY]")
|
1849
1912
|
print("="*60)
|
1850
1913
|
|
1914
|
+
print(f"š DEBUG: About to call call_openai_for_debug...")
|
1915
|
+
print(f"š DEBUG: Command: {cmd_to_execute}")
|
1916
|
+
print(f"š DEBUG: Debug output length: {len(debug_output)}")
|
1917
|
+
print(f"š DEBUG: Current directory: {current_dir}")
|
1918
|
+
print(f"š DEBUG: Sandbox available: {sandbox is not None}")
|
1919
|
+
print(f"š DEBUG: Debug output preview: {debug_output[:200]}...")
|
1920
|
+
|
1851
1921
|
fix_command = call_openai_for_debug(cmd_to_execute, debug_output, current_dir=current_dir, sandbox=sandbox)
|
1852
1922
|
|
1923
|
+
print(f"š DEBUG: call_openai_for_debug returned: {fix_command}")
|
1924
|
+
|
1853
1925
|
if fix_command:
|
1854
1926
|
print(f"š§ OpenAI suggested fix command: {fix_command}")
|
1855
1927
|
|
@@ -2487,7 +2559,7 @@ ssh_app = modal.App("ssh-container-app")
|
|
2487
2559
|
"python3", "python3-pip", "build-essential", "tmux", "screen", "nano",
|
2488
2560
|
"gpg", "ca-certificates", "software-properties-common"
|
2489
2561
|
)
|
2490
|
-
.pip_install("uv", "modal") #
|
2562
|
+
.pip_install("uv", "modal", "requests", "openai") # Install required packages for LLM debugging
|
2491
2563
|
.run_commands(
|
2492
2564
|
# Create SSH directory
|
2493
2565
|
"mkdir -p /var/run/sshd",
|
@@ -2553,17 +2625,172 @@ def ssh_container_function(ssh_password, repo_url=None, repo_name=None, setup_co
|
|
2553
2625
|
# Run setup commands if provided
|
2554
2626
|
if setup_commands:
|
2555
2627
|
print(f"āļø Running {len(setup_commands)} setup commands...")
|
2556
|
-
|
2557
|
-
|
2628
|
+
|
2629
|
+
# First, let's check the current directory structure
|
2630
|
+
print("š Checking current directory structure before running setup commands...")
|
2631
|
+
try:
|
2632
|
+
result = subprocess.run("pwd && ls -la", shell=True, check=True,
|
2633
|
+
capture_output=True, text=True)
|
2634
|
+
print(f"š Current directory: {result.stdout}")
|
2635
|
+
except subprocess.CalledProcessError as e:
|
2636
|
+
print(f"ā ļø Could not check directory structure: {e}")
|
2637
|
+
|
2638
|
+
# Define a simple run_command function for SSH container
|
2639
|
+
def run_command_with_llm_debug(cmd, show_output=True, retry_count=0, max_retries=3):
|
2640
|
+
"""Execute a command with LLM debugging enabled"""
|
2641
|
+
print(f"š§ Executing: {cmd}")
|
2558
2642
|
try:
|
2559
|
-
|
2560
|
-
|
2561
|
-
|
2643
|
+
# Handle special case for source command which doesn't work with subprocess.run
|
2644
|
+
if cmd.strip().startswith("source ") or " source " in cmd:
|
2645
|
+
print("ā ļø Detected 'source' command which doesn't work with subprocess.run")
|
2646
|
+
print("š Converting to bash -c with dot (.) instead of source")
|
2647
|
+
# Replace source with . (dot) which is the same as source but works in sh
|
2648
|
+
modified_cmd = cmd.replace("source ", ". ")
|
2649
|
+
# Wrap in bash -c to ensure it runs in bash
|
2650
|
+
bash_cmd = f"bash -c '{modified_cmd}'"
|
2651
|
+
print(f"š Modified command: {bash_cmd}")
|
2652
|
+
result = subprocess.run(bash_cmd, shell=True, check=True,
|
2653
|
+
capture_output=True, text=True)
|
2654
|
+
else:
|
2655
|
+
result = subprocess.run(cmd, shell=True, check=True,
|
2656
|
+
capture_output=True, text=True)
|
2657
|
+
|
2658
|
+
if result.stdout and show_output:
|
2562
2659
|
print(f"ā
Output: {result.stdout}")
|
2660
|
+
return True, result.stdout, ""
|
2563
2661
|
except subprocess.CalledProcessError as e:
|
2662
|
+
error_output = e.stderr if e.stderr else str(e)
|
2564
2663
|
print(f"ā Command failed: {e}")
|
2565
|
-
|
2566
|
-
|
2664
|
+
print(f"ā Error: {error_output}")
|
2665
|
+
|
2666
|
+
# Check for common errors that we can fix automatically
|
2667
|
+
common_errors = [
|
2668
|
+
# Source command not found
|
2669
|
+
{
|
2670
|
+
"pattern": "source: not found",
|
2671
|
+
"fix": lambda cmd: cmd.replace("source ", ". ")
|
2672
|
+
},
|
2673
|
+
# Conda not found
|
2674
|
+
{
|
2675
|
+
"pattern": "conda: not found",
|
2676
|
+
"fix": lambda cmd: "apt-get update && apt-get install -y wget bzip2 && wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O /tmp/miniconda.sh && bash /tmp/miniconda.sh -b -p /opt/conda && export PATH=/opt/conda/bin:$PATH && echo 'export PATH=/opt/conda/bin:$PATH' >> ~/.bashrc && source ~/.bashrc && " + cmd
|
2677
|
+
},
|
2678
|
+
# File not found for chmod
|
2679
|
+
{
|
2680
|
+
"pattern": "chmod: cannot access",
|
2681
|
+
"fix": lambda cmd: "pwd && ls -la && echo 'File not found, checking current directory and contents'"
|
2682
|
+
},
|
2683
|
+
# No such file or directory
|
2684
|
+
{
|
2685
|
+
"pattern": "No such file or directory",
|
2686
|
+
"fix": lambda cmd: "pwd && ls -la && echo 'Checking current directory and contents'"
|
2687
|
+
},
|
2688
|
+
# Directory navigation issues
|
2689
|
+
{
|
2690
|
+
"pattern": "cd: no such file or directory",
|
2691
|
+
"fix": lambda cmd: "pwd && ls -la && echo 'Directory not found, checking current location'"
|
2692
|
+
},
|
2693
|
+
# File not found for execution
|
2694
|
+
{
|
2695
|
+
"pattern": "not found",
|
2696
|
+
"fix": lambda cmd: "pwd && ls -la && echo 'File not found, checking current location'"
|
2697
|
+
}
|
2698
|
+
]
|
2699
|
+
|
2700
|
+
# Check if any of the common errors match and apply automatic fix
|
2701
|
+
for error_info in common_errors:
|
2702
|
+
if error_info["pattern"] in error_output:
|
2703
|
+
print(f"š Detected common error: {error_info['pattern']}")
|
2704
|
+
fix_func = error_info["fix"]
|
2705
|
+
fixed_cmd = fix_func(cmd)
|
2706
|
+
print(f"š§ Applying automatic fix: {fixed_cmd}")
|
2707
|
+
|
2708
|
+
# Run the fixed command
|
2709
|
+
try:
|
2710
|
+
fix_result = subprocess.run(fixed_cmd, shell=True, check=True,
|
2711
|
+
capture_output=True, text=True)
|
2712
|
+
if fix_result.stdout:
|
2713
|
+
print(f"ā
Automatic fix output: {fix_result.stdout}")
|
2714
|
+
|
2715
|
+
# Retry the original command
|
2716
|
+
print(f"š Retrying original command: {cmd}")
|
2717
|
+
return run_command_with_llm_debug(cmd, show_output, retry_count + 1, max_retries)
|
2718
|
+
except subprocess.CalledProcessError as fix_e:
|
2719
|
+
print(f"ā Automatic fix failed: {fix_e}")
|
2720
|
+
# Continue with LLM debugging
|
2721
|
+
|
2722
|
+
# Call OpenAI for debugging
|
2723
|
+
print("š Attempting to debug the failed command with OpenAI...")
|
2724
|
+
try:
|
2725
|
+
# Get the current directory for context
|
2726
|
+
current_dir = os.getcwd()
|
2727
|
+
|
2728
|
+
# Call OpenAI for debugging
|
2729
|
+
print(f"š DEBUG: About to call call_openai_for_debug...")
|
2730
|
+
print(f"š DEBUG: Command: {cmd}")
|
2731
|
+
print(f"š DEBUG: Error output length: {len(error_output)}")
|
2732
|
+
print(f"š DEBUG: Current directory: {current_dir}")
|
2733
|
+
|
2734
|
+
fix_command = call_openai_for_debug(cmd, error_output, current_dir=current_dir)
|
2735
|
+
|
2736
|
+
print(f"š DEBUG: call_openai_for_debug returned: {fix_command}")
|
2737
|
+
|
2738
|
+
if fix_command:
|
2739
|
+
print(f"š§ OpenAI suggested fix command: {fix_command}")
|
2740
|
+
|
2741
|
+
# Run the fix command
|
2742
|
+
print(f"š Running suggested fix command: {fix_command}")
|
2743
|
+
try:
|
2744
|
+
fix_result = subprocess.run(fix_command, shell=True, check=True,
|
2745
|
+
capture_output=True, text=True)
|
2746
|
+
if fix_result.stdout:
|
2747
|
+
print(f"ā
Fix command output: {fix_result.stdout}")
|
2748
|
+
|
2749
|
+
# Retry the original command
|
2750
|
+
print(f"š Retrying original command: {cmd}")
|
2751
|
+
return run_command_with_llm_debug(cmd, show_output, retry_count + 1, max_retries)
|
2752
|
+
except subprocess.CalledProcessError as fix_e:
|
2753
|
+
print(f"ā Fix command also failed: {fix_e}")
|
2754
|
+
return False, "", error_output
|
2755
|
+
else:
|
2756
|
+
print("ā No fix suggested by OpenAI")
|
2757
|
+
return False, "", error_output
|
2758
|
+
|
2759
|
+
except Exception as debug_e:
|
2760
|
+
print(f"ā LLM debugging failed: {debug_e}")
|
2761
|
+
return False, "", error_output
|
2762
|
+
|
2763
|
+
for i, cmd in enumerate(setup_commands, 1):
|
2764
|
+
print(f"š Executing command {i}/{len(setup_commands)}: {cmd}")
|
2765
|
+
|
2766
|
+
# Check if this is a cd command and if the directory exists
|
2767
|
+
if cmd.strip().startswith("cd "):
|
2768
|
+
cd_parts = cmd.split(None, 1)
|
2769
|
+
if len(cd_parts) >= 2:
|
2770
|
+
target_dir = cd_parts[1].strip('"\'')
|
2771
|
+
print(f"š Checking if directory exists: {target_dir}")
|
2772
|
+
try:
|
2773
|
+
check_result = subprocess.run(f"test -d '{target_dir}'", shell=True,
|
2774
|
+
capture_output=True, text=True)
|
2775
|
+
if check_result.returncode != 0:
|
2776
|
+
print(f"ā ļø Directory does not exist: {target_dir}")
|
2777
|
+
print(f"š Current directory contents:")
|
2778
|
+
subprocess.run("pwd && ls -la", shell=True, check=False)
|
2779
|
+
|
2780
|
+
# Try to find similar directories
|
2781
|
+
print(f"š Looking for similar directories...")
|
2782
|
+
subprocess.run("find . -type d -name '*llama*' -o -name '*nano*' 2>/dev/null | head -10", shell=True, check=False)
|
2783
|
+
except Exception as e:
|
2784
|
+
print(f"ā ļø Could not check directory: {e}")
|
2785
|
+
|
2786
|
+
success, stdout, stderr = run_command_with_llm_debug(cmd, show_output=True)
|
2787
|
+
if not success:
|
2788
|
+
print(f"ā ļø Command {i} failed, but continuing with remaining commands...")
|
2789
|
+
|
2790
|
+
# If this was a cd command that failed, try to understand the directory structure
|
2791
|
+
if cmd.strip().startswith("cd ") and "No such file or directory" in stderr:
|
2792
|
+
print(f"š Analyzing directory structure after failed cd command...")
|
2793
|
+
subprocess.run("pwd && ls -la && echo '--- Parent directory ---' && ls -la ..", shell=True, check=False)
|
2567
2794
|
|
2568
2795
|
# Get container info
|
2569
2796
|
print("š Container started successfully!")
|
@@ -2798,7 +3025,7 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
2798
3025
|
"python3", "python3-pip", "build-essential", "tmux", "screen", "nano",
|
2799
3026
|
"gpg", "ca-certificates", "software-properties-common"
|
2800
3027
|
)
|
2801
|
-
.pip_install("uv", "modal") # Fast Python package installer and
|
3028
|
+
.pip_install("uv", "modal", "requests") # Fast Python package installer, Modal, and requests for API calls
|
2802
3029
|
.run_commands(
|
2803
3030
|
# Create SSH directory
|
2804
3031
|
"mkdir -p /var/run/sshd",
|
@@ -2831,7 +3058,7 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
2831
3058
|
if volume:
|
2832
3059
|
volumes_config[volume_mount_path] = volume
|
2833
3060
|
|
2834
|
-
# Define the SSH container function
|
3061
|
+
# Define the SSH container function with all necessary imports
|
2835
3062
|
@app.function(
|
2836
3063
|
image=ssh_image,
|
2837
3064
|
timeout=timeout_minutes * 60, # Convert to seconds
|
@@ -2840,6 +3067,8 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
2840
3067
|
memory=8192,
|
2841
3068
|
serialized=True,
|
2842
3069
|
volumes=volumes_config if volumes_config else None,
|
3070
|
+
# Include all required modules in container
|
3071
|
+
mounts=[modal.Mount.from_local_python_packages("requests", "openai")]
|
2843
3072
|
)
|
2844
3073
|
def ssh_container_function():
|
2845
3074
|
"""Start SSH container with password authentication and optional setup."""
|
@@ -2874,17 +3103,164 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
2874
3103
|
# Run setup commands if provided
|
2875
3104
|
if setup_commands:
|
2876
3105
|
print(f"āļø Running {len(setup_commands)} setup commands...")
|
2877
|
-
|
2878
|
-
|
3106
|
+
|
3107
|
+
# Define a helper function for running commands with LLM debugging
|
3108
|
+
def run_command_with_basic_error_handling(cmd, show_output=True, retry_count=0, max_retries=3):
|
3109
|
+
"""Execute a command with LLM debugging enabled"""
|
3110
|
+
print(f"š§ Executing: {cmd}")
|
2879
3111
|
try:
|
2880
|
-
|
2881
|
-
|
2882
|
-
|
3112
|
+
# Handle special case for source command which doesn't work with subprocess.run
|
3113
|
+
if cmd.strip().startswith("source ") or " source " in cmd:
|
3114
|
+
print("ā ļø Detected 'source' command which doesn't work with subprocess.run")
|
3115
|
+
print("š Converting to bash -c with dot (.) instead of source")
|
3116
|
+
# Replace source with . (dot) which is the same as source but works in sh
|
3117
|
+
modified_cmd = cmd.replace("source ", ". ")
|
3118
|
+
# Wrap in bash -c to ensure it runs in bash
|
3119
|
+
bash_cmd = f"bash -c '{modified_cmd}'"
|
3120
|
+
print(f"š Modified command: {bash_cmd}")
|
3121
|
+
result = subprocess.run(bash_cmd, shell=True, check=True,
|
3122
|
+
capture_output=True, text=True)
|
3123
|
+
else:
|
3124
|
+
result = subprocess.run(cmd, shell=True, check=True,
|
3125
|
+
capture_output=True, text=True)
|
3126
|
+
|
3127
|
+
if result.stdout and show_output:
|
2883
3128
|
print(f"ā
Output: {result.stdout}")
|
3129
|
+
return True, result.stdout, ""
|
2884
3130
|
except subprocess.CalledProcessError as e:
|
3131
|
+
error_output = e.stderr if e.stderr else str(e)
|
2885
3132
|
print(f"ā Command failed: {e}")
|
2886
|
-
|
2887
|
-
|
3133
|
+
print(f"ā Error: {error_output}")
|
3134
|
+
|
3135
|
+
# Check for common errors that we can fix automatically
|
3136
|
+
common_errors = [
|
3137
|
+
# Source command not found
|
3138
|
+
{
|
3139
|
+
"pattern": "source: not found",
|
3140
|
+
"fix": lambda cmd: cmd.replace("source ", ". ")
|
3141
|
+
},
|
3142
|
+
# Conda not found
|
3143
|
+
{
|
3144
|
+
"pattern": "conda: not found",
|
3145
|
+
"fix": lambda cmd: "apt-get update && apt-get install -y wget bzip2 && wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O /tmp/miniconda.sh && bash /tmp/miniconda.sh -b -p /opt/conda && export PATH=/opt/conda/bin:$PATH && echo 'export PATH=/opt/conda/bin:$PATH' >> ~/.bashrc && source ~/.bashrc && " + cmd
|
3146
|
+
},
|
3147
|
+
# File not found for chmod
|
3148
|
+
{
|
3149
|
+
"pattern": "chmod: cannot access",
|
3150
|
+
"fix": lambda cmd: "pwd && ls -la && echo 'File not found, checking current directory and contents'"
|
3151
|
+
},
|
3152
|
+
# No such file or directory
|
3153
|
+
{
|
3154
|
+
"pattern": "No such file or directory",
|
3155
|
+
"fix": lambda cmd: "pwd && ls -la && echo 'Checking current directory and contents'"
|
3156
|
+
},
|
3157
|
+
# Directory navigation issues
|
3158
|
+
{
|
3159
|
+
"pattern": "cd: no such file or directory",
|
3160
|
+
"fix": lambda cmd: "pwd && ls -la && echo 'Directory not found, checking current location'"
|
3161
|
+
},
|
3162
|
+
# File not found for execution
|
3163
|
+
{
|
3164
|
+
"pattern": "not found",
|
3165
|
+
"fix": lambda cmd: "pwd && ls -la && echo 'File not found, checking current location'"
|
3166
|
+
}
|
3167
|
+
]
|
3168
|
+
|
3169
|
+
# Check if any of the common errors match and apply automatic fix
|
3170
|
+
for error_info in common_errors:
|
3171
|
+
if error_info["pattern"] in error_output:
|
3172
|
+
print(f"š Detected common error: {error_info['pattern']}")
|
3173
|
+
fix_func = error_info["fix"]
|
3174
|
+
fixed_cmd = fix_func(cmd)
|
3175
|
+
print(f"š§ Applying automatic fix: {fixed_cmd}")
|
3176
|
+
|
3177
|
+
# Run the fixed command
|
3178
|
+
try:
|
3179
|
+
fix_result = subprocess.run(fixed_cmd, shell=True, check=True,
|
3180
|
+
capture_output=True, text=True)
|
3181
|
+
if fix_result.stdout:
|
3182
|
+
print(f"ā
Automatic fix output: {fix_result.stdout}")
|
3183
|
+
|
3184
|
+
# Retry the original command
|
3185
|
+
print(f"š Retrying original command: {cmd}")
|
3186
|
+
return run_command_with_basic_error_handling(cmd, show_output, retry_count + 1, max_retries)
|
3187
|
+
except subprocess.CalledProcessError as fix_e:
|
3188
|
+
print(f"ā Automatic fix failed: {fix_e}")
|
3189
|
+
# Continue with LLM debugging
|
3190
|
+
|
3191
|
+
# Call OpenAI for debugging
|
3192
|
+
print("š Attempting to debug the failed command with OpenAI...")
|
3193
|
+
try:
|
3194
|
+
# Get the current directory for context
|
3195
|
+
current_dir = os.getcwd()
|
3196
|
+
|
3197
|
+
# Call OpenAI for debugging
|
3198
|
+
print(f"š DEBUG: About to call call_openai_for_debug...")
|
3199
|
+
print(f"š DEBUG: Command: {cmd}")
|
3200
|
+
print(f"š DEBUG: Error output length: {len(error_output)}")
|
3201
|
+
print(f"š DEBUG: Current directory: {current_dir}")
|
3202
|
+
|
3203
|
+
fix_command = call_openai_for_debug(cmd, error_output, current_dir=current_dir)
|
3204
|
+
|
3205
|
+
print(f"š DEBUG: call_openai_for_debug returned: {fix_command}")
|
3206
|
+
|
3207
|
+
if fix_command:
|
3208
|
+
print(f"š§ OpenAI suggested fix command: {fix_command}")
|
3209
|
+
|
3210
|
+
# Run the fix command
|
3211
|
+
print(f"š Running suggested fix command: {fix_command}")
|
3212
|
+
try:
|
3213
|
+
fix_result = subprocess.run(fix_command, shell=True, check=True,
|
3214
|
+
capture_output=True, text=True)
|
3215
|
+
if fix_result.stdout:
|
3216
|
+
print(f"ā
Fix command output: {fix_result.stdout}")
|
3217
|
+
|
3218
|
+
# Retry the original command
|
3219
|
+
print(f"š Retrying original command: {cmd}")
|
3220
|
+
return run_command_with_basic_error_handling(cmd, show_output, retry_count + 1, max_retries)
|
3221
|
+
except subprocess.CalledProcessError as fix_e:
|
3222
|
+
print(f"ā Fix command also failed: {fix_e}")
|
3223
|
+
return False, "", error_output
|
3224
|
+
else:
|
3225
|
+
print("ā No fix suggested by OpenAI")
|
3226
|
+
return False, "", error_output
|
3227
|
+
|
3228
|
+
except Exception as debug_e:
|
3229
|
+
print(f"ā LLM debugging failed: {debug_e}")
|
3230
|
+
return False, "", error_output
|
3231
|
+
|
3232
|
+
# Run each setup command
|
3233
|
+
for i, cmd in enumerate(setup_commands, 1):
|
3234
|
+
print(f"š Executing command {i}/{len(setup_commands)}: {cmd}")
|
3235
|
+
|
3236
|
+
# Check if this is a cd command and if the directory exists
|
3237
|
+
if cmd.strip().startswith("cd "):
|
3238
|
+
cd_parts = cmd.split(None, 1)
|
3239
|
+
if len(cd_parts) >= 2:
|
3240
|
+
target_dir = cd_parts[1].strip('"\'')
|
3241
|
+
print(f"š Checking if directory exists: {target_dir}")
|
3242
|
+
try:
|
3243
|
+
check_result = subprocess.run(f"test -d '{target_dir}'", shell=True,
|
3244
|
+
capture_output=True, text=True)
|
3245
|
+
if check_result.returncode != 0:
|
3246
|
+
print(f"ā ļø Directory does not exist: {target_dir}")
|
3247
|
+
print(f"š Current directory contents:")
|
3248
|
+
subprocess.run("pwd && ls -la", shell=True, check=False)
|
3249
|
+
|
3250
|
+
# Try to find similar directories
|
3251
|
+
print(f"š Looking for similar directories...")
|
3252
|
+
subprocess.run("find . -type d -name '*llama*' -o -name '*nano*' 2>/dev/null | head -10", shell=True, check=False)
|
3253
|
+
except Exception as e:
|
3254
|
+
print(f"ā ļø Could not check directory: {e}")
|
3255
|
+
|
3256
|
+
success, stdout, stderr = run_command_with_basic_error_handling(cmd, show_output=True)
|
3257
|
+
if not success:
|
3258
|
+
print(f"ā ļø Command {i} failed, but continuing with remaining commands...")
|
3259
|
+
|
3260
|
+
# If this was a cd command that failed, try to understand the directory structure
|
3261
|
+
if cmd.strip().startswith("cd ") and "No such file or directory" in stderr:
|
3262
|
+
print(f"š Analyzing directory structure after failed cd command...")
|
3263
|
+
subprocess.run("pwd && ls -la && echo '--- Parent directory ---' && ls -la ..", shell=True, check=False)
|
2888
3264
|
|
2889
3265
|
# Create SSH tunnel
|
2890
3266
|
with modal.forward(22, unencrypted=True) as tunnel:
|
@@ -3573,7 +3949,7 @@ def create_ssh_container_function(gpu_type="a10g", timeout_minutes=60, volume=No
|
|
3573
3949
|
"python3", "python3-pip", "build-essential", "tmux", "screen", "nano",
|
3574
3950
|
"gpg", "ca-certificates", "software-properties-common"
|
3575
3951
|
)
|
3576
|
-
.pip_install("uv", "modal") #
|
3952
|
+
.pip_install("uv", "modal", "requests", "openai") # Install required packages for LLM debugging
|
3577
3953
|
.run_commands(
|
3578
3954
|
# Create SSH directory
|
3579
3955
|
"mkdir -p /var/run/sshd",
|
@@ -3611,6 +3987,8 @@ def create_ssh_container_function(gpu_type="a10g", timeout_minutes=60, volume=No
|
|
3611
3987
|
memory=8192,
|
3612
3988
|
serialized=True,
|
3613
3989
|
volumes=volumes if volumes else None,
|
3990
|
+
# Include all required modules in container
|
3991
|
+
mounts=[modal.Mount.from_local_python_packages("requests", "openai")]
|
3614
3992
|
)
|
3615
3993
|
def ssh_container(ssh_password, repo_url=None, repo_name=None, setup_commands=None):
|
3616
3994
|
import subprocess
|
@@ -4147,7 +4525,7 @@ def prompt_for_gpu():
|
|
4147
4525
|
"""Display the GPU selection menu with current selection highlighted."""
|
4148
4526
|
print("\nš Available GPU Options:")
|
4149
4527
|
print("āāāāāāāāāāāāāāāā¬āāāāāāāāāā")
|
4150
|
-
print("ā
|
4528
|
+
print("ā GPU Type ā VRAM ā")
|
4151
4529
|
print("āāāāāāāāāāāāāāāā¼āāāāāāāāāā¤")
|
4152
4530
|
|
4153
4531
|
for i, gpu_type in enumerate(options):
|
@@ -4299,6 +4677,16 @@ if __name__ == "__main__":
|
|
4299
4677
|
else:
|
4300
4678
|
print("Setup Commands: Auto-detect from repository")
|
4301
4679
|
|
4680
|
+
# Confirm settings
|
4681
|
+
try:
|
4682
|
+
proceed = input("Proceed with these settings? (Y/n): ").strip().lower()
|
4683
|
+
if proceed in ('n', 'no'):
|
4684
|
+
print("š Operation cancelled by user.")
|
4685
|
+
sys.exit(0)
|
4686
|
+
except KeyboardInterrupt:
|
4687
|
+
print("\nš Operation cancelled by user.")
|
4688
|
+
sys.exit(0)
|
4689
|
+
|
4302
4690
|
# Interactive mode or missing required arguments
|
4303
4691
|
if args.interactive or not args.repo_url or not args.volume_name:
|
4304
4692
|
# Get repository URL if not provided
|