gitarsenal-cli 1.9.12 โ 1.9.13
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/requirements.txt +2 -1
- package/python/test_claude_fallback.py +118 -0
- package/python/test_modalSandboxScript.py +454 -83
- package/test_modalSandboxScript.py +450 -82
package/.venv_status.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"created":"2025-08-
|
|
1
|
+
{"created":"2025-08-06T07:22:37.370Z","packages":["modal","gitingest","requests"],"uv_version":"uv 0.8.4 (Homebrew 2025-07-30)"}
|
package/package.json
CHANGED
package/python/requirements.txt
CHANGED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Test script to verify Claude fallback functionality
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
# Add the current directory to the path so we can import the main module
|
|
10
|
+
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
11
|
+
|
|
12
|
+
def test_claude_fallback():
|
|
13
|
+
"""Test the Claude fallback functionality"""
|
|
14
|
+
print("๐งช Testing Claude fallback functionality...")
|
|
15
|
+
|
|
16
|
+
# Test 1: Check if anthropic is imported correctly
|
|
17
|
+
try:
|
|
18
|
+
from test_modalSandboxScript import anthropic
|
|
19
|
+
if anthropic is not None:
|
|
20
|
+
print("โ
Anthropic library imported successfully")
|
|
21
|
+
else:
|
|
22
|
+
print("โ ๏ธ Anthropic library not available")
|
|
23
|
+
except ImportError as e:
|
|
24
|
+
print(f"โ Failed to import anthropic: {e}")
|
|
25
|
+
return False
|
|
26
|
+
|
|
27
|
+
# Test 2: Check if ANTHROPIC_API_KEY is set
|
|
28
|
+
anthropic_api_key = os.environ.get("ANTHROPIC_API_KEY")
|
|
29
|
+
if anthropic_api_key:
|
|
30
|
+
print("โ
ANTHROPIC_API_KEY found in environment")
|
|
31
|
+
else:
|
|
32
|
+
print("โ ๏ธ ANTHROPIC_API_KEY not found in environment")
|
|
33
|
+
print("๐ก Set ANTHROPIC_API_KEY to test Claude functionality")
|
|
34
|
+
|
|
35
|
+
# Test 3: Test a simple Claude API call
|
|
36
|
+
if anthropic is not None and anthropic_api_key:
|
|
37
|
+
try:
|
|
38
|
+
client = anthropic.Anthropic(api_key=anthropic_api_key)
|
|
39
|
+
message = client.messages.create(
|
|
40
|
+
model="claude-3-5-sonnet-20241022",
|
|
41
|
+
max_tokens=50,
|
|
42
|
+
messages=[
|
|
43
|
+
{"role": "user", "content": "Hello, please respond with 'Claude is working!'"}
|
|
44
|
+
]
|
|
45
|
+
)
|
|
46
|
+
response = message.content[0].text.strip()
|
|
47
|
+
print(f"โ
Claude API test successful: {response}")
|
|
48
|
+
return True
|
|
49
|
+
except Exception as e:
|
|
50
|
+
print(f"โ Claude API test failed: {e}")
|
|
51
|
+
return False
|
|
52
|
+
else:
|
|
53
|
+
print("โ ๏ธ Skipping Claude API test (missing library or API key)")
|
|
54
|
+
return False
|
|
55
|
+
|
|
56
|
+
def test_openai_fallback():
|
|
57
|
+
"""Test the OpenAI fallback functionality"""
|
|
58
|
+
print("\n๐งช Testing OpenAI fallback functionality...")
|
|
59
|
+
|
|
60
|
+
# Test 1: Check if OpenAI API key is set
|
|
61
|
+
openai_api_key = os.environ.get("OPENAI_API_KEY")
|
|
62
|
+
if openai_api_key:
|
|
63
|
+
print("โ
OPENAI_API_KEY found in environment")
|
|
64
|
+
else:
|
|
65
|
+
print("โ ๏ธ OPENAI_API_KEY not found in environment")
|
|
66
|
+
print("๐ก Set OPENAI_API_KEY to test OpenAI functionality")
|
|
67
|
+
|
|
68
|
+
# Test 2: Test a simple OpenAI API call
|
|
69
|
+
if openai_api_key:
|
|
70
|
+
try:
|
|
71
|
+
import openai
|
|
72
|
+
client = openai.OpenAI(api_key=openai_api_key)
|
|
73
|
+
response = client.chat.completions.create(
|
|
74
|
+
model="gpt-3.5-turbo",
|
|
75
|
+
messages=[
|
|
76
|
+
{"role": "user", "content": "Hello, please respond with 'OpenAI is working!'"}
|
|
77
|
+
],
|
|
78
|
+
max_tokens=50
|
|
79
|
+
)
|
|
80
|
+
response_text = response.choices[0].message.content.strip()
|
|
81
|
+
print(f"โ
OpenAI API test successful: {response_text}")
|
|
82
|
+
return True
|
|
83
|
+
except Exception as e:
|
|
84
|
+
print(f"โ OpenAI API test failed: {e}")
|
|
85
|
+
return False
|
|
86
|
+
else:
|
|
87
|
+
print("โ ๏ธ Skipping OpenAI API test (missing API key)")
|
|
88
|
+
return False
|
|
89
|
+
|
|
90
|
+
def main():
|
|
91
|
+
"""Main test function"""
|
|
92
|
+
print("๐ Starting Claude fallback tests...")
|
|
93
|
+
print("=" * 50)
|
|
94
|
+
|
|
95
|
+
claude_success = test_claude_fallback()
|
|
96
|
+
openai_success = test_openai_fallback()
|
|
97
|
+
|
|
98
|
+
print("\n" + "=" * 50)
|
|
99
|
+
print("๐ Test Results:")
|
|
100
|
+
print(f"Claude API: {'โ
Working' if claude_success else 'โ Failed'}")
|
|
101
|
+
print(f"OpenAI API: {'โ
Working' if openai_success else 'โ Failed'}")
|
|
102
|
+
|
|
103
|
+
if claude_success and openai_success:
|
|
104
|
+
print("\n๐ Both APIs are working! Claude fallback is ready.")
|
|
105
|
+
elif claude_success:
|
|
106
|
+
print("\nโ
Claude API is working! Can be used as fallback.")
|
|
107
|
+
elif openai_success:
|
|
108
|
+
print("\nโ
OpenAI API is working! Claude fallback will be used when OpenAI fails.")
|
|
109
|
+
else:
|
|
110
|
+
print("\nโ Neither API is working. Please check your API keys.")
|
|
111
|
+
|
|
112
|
+
print("\n๐ก To test the fallback functionality:")
|
|
113
|
+
print("1. Set both OPENAI_API_KEY and ANTHROPIC_API_KEY")
|
|
114
|
+
print("2. Run the main script with a failing command")
|
|
115
|
+
print("3. Watch for '๐ Trying Claude-4-Sonnet as fallback...' messages")
|
|
116
|
+
|
|
117
|
+
if __name__ == "__main__":
|
|
118
|
+
main()
|
|
@@ -15,7 +15,7 @@ import uuid
|
|
|
15
15
|
import signal
|
|
16
16
|
from pathlib import Path
|
|
17
17
|
import modal
|
|
18
|
-
|
|
18
|
+
import anthropic
|
|
19
19
|
# Import authentication manager
|
|
20
20
|
try:
|
|
21
21
|
from auth_manager import AuthManager
|
|
@@ -516,21 +516,57 @@ class PersistentShell:
|
|
|
516
516
|
REMOVE_COMMAND: <reason>
|
|
517
517
|
"""
|
|
518
518
|
|
|
519
|
-
#
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
519
|
+
# Try OpenAI API first
|
|
520
|
+
try:
|
|
521
|
+
import openai
|
|
522
|
+
client = openai.OpenAI(api_key=api_key)
|
|
523
|
+
|
|
524
|
+
response = client.chat.completions.create(
|
|
525
|
+
model="gpt-4.1",
|
|
526
|
+
messages=[
|
|
527
|
+
{"role": "system", "content": "You are a helpful assistant that suggests alternative commands that don't require user input."},
|
|
528
|
+
{"role": "user", "content": prompt}
|
|
529
|
+
],
|
|
530
|
+
max_tokens=150,
|
|
531
|
+
temperature=0.7
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
response_text = response.choices[0].message.content.strip()
|
|
535
|
+
|
|
536
|
+
except Exception as e:
|
|
537
|
+
print(f"โ ๏ธ OpenAI API call failed: {e}")
|
|
538
|
+
|
|
539
|
+
# Try Claude as fallback if available
|
|
540
|
+
if anthropic is not None:
|
|
541
|
+
print("๐ Trying Claude-4-Sonnet as fallback for alternative command suggestion...")
|
|
542
|
+
try:
|
|
543
|
+
# Get Anthropic API key
|
|
544
|
+
anthropic_api_key = os.environ.get("ANTHROPIC_API_KEY")
|
|
545
|
+
if not anthropic_api_key:
|
|
546
|
+
print("โ ๏ธ No ANTHROPIC_API_KEY found in environment")
|
|
547
|
+
return None
|
|
548
|
+
|
|
549
|
+
# Create Anthropic client
|
|
550
|
+
client = anthropic.Anthropic(api_key=anthropic_api_key)
|
|
551
|
+
|
|
552
|
+
print("๐ค Calling Claude-4-Sonnet for alternative command suggestion...")
|
|
553
|
+
message = client.messages.create(
|
|
554
|
+
model="claude-3-5-sonnet-20241022",
|
|
555
|
+
max_tokens=150,
|
|
556
|
+
messages=[
|
|
557
|
+
{"role": "user", "content": prompt}
|
|
558
|
+
]
|
|
559
|
+
)
|
|
560
|
+
|
|
561
|
+
response_text = message.content[0].text.strip()
|
|
562
|
+
print("โ
Claude alternative command suggestion completed")
|
|
563
|
+
|
|
564
|
+
except Exception as claude_error:
|
|
565
|
+
print(f"โ Claude API call failed: {claude_error}")
|
|
566
|
+
return None
|
|
567
|
+
else:
|
|
568
|
+
print("โ ๏ธ Claude fallback not available (anthropic library not installed)")
|
|
569
|
+
return None
|
|
534
570
|
|
|
535
571
|
# Check if the response suggests removing the command
|
|
536
572
|
if response_text.startswith("REMOVE_COMMAND:"):
|
|
@@ -967,23 +1003,59 @@ class CommandListManager:
|
|
|
967
1003
|
RUN: <reason>
|
|
968
1004
|
"""
|
|
969
1005
|
|
|
970
|
-
#
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
1006
|
+
# Try OpenAI API first
|
|
1007
|
+
try:
|
|
1008
|
+
import openai
|
|
1009
|
+
client = openai.OpenAI(api_key=api_key)
|
|
1010
|
+
|
|
1011
|
+
print("๐ Analyzing if original command should be skipped...")
|
|
1012
|
+
|
|
1013
|
+
response = client.chat.completions.create(
|
|
1014
|
+
model="gpt-3.5-turbo",
|
|
1015
|
+
messages=[
|
|
1016
|
+
{"role": "system", "content": "You are a helpful assistant that analyzes command execution."},
|
|
1017
|
+
{"role": "user", "content": prompt}
|
|
1018
|
+
],
|
|
1019
|
+
max_tokens=100,
|
|
1020
|
+
temperature=0.3
|
|
1021
|
+
)
|
|
1022
|
+
|
|
1023
|
+
response_text = response.choices[0].message.content.strip()
|
|
1024
|
+
|
|
1025
|
+
except Exception as e:
|
|
1026
|
+
print(f"โ ๏ธ OpenAI API call failed: {e}")
|
|
1027
|
+
|
|
1028
|
+
# Try Claude as fallback if available
|
|
1029
|
+
if anthropic is not None:
|
|
1030
|
+
print("๐ Trying Claude-4-Sonnet as fallback for command skip analysis...")
|
|
1031
|
+
try:
|
|
1032
|
+
# Get Anthropic API key
|
|
1033
|
+
anthropic_api_key = os.environ.get("ANTHROPIC_API_KEY")
|
|
1034
|
+
if not anthropic_api_key:
|
|
1035
|
+
print("โ ๏ธ No ANTHROPIC_API_KEY found in environment")
|
|
1036
|
+
return False, "No API key available"
|
|
1037
|
+
|
|
1038
|
+
# Create Anthropic client
|
|
1039
|
+
client = anthropic.Anthropic(api_key=anthropic_api_key)
|
|
1040
|
+
|
|
1041
|
+
print("๐ค Calling Claude-4-Sonnet for command skip analysis...")
|
|
1042
|
+
message = client.messages.create(
|
|
1043
|
+
model="claude-3-5-sonnet-20241022",
|
|
1044
|
+
max_tokens=100,
|
|
1045
|
+
messages=[
|
|
1046
|
+
{"role": "user", "content": prompt}
|
|
1047
|
+
]
|
|
1048
|
+
)
|
|
1049
|
+
|
|
1050
|
+
response_text = message.content[0].text.strip()
|
|
1051
|
+
print("โ
Claude command skip analysis completed")
|
|
1052
|
+
|
|
1053
|
+
except Exception as claude_error:
|
|
1054
|
+
print(f"โ Claude API call failed: {claude_error}")
|
|
1055
|
+
return False, f"Error: {claude_error}"
|
|
1056
|
+
else:
|
|
1057
|
+
print("โ ๏ธ Claude fallback not available (anthropic library not installed)")
|
|
1058
|
+
return False, "No API key available"
|
|
987
1059
|
|
|
988
1060
|
# Parse the response
|
|
989
1061
|
if response_text.startswith("SKIP:"):
|
|
@@ -1111,24 +1183,60 @@ class CommandListManager:
|
|
|
1111
1183
|
Only include commands that need changes (SKIP, MODIFY, ADD_AFTER), not KEEP actions.
|
|
1112
1184
|
"""
|
|
1113
1185
|
|
|
1114
|
-
#
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1186
|
+
# Try OpenAI API first
|
|
1187
|
+
try:
|
|
1188
|
+
import openai
|
|
1189
|
+
import json
|
|
1190
|
+
client = openai.OpenAI(api_key=api_key)
|
|
1191
|
+
|
|
1192
|
+
print("๐ Analyzing command list for optimizations...")
|
|
1193
|
+
|
|
1194
|
+
response = client.chat.completions.create(
|
|
1195
|
+
model="gpt-4.1", # Use a more capable model for this complex task
|
|
1196
|
+
messages=[
|
|
1197
|
+
{"role": "system", "content": "You are a helpful assistant that analyzes and optimizes command lists."},
|
|
1198
|
+
{"role": "user", "content": prompt}
|
|
1199
|
+
],
|
|
1200
|
+
max_tokens=1000,
|
|
1201
|
+
temperature=0.2
|
|
1202
|
+
)
|
|
1203
|
+
|
|
1204
|
+
response_text = response.choices[0].message.content.strip()
|
|
1205
|
+
|
|
1206
|
+
except Exception as e:
|
|
1207
|
+
print(f"โ ๏ธ OpenAI API call failed: {e}")
|
|
1208
|
+
|
|
1209
|
+
# Try Claude as fallback if available
|
|
1210
|
+
if anthropic is not None:
|
|
1211
|
+
print("๐ Trying Claude-4-Sonnet as fallback for command list analysis...")
|
|
1212
|
+
try:
|
|
1213
|
+
# Get Anthropic API key
|
|
1214
|
+
anthropic_api_key = os.environ.get("ANTHROPIC_API_KEY")
|
|
1215
|
+
if not anthropic_api_key:
|
|
1216
|
+
print("โ ๏ธ No ANTHROPIC_API_KEY found in environment")
|
|
1217
|
+
return False
|
|
1218
|
+
|
|
1219
|
+
# Create Anthropic client
|
|
1220
|
+
client = anthropic.Anthropic(api_key=anthropic_api_key)
|
|
1221
|
+
|
|
1222
|
+
print("๐ค Calling Claude-4-Sonnet for command list analysis...")
|
|
1223
|
+
message = client.messages.create(
|
|
1224
|
+
model="claude-3-5-sonnet-20241022",
|
|
1225
|
+
max_tokens=1000,
|
|
1226
|
+
messages=[
|
|
1227
|
+
{"role": "user", "content": prompt}
|
|
1228
|
+
]
|
|
1229
|
+
)
|
|
1230
|
+
|
|
1231
|
+
response_text = message.content[0].text.strip()
|
|
1232
|
+
print("โ
Claude command list analysis completed")
|
|
1233
|
+
|
|
1234
|
+
except Exception as claude_error:
|
|
1235
|
+
print(f"โ Claude API call failed: {claude_error}")
|
|
1236
|
+
return False
|
|
1237
|
+
else:
|
|
1238
|
+
print("โ ๏ธ Claude fallback not available (anthropic library not installed)")
|
|
1239
|
+
return False
|
|
1132
1240
|
|
|
1133
1241
|
# Extract JSON from the response
|
|
1134
1242
|
try:
|
|
@@ -1780,8 +1888,186 @@ Do not provide any explanations, just the exact command to run.
|
|
|
1780
1888
|
last_error = error
|
|
1781
1889
|
|
|
1782
1890
|
if not result:
|
|
1783
|
-
print(f"โ All model attempts failed. Last error: {last_error}")
|
|
1784
|
-
|
|
1891
|
+
print(f"โ All OpenAI model attempts failed. Last error: {last_error}")
|
|
1892
|
+
|
|
1893
|
+
# Try Claude as fallback if available
|
|
1894
|
+
if anthropic is not None:
|
|
1895
|
+
print("๐ Trying Claude-4-Sonnet as fallback...")
|
|
1896
|
+
try:
|
|
1897
|
+
# Get Anthropic API key
|
|
1898
|
+
anthropic_api_key = os.environ.get("ANTHROPIC_API_KEY")
|
|
1899
|
+
if not anthropic_api_key:
|
|
1900
|
+
print("โ ๏ธ No ANTHROPIC_API_KEY found in environment")
|
|
1901
|
+
return None
|
|
1902
|
+
|
|
1903
|
+
# Create Anthropic client
|
|
1904
|
+
client = anthropic.Anthropic(api_key=anthropic_api_key)
|
|
1905
|
+
|
|
1906
|
+
# Prepare the same prompt for Claude
|
|
1907
|
+
claude_prompt = f"""
|
|
1908
|
+
I'm trying to run the following command in a Linux environment:
|
|
1909
|
+
|
|
1910
|
+
```
|
|
1911
|
+
{command}
|
|
1912
|
+
```
|
|
1913
|
+
|
|
1914
|
+
But it failed with this error:
|
|
1915
|
+
|
|
1916
|
+
```
|
|
1917
|
+
{error_output}
|
|
1918
|
+
```
|
|
1919
|
+
{system_info}
|
|
1920
|
+
{directory_context}
|
|
1921
|
+
{file_context}
|
|
1922
|
+
|
|
1923
|
+
AVAILABLE CREDENTIALS:
|
|
1924
|
+
{auth_context}
|
|
1925
|
+
|
|
1926
|
+
Please analyze the error and provide ONLY a single terminal command that would fix the issue.
|
|
1927
|
+
Consider the current directory, system information, directory contents, and available credentials carefully before suggesting a solution.
|
|
1928
|
+
|
|
1929
|
+
IMPORTANT GUIDELINES:
|
|
1930
|
+
1. For any commands that might ask for yes/no confirmation, use the appropriate non-interactive flag:
|
|
1931
|
+
- For apt/apt-get: use -y or --yes
|
|
1932
|
+
- For rm: use -f or --force
|
|
1933
|
+
|
|
1934
|
+
2. If the error indicates a file is not found:
|
|
1935
|
+
- FIRST try to search for the file using: find . -name "filename" -type f 2>/dev/null
|
|
1936
|
+
- If found, navigate to that directory using: cd /path/to/directory
|
|
1937
|
+
- If not found, then consider creating the file or installing missing packages
|
|
1938
|
+
|
|
1939
|
+
3. For missing packages or dependencies:
|
|
1940
|
+
- Use pip install for Python packages
|
|
1941
|
+
- Use apt-get install -y for system packages
|
|
1942
|
+
- Use npm install for Node.js packages
|
|
1943
|
+
|
|
1944
|
+
4. For authentication issues:
|
|
1945
|
+
- Analyze the error to determine what type of authentication is needed
|
|
1946
|
+
- ALWAYS use the actual credential values from the AVAILABLE CREDENTIALS section above (NOT placeholders)
|
|
1947
|
+
- Look for the specific API key or token needed in the auth_context and use its exact value
|
|
1948
|
+
- Common patterns:
|
|
1949
|
+
* wandb errors: use wandb login with the actual WANDB_API_KEY value from auth_context
|
|
1950
|
+
* huggingface errors: use huggingface-cli login with the actual HF_TOKEN or HUGGINGFACE_TOKEN value from auth_context
|
|
1951
|
+
* github errors: configure git credentials with the actual GITHUB_TOKEN value from auth_context
|
|
1952
|
+
* kaggle errors: create ~/.kaggle/kaggle.json with the actual KAGGLE_USERNAME and KAGGLE_KEY values from auth_context
|
|
1953
|
+
* API errors: export the appropriate API key as environment variable using the actual value from auth_context
|
|
1954
|
+
|
|
1955
|
+
5. Environment variable exports:
|
|
1956
|
+
- Use export commands for API keys that need to be in environment
|
|
1957
|
+
- ALWAYS use the actual credential values from auth_context, never use placeholders like "YOUR_API_KEY"
|
|
1958
|
+
- Example: export OPENAI_API_KEY="sk-..." (using the actual key from auth_context)
|
|
1959
|
+
|
|
1960
|
+
6. CRITICAL: When using any API key, token, or credential:
|
|
1961
|
+
- Find the exact value in the AVAILABLE CREDENTIALS section
|
|
1962
|
+
- Use that exact value in your command
|
|
1963
|
+
- Do not use generic placeholders or dummy values
|
|
1964
|
+
- The auth_context contains real, usable credentials
|
|
1965
|
+
|
|
1966
|
+
7. For Git SSH authentication failures:
|
|
1967
|
+
- If the error contains "Host key verification failed" or "Could not read from remote repository"
|
|
1968
|
+
- ALWAYS convert SSH URLs to HTTPS URLs for public repositories
|
|
1969
|
+
- Replace git@github.com:username/repo.git with https://github.com/username/repo.git
|
|
1970
|
+
- This works for public repositories without authentication
|
|
1971
|
+
- Example: git clone https://github.com/xg-chu/ARTalk.git
|
|
1972
|
+
|
|
1973
|
+
Do not provide any explanations, just the exact command to run.
|
|
1974
|
+
"""
|
|
1975
|
+
|
|
1976
|
+
print("๐ค Calling Claude-4-Sonnet to debug the failed command...")
|
|
1977
|
+
message = client.messages.create(
|
|
1978
|
+
model="claude-3-5-sonnet-20241022",
|
|
1979
|
+
max_tokens=300,
|
|
1980
|
+
messages=[
|
|
1981
|
+
{"role": "user", "content": claude_prompt}
|
|
1982
|
+
]
|
|
1983
|
+
)
|
|
1984
|
+
|
|
1985
|
+
fix_command = message.content[0].text.strip()
|
|
1986
|
+
print(f"๐ DEBUG: Raw Claude response content: {fix_command}")
|
|
1987
|
+
|
|
1988
|
+
# Process the response similar to OpenAI
|
|
1989
|
+
original_response = fix_command
|
|
1990
|
+
|
|
1991
|
+
# Extract just the command if it's wrapped in backticks or explanation
|
|
1992
|
+
if "```" in fix_command:
|
|
1993
|
+
# Extract content between backticks
|
|
1994
|
+
import re
|
|
1995
|
+
code_blocks = re.findall(r'```(?:bash|sh)?\s*(.*?)\s*```', fix_command, re.DOTALL)
|
|
1996
|
+
if code_blocks:
|
|
1997
|
+
fix_command = code_blocks[0].strip()
|
|
1998
|
+
print(f"โ
Extracted command from code block: {fix_command}")
|
|
1999
|
+
|
|
2000
|
+
# If the response still has explanatory text, try to extract just the command
|
|
2001
|
+
if len(fix_command.split('\n')) > 1:
|
|
2002
|
+
# First try to find lines that look like commands (start with common command prefixes)
|
|
2003
|
+
command_prefixes = ['sudo', 'apt', 'pip', 'npm', 'yarn', 'git', 'cd', 'mv', 'cp', 'rm', 'mkdir', 'touch',
|
|
2004
|
+
'chmod', 'chown', 'echo', 'cat', 'python', 'python3', 'node', 'export',
|
|
2005
|
+
'curl', 'wget', 'docker', 'make', 'gcc', 'g++', 'javac', 'java',
|
|
2006
|
+
'conda', 'uv', 'poetry', 'nvm', 'rbenv', 'pyenv', 'rustup']
|
|
2007
|
+
|
|
2008
|
+
# Check for lines that start with common command prefixes
|
|
2009
|
+
command_lines = [line.strip() for line in fix_command.split('\n')
|
|
2010
|
+
if any(line.strip().startswith(prefix) for prefix in command_prefixes)]
|
|
2011
|
+
|
|
2012
|
+
if command_lines:
|
|
2013
|
+
# Use the first command line found
|
|
2014
|
+
fix_command = command_lines[0]
|
|
2015
|
+
print(f"โ
Identified command by prefix: {fix_command}")
|
|
2016
|
+
else:
|
|
2017
|
+
# Try to find lines that look like commands (contain common shell patterns)
|
|
2018
|
+
shell_patterns = [' | ', ' > ', ' >> ', ' && ', ' || ', ' ; ', '$(', '`', ' -y ', ' --yes ']
|
|
2019
|
+
command_lines = [line.strip() for line in fix_command.split('\n')
|
|
2020
|
+
if any(pattern in line for pattern in shell_patterns)]
|
|
2021
|
+
|
|
2022
|
+
if command_lines:
|
|
2023
|
+
# Use the first command line found
|
|
2024
|
+
fix_command = command_lines[0]
|
|
2025
|
+
print(f"โ
Identified command by shell pattern: {fix_command}")
|
|
2026
|
+
else:
|
|
2027
|
+
# Fall back to the shortest non-empty line as it's likely the command
|
|
2028
|
+
lines = [line.strip() for line in fix_command.split('\n') if line.strip()]
|
|
2029
|
+
if lines:
|
|
2030
|
+
# Exclude very short lines that are likely not commands
|
|
2031
|
+
valid_lines = [line for line in lines if len(line) > 5]
|
|
2032
|
+
if valid_lines:
|
|
2033
|
+
fix_command = min(valid_lines, key=len)
|
|
2034
|
+
else:
|
|
2035
|
+
fix_command = min(lines, key=len)
|
|
2036
|
+
print(f"โ
Selected shortest line as command: {fix_command}")
|
|
2037
|
+
|
|
2038
|
+
# Clean up the command - remove any trailing periods or quotes
|
|
2039
|
+
fix_command = fix_command.rstrip('.;"\'')
|
|
2040
|
+
|
|
2041
|
+
# Remove common prefixes that LLMs sometimes add
|
|
2042
|
+
prefixes_to_remove = [
|
|
2043
|
+
"Run: ", "Execute: ", "Try: ", "Command: ", "Fix: ", "Solution: ",
|
|
2044
|
+
"You should run: ", "You can run: ", "You need to run: "
|
|
2045
|
+
]
|
|
2046
|
+
for prefix in prefixes_to_remove:
|
|
2047
|
+
if fix_command.startswith(prefix):
|
|
2048
|
+
fix_command = fix_command[len(prefix):].strip()
|
|
2049
|
+
print(f"โ
Removed prefix: {prefix}")
|
|
2050
|
+
break
|
|
2051
|
+
|
|
2052
|
+
# If the command is still multi-line or very long, it might not be a valid command
|
|
2053
|
+
if len(fix_command.split('\n')) > 1 or len(fix_command) > 500:
|
|
2054
|
+
print("โ ๏ธ Extracted command appears invalid (multi-line or too long)")
|
|
2055
|
+
print("๐ Original response from Claude:")
|
|
2056
|
+
print("-" * 60)
|
|
2057
|
+
print(original_response)
|
|
2058
|
+
print("-" * 60)
|
|
2059
|
+
print("โ ๏ธ Using best guess for command")
|
|
2060
|
+
|
|
2061
|
+
print(f"๐ง Claude suggested fix: {fix_command}")
|
|
2062
|
+
print(f"๐ DEBUG: Returning Claude fix command: {fix_command}")
|
|
2063
|
+
return fix_command
|
|
2064
|
+
|
|
2065
|
+
except Exception as e:
|
|
2066
|
+
print(f"โ Claude API call failed: {e}")
|
|
2067
|
+
return None
|
|
2068
|
+
else:
|
|
2069
|
+
print("โ ๏ธ Claude fallback not available (anthropic library not installed)")
|
|
2070
|
+
return None
|
|
1785
2071
|
|
|
1786
2072
|
# Process the response
|
|
1787
2073
|
try:
|
|
@@ -1988,7 +2274,65 @@ Provide fixes for all {len(failed_commands)} failed commands:"""
|
|
|
1988
2274
|
return fixes
|
|
1989
2275
|
else:
|
|
1990
2276
|
print(f"โ OpenAI API error: {response.status_code} - {response.text}")
|
|
1991
|
-
|
|
2277
|
+
|
|
2278
|
+
# Try Claude as fallback if available
|
|
2279
|
+
if anthropic is not None:
|
|
2280
|
+
print("๐ Trying Claude-4-Sonnet as fallback for batch debugging...")
|
|
2281
|
+
try:
|
|
2282
|
+
# Get Anthropic API key
|
|
2283
|
+
anthropic_api_key = os.environ.get("ANTHROPIC_API_KEY")
|
|
2284
|
+
if not anthropic_api_key:
|
|
2285
|
+
print("โ ๏ธ No ANTHROPIC_API_KEY found in environment")
|
|
2286
|
+
return []
|
|
2287
|
+
|
|
2288
|
+
# Create Anthropic client
|
|
2289
|
+
client = anthropic.Anthropic(api_key=anthropic_api_key)
|
|
2290
|
+
|
|
2291
|
+
print("๐ค Calling Claude-4-Sonnet for batch debugging...")
|
|
2292
|
+
message = client.messages.create(
|
|
2293
|
+
model="claude-3-5-sonnet-20241022",
|
|
2294
|
+
max_tokens=1000,
|
|
2295
|
+
messages=[
|
|
2296
|
+
{"role": "user", "content": prompt}
|
|
2297
|
+
]
|
|
2298
|
+
)
|
|
2299
|
+
|
|
2300
|
+
content = message.content[0].text
|
|
2301
|
+
print(f"โ
Claude batch analysis completed")
|
|
2302
|
+
|
|
2303
|
+
# Parse the response to extract fix commands
|
|
2304
|
+
fixes = []
|
|
2305
|
+
for i in range(1, len(failed_commands) + 1):
|
|
2306
|
+
fix_pattern = f"FIX_COMMAND_{i}: (.+)"
|
|
2307
|
+
reason_pattern = f"REASON_{i}: (.+)"
|
|
2308
|
+
|
|
2309
|
+
fix_match = re.search(fix_pattern, content, re.MULTILINE)
|
|
2310
|
+
reason_match = re.search(reason_pattern, content, re.MULTILINE)
|
|
2311
|
+
|
|
2312
|
+
if fix_match:
|
|
2313
|
+
fix_command = fix_match.group(1).strip()
|
|
2314
|
+
reason = reason_match.group(1).strip() if reason_match else "Claude suggested fix"
|
|
2315
|
+
|
|
2316
|
+
# Clean up the fix command
|
|
2317
|
+
if fix_command.startswith('`') and fix_command.endswith('`'):
|
|
2318
|
+
fix_command = fix_command[1:-1]
|
|
2319
|
+
|
|
2320
|
+
fixes.append({
|
|
2321
|
+
'original_command': failed_commands[i-1]['command'],
|
|
2322
|
+
'fix_command': fix_command,
|
|
2323
|
+
'reason': reason,
|
|
2324
|
+
'command_index': i-1
|
|
2325
|
+
})
|
|
2326
|
+
|
|
2327
|
+
print(f"๐ง Generated {len(fixes)} fix commands from Claude batch analysis")
|
|
2328
|
+
return fixes
|
|
2329
|
+
|
|
2330
|
+
except Exception as e:
|
|
2331
|
+
print(f"โ Claude API call failed: {e}")
|
|
2332
|
+
return []
|
|
2333
|
+
else:
|
|
2334
|
+
print("โ ๏ธ Claude fallback not available (anthropic library not installed)")
|
|
2335
|
+
return []
|
|
1992
2336
|
|
|
1993
2337
|
except Exception as e:
|
|
1994
2338
|
print(f"โ Error during batch debugging: {e}")
|
|
@@ -3565,21 +3909,57 @@ Return only the JSON array, no other text.
|
|
|
3565
3909
|
print("โ ๏ธ No OpenAI API key available for command preprocessing")
|
|
3566
3910
|
return setup_commands
|
|
3567
3911
|
|
|
3568
|
-
#
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3912
|
+
# Try OpenAI API first
|
|
3913
|
+
try:
|
|
3914
|
+
import openai
|
|
3915
|
+
client = openai.OpenAI(api_key=api_key)
|
|
3916
|
+
|
|
3917
|
+
response = client.chat.completions.create(
|
|
3918
|
+
model="gpt-3.5-turbo",
|
|
3919
|
+
messages=[
|
|
3920
|
+
{"role": "system", "content": "You are a command preprocessing assistant that modifies setup commands to use available credentials and make them non-interactive."},
|
|
3921
|
+
{"role": "user", "content": prompt}
|
|
3922
|
+
],
|
|
3923
|
+
temperature=0.1,
|
|
3924
|
+
max_tokens=2000
|
|
3925
|
+
)
|
|
3926
|
+
|
|
3927
|
+
result = response.choices[0].message.content.strip()
|
|
3928
|
+
|
|
3929
|
+
except Exception as e:
|
|
3930
|
+
print(f"โ ๏ธ OpenAI API call failed: {e}")
|
|
3931
|
+
|
|
3932
|
+
# Try Claude as fallback if available
|
|
3933
|
+
if anthropic is not None:
|
|
3934
|
+
print("๐ Trying Claude-4-Sonnet as fallback for command preprocessing...")
|
|
3935
|
+
try:
|
|
3936
|
+
# Get Anthropic API key
|
|
3937
|
+
anthropic_api_key = os.environ.get("ANTHROPIC_API_KEY")
|
|
3938
|
+
if not anthropic_api_key:
|
|
3939
|
+
print("โ ๏ธ No ANTHROPIC_API_KEY found in environment")
|
|
3940
|
+
return fallback_preprocess_commands(setup_commands, stored_credentials)
|
|
3941
|
+
|
|
3942
|
+
# Create Anthropic client
|
|
3943
|
+
client = anthropic.Anthropic(api_key=anthropic_api_key)
|
|
3944
|
+
|
|
3945
|
+
print("๐ค Calling Claude-4-Sonnet for command preprocessing...")
|
|
3946
|
+
message = client.messages.create(
|
|
3947
|
+
model="claude-3-5-sonnet-20241022",
|
|
3948
|
+
max_tokens=2000,
|
|
3949
|
+
messages=[
|
|
3950
|
+
{"role": "user", "content": prompt}
|
|
3951
|
+
]
|
|
3952
|
+
)
|
|
3953
|
+
|
|
3954
|
+
result = message.content[0].text.strip()
|
|
3955
|
+
print("โ
Claude preprocessing completed")
|
|
3956
|
+
|
|
3957
|
+
except Exception as claude_error:
|
|
3958
|
+
print(f"โ Claude API call failed: {claude_error}")
|
|
3959
|
+
return fallback_preprocess_commands(setup_commands, stored_credentials)
|
|
3960
|
+
else:
|
|
3961
|
+
print("โ ๏ธ Claude fallback not available (anthropic library not installed)")
|
|
3962
|
+
return fallback_preprocess_commands(setup_commands, stored_credentials)
|
|
3583
3963
|
|
|
3584
3964
|
# Debug: Print the raw response
|
|
3585
3965
|
print(f"๐ LLM Response: {result[:200]}...")
|
|
@@ -3822,7 +4202,6 @@ if __name__ == "__main__":
|
|
|
3822
4202
|
parser = argparse.ArgumentParser()
|
|
3823
4203
|
parser.add_argument('--gpu', type=str, help='GPU type (e.g., A10G, T4, A100-80GB). If not provided, will prompt for GPU selection.')
|
|
3824
4204
|
parser.add_argument('--repo-url', type=str, help='Repository URL to clone')
|
|
3825
|
-
parser.add_argument('--repo-name', type=str, help='Repository name override')
|
|
3826
4205
|
parser.add_argument('--setup-commands', type=str, nargs='+', help='Setup commands to run (deprecated)')
|
|
3827
4206
|
parser.add_argument('--setup-commands-json', type=str, help='Setup commands as JSON array')
|
|
3828
4207
|
parser.add_argument('--commands-file', type=str, help='Path to file containing setup commands (one per line)')
|
|
@@ -3930,19 +4309,7 @@ if __name__ == "__main__":
|
|
|
3930
4309
|
print(f"Setup Commands: {len(args.setup_commands)} custom commands")
|
|
3931
4310
|
else:
|
|
3932
4311
|
print("Setup Commands: Auto-detect from repository")
|
|
3933
|
-
|
|
3934
|
-
# Confirm settings
|
|
3935
|
-
if not args.yes:
|
|
3936
|
-
try:
|
|
3937
|
-
proceed = input("Proceed with these settings? (Y/n): ").strip().lower()
|
|
3938
|
-
if proceed in ('n', 'no'):
|
|
3939
|
-
print("๐ Operation cancelled by user.")
|
|
3940
|
-
sys.exit(0)
|
|
3941
|
-
except KeyboardInterrupt:
|
|
3942
|
-
print("\n๐ Operation cancelled by user.")
|
|
3943
|
-
sys.exit(0)
|
|
3944
|
-
else:
|
|
3945
|
-
print("โ
Skipping confirmation prompt (--yes flag used)")
|
|
4312
|
+
|
|
3946
4313
|
|
|
3947
4314
|
# Interactive mode or missing required arguments
|
|
3948
4315
|
if args.interactive or not args.repo_url or not args.volume_name:
|
|
@@ -4112,8 +4479,12 @@ if __name__ == "__main__":
|
|
|
4112
4479
|
interactive=args.interactive
|
|
4113
4480
|
)
|
|
4114
4481
|
except KeyboardInterrupt:
|
|
4482
|
+
# print("\n\n๐ Execution interrupted")
|
|
4483
|
+
# print("๐งน Cleaning up resources...")
|
|
4115
4484
|
cleanup_modal_token()
|
|
4116
4485
|
sys.exit(1)
|
|
4117
4486
|
except Exception as e:
|
|
4487
|
+
# print(f"\nโ Error: {e}")
|
|
4488
|
+
# print("๐งน Cleaning up resources...")
|
|
4118
4489
|
cleanup_modal_token()
|
|
4119
4490
|
sys.exit(1)
|
|
@@ -15,7 +15,7 @@ import uuid
|
|
|
15
15
|
import signal
|
|
16
16
|
from pathlib import Path
|
|
17
17
|
import modal
|
|
18
|
-
|
|
18
|
+
import anthropic
|
|
19
19
|
# Import authentication manager
|
|
20
20
|
try:
|
|
21
21
|
from auth_manager import AuthManager
|
|
@@ -516,21 +516,57 @@ class PersistentShell:
|
|
|
516
516
|
REMOVE_COMMAND: <reason>
|
|
517
517
|
"""
|
|
518
518
|
|
|
519
|
-
#
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
519
|
+
# Try OpenAI API first
|
|
520
|
+
try:
|
|
521
|
+
import openai
|
|
522
|
+
client = openai.OpenAI(api_key=api_key)
|
|
523
|
+
|
|
524
|
+
response = client.chat.completions.create(
|
|
525
|
+
model="gpt-4.1",
|
|
526
|
+
messages=[
|
|
527
|
+
{"role": "system", "content": "You are a helpful assistant that suggests alternative commands that don't require user input."},
|
|
528
|
+
{"role": "user", "content": prompt}
|
|
529
|
+
],
|
|
530
|
+
max_tokens=150,
|
|
531
|
+
temperature=0.7
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
response_text = response.choices[0].message.content.strip()
|
|
535
|
+
|
|
536
|
+
except Exception as e:
|
|
537
|
+
print(f"โ ๏ธ OpenAI API call failed: {e}")
|
|
538
|
+
|
|
539
|
+
# Try Claude as fallback if available
|
|
540
|
+
if anthropic is not None:
|
|
541
|
+
print("๐ Trying Claude-4-Sonnet as fallback for alternative command suggestion...")
|
|
542
|
+
try:
|
|
543
|
+
# Get Anthropic API key
|
|
544
|
+
anthropic_api_key = os.environ.get("ANTHROPIC_API_KEY")
|
|
545
|
+
if not anthropic_api_key:
|
|
546
|
+
print("โ ๏ธ No ANTHROPIC_API_KEY found in environment")
|
|
547
|
+
return None
|
|
548
|
+
|
|
549
|
+
# Create Anthropic client
|
|
550
|
+
client = anthropic.Anthropic(api_key=anthropic_api_key)
|
|
551
|
+
|
|
552
|
+
print("๐ค Calling Claude-4-Sonnet for alternative command suggestion...")
|
|
553
|
+
message = client.messages.create(
|
|
554
|
+
model="claude-3-5-sonnet-20241022",
|
|
555
|
+
max_tokens=150,
|
|
556
|
+
messages=[
|
|
557
|
+
{"role": "user", "content": prompt}
|
|
558
|
+
]
|
|
559
|
+
)
|
|
560
|
+
|
|
561
|
+
response_text = message.content[0].text.strip()
|
|
562
|
+
print("โ
Claude alternative command suggestion completed")
|
|
563
|
+
|
|
564
|
+
except Exception as claude_error:
|
|
565
|
+
print(f"โ Claude API call failed: {claude_error}")
|
|
566
|
+
return None
|
|
567
|
+
else:
|
|
568
|
+
print("โ ๏ธ Claude fallback not available (anthropic library not installed)")
|
|
569
|
+
return None
|
|
534
570
|
|
|
535
571
|
# Check if the response suggests removing the command
|
|
536
572
|
if response_text.startswith("REMOVE_COMMAND:"):
|
|
@@ -967,23 +1003,59 @@ class CommandListManager:
|
|
|
967
1003
|
RUN: <reason>
|
|
968
1004
|
"""
|
|
969
1005
|
|
|
970
|
-
#
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
1006
|
+
# Try OpenAI API first
|
|
1007
|
+
try:
|
|
1008
|
+
import openai
|
|
1009
|
+
client = openai.OpenAI(api_key=api_key)
|
|
1010
|
+
|
|
1011
|
+
print("๐ Analyzing if original command should be skipped...")
|
|
1012
|
+
|
|
1013
|
+
response = client.chat.completions.create(
|
|
1014
|
+
model="gpt-3.5-turbo",
|
|
1015
|
+
messages=[
|
|
1016
|
+
{"role": "system", "content": "You are a helpful assistant that analyzes command execution."},
|
|
1017
|
+
{"role": "user", "content": prompt}
|
|
1018
|
+
],
|
|
1019
|
+
max_tokens=100,
|
|
1020
|
+
temperature=0.3
|
|
1021
|
+
)
|
|
1022
|
+
|
|
1023
|
+
response_text = response.choices[0].message.content.strip()
|
|
1024
|
+
|
|
1025
|
+
except Exception as e:
|
|
1026
|
+
print(f"โ ๏ธ OpenAI API call failed: {e}")
|
|
1027
|
+
|
|
1028
|
+
# Try Claude as fallback if available
|
|
1029
|
+
if anthropic is not None:
|
|
1030
|
+
print("๐ Trying Claude-4-Sonnet as fallback for command skip analysis...")
|
|
1031
|
+
try:
|
|
1032
|
+
# Get Anthropic API key
|
|
1033
|
+
anthropic_api_key = os.environ.get("ANTHROPIC_API_KEY")
|
|
1034
|
+
if not anthropic_api_key:
|
|
1035
|
+
print("โ ๏ธ No ANTHROPIC_API_KEY found in environment")
|
|
1036
|
+
return False, "No API key available"
|
|
1037
|
+
|
|
1038
|
+
# Create Anthropic client
|
|
1039
|
+
client = anthropic.Anthropic(api_key=anthropic_api_key)
|
|
1040
|
+
|
|
1041
|
+
print("๐ค Calling Claude-4-Sonnet for command skip analysis...")
|
|
1042
|
+
message = client.messages.create(
|
|
1043
|
+
model="claude-3-5-sonnet-20241022",
|
|
1044
|
+
max_tokens=100,
|
|
1045
|
+
messages=[
|
|
1046
|
+
{"role": "user", "content": prompt}
|
|
1047
|
+
]
|
|
1048
|
+
)
|
|
1049
|
+
|
|
1050
|
+
response_text = message.content[0].text.strip()
|
|
1051
|
+
print("โ
Claude command skip analysis completed")
|
|
1052
|
+
|
|
1053
|
+
except Exception as claude_error:
|
|
1054
|
+
print(f"โ Claude API call failed: {claude_error}")
|
|
1055
|
+
return False, f"Error: {claude_error}"
|
|
1056
|
+
else:
|
|
1057
|
+
print("โ ๏ธ Claude fallback not available (anthropic library not installed)")
|
|
1058
|
+
return False, "No API key available"
|
|
987
1059
|
|
|
988
1060
|
# Parse the response
|
|
989
1061
|
if response_text.startswith("SKIP:"):
|
|
@@ -1111,24 +1183,60 @@ class CommandListManager:
|
|
|
1111
1183
|
Only include commands that need changes (SKIP, MODIFY, ADD_AFTER), not KEEP actions.
|
|
1112
1184
|
"""
|
|
1113
1185
|
|
|
1114
|
-
#
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1186
|
+
# Try OpenAI API first
|
|
1187
|
+
try:
|
|
1188
|
+
import openai
|
|
1189
|
+
import json
|
|
1190
|
+
client = openai.OpenAI(api_key=api_key)
|
|
1191
|
+
|
|
1192
|
+
print("๐ Analyzing command list for optimizations...")
|
|
1193
|
+
|
|
1194
|
+
response = client.chat.completions.create(
|
|
1195
|
+
model="gpt-4.1", # Use a more capable model for this complex task
|
|
1196
|
+
messages=[
|
|
1197
|
+
{"role": "system", "content": "You are a helpful assistant that analyzes and optimizes command lists."},
|
|
1198
|
+
{"role": "user", "content": prompt}
|
|
1199
|
+
],
|
|
1200
|
+
max_tokens=1000,
|
|
1201
|
+
temperature=0.2
|
|
1202
|
+
)
|
|
1203
|
+
|
|
1204
|
+
response_text = response.choices[0].message.content.strip()
|
|
1205
|
+
|
|
1206
|
+
except Exception as e:
|
|
1207
|
+
print(f"โ ๏ธ OpenAI API call failed: {e}")
|
|
1208
|
+
|
|
1209
|
+
# Try Claude as fallback if available
|
|
1210
|
+
if anthropic is not None:
|
|
1211
|
+
print("๐ Trying Claude-4-Sonnet as fallback for command list analysis...")
|
|
1212
|
+
try:
|
|
1213
|
+
# Get Anthropic API key
|
|
1214
|
+
anthropic_api_key = os.environ.get("ANTHROPIC_API_KEY")
|
|
1215
|
+
if not anthropic_api_key:
|
|
1216
|
+
print("โ ๏ธ No ANTHROPIC_API_KEY found in environment")
|
|
1217
|
+
return False
|
|
1218
|
+
|
|
1219
|
+
# Create Anthropic client
|
|
1220
|
+
client = anthropic.Anthropic(api_key=anthropic_api_key)
|
|
1221
|
+
|
|
1222
|
+
print("๐ค Calling Claude-4-Sonnet for command list analysis...")
|
|
1223
|
+
message = client.messages.create(
|
|
1224
|
+
model="claude-3-5-sonnet-20241022",
|
|
1225
|
+
max_tokens=1000,
|
|
1226
|
+
messages=[
|
|
1227
|
+
{"role": "user", "content": prompt}
|
|
1228
|
+
]
|
|
1229
|
+
)
|
|
1230
|
+
|
|
1231
|
+
response_text = message.content[0].text.strip()
|
|
1232
|
+
print("โ
Claude command list analysis completed")
|
|
1233
|
+
|
|
1234
|
+
except Exception as claude_error:
|
|
1235
|
+
print(f"โ Claude API call failed: {claude_error}")
|
|
1236
|
+
return False
|
|
1237
|
+
else:
|
|
1238
|
+
print("โ ๏ธ Claude fallback not available (anthropic library not installed)")
|
|
1239
|
+
return False
|
|
1132
1240
|
|
|
1133
1241
|
# Extract JSON from the response
|
|
1134
1242
|
try:
|
|
@@ -1780,8 +1888,186 @@ Do not provide any explanations, just the exact command to run.
|
|
|
1780
1888
|
last_error = error
|
|
1781
1889
|
|
|
1782
1890
|
if not result:
|
|
1783
|
-
print(f"โ All model attempts failed. Last error: {last_error}")
|
|
1784
|
-
|
|
1891
|
+
print(f"โ All OpenAI model attempts failed. Last error: {last_error}")
|
|
1892
|
+
|
|
1893
|
+
# Try Claude as fallback if available
|
|
1894
|
+
if anthropic is not None:
|
|
1895
|
+
print("๐ Trying Claude-4-Sonnet as fallback...")
|
|
1896
|
+
try:
|
|
1897
|
+
# Get Anthropic API key
|
|
1898
|
+
anthropic_api_key = os.environ.get("ANTHROPIC_API_KEY")
|
|
1899
|
+
if not anthropic_api_key:
|
|
1900
|
+
print("โ ๏ธ No ANTHROPIC_API_KEY found in environment")
|
|
1901
|
+
return None
|
|
1902
|
+
|
|
1903
|
+
# Create Anthropic client
|
|
1904
|
+
client = anthropic.Anthropic(api_key=anthropic_api_key)
|
|
1905
|
+
|
|
1906
|
+
# Prepare the same prompt for Claude
|
|
1907
|
+
claude_prompt = f"""
|
|
1908
|
+
I'm trying to run the following command in a Linux environment:
|
|
1909
|
+
|
|
1910
|
+
```
|
|
1911
|
+
{command}
|
|
1912
|
+
```
|
|
1913
|
+
|
|
1914
|
+
But it failed with this error:
|
|
1915
|
+
|
|
1916
|
+
```
|
|
1917
|
+
{error_output}
|
|
1918
|
+
```
|
|
1919
|
+
{system_info}
|
|
1920
|
+
{directory_context}
|
|
1921
|
+
{file_context}
|
|
1922
|
+
|
|
1923
|
+
AVAILABLE CREDENTIALS:
|
|
1924
|
+
{auth_context}
|
|
1925
|
+
|
|
1926
|
+
Please analyze the error and provide ONLY a single terminal command that would fix the issue.
|
|
1927
|
+
Consider the current directory, system information, directory contents, and available credentials carefully before suggesting a solution.
|
|
1928
|
+
|
|
1929
|
+
IMPORTANT GUIDELINES:
|
|
1930
|
+
1. For any commands that might ask for yes/no confirmation, use the appropriate non-interactive flag:
|
|
1931
|
+
- For apt/apt-get: use -y or --yes
|
|
1932
|
+
- For rm: use -f or --force
|
|
1933
|
+
|
|
1934
|
+
2. If the error indicates a file is not found:
|
|
1935
|
+
- FIRST try to search for the file using: find . -name "filename" -type f 2>/dev/null
|
|
1936
|
+
- If found, navigate to that directory using: cd /path/to/directory
|
|
1937
|
+
- If not found, then consider creating the file or installing missing packages
|
|
1938
|
+
|
|
1939
|
+
3. For missing packages or dependencies:
|
|
1940
|
+
- Use pip install for Python packages
|
|
1941
|
+
- Use apt-get install -y for system packages
|
|
1942
|
+
- Use npm install for Node.js packages
|
|
1943
|
+
|
|
1944
|
+
4. For authentication issues:
|
|
1945
|
+
- Analyze the error to determine what type of authentication is needed
|
|
1946
|
+
- ALWAYS use the actual credential values from the AVAILABLE CREDENTIALS section above (NOT placeholders)
|
|
1947
|
+
- Look for the specific API key or token needed in the auth_context and use its exact value
|
|
1948
|
+
- Common patterns:
|
|
1949
|
+
* wandb errors: use wandb login with the actual WANDB_API_KEY value from auth_context
|
|
1950
|
+
* huggingface errors: use huggingface-cli login with the actual HF_TOKEN or HUGGINGFACE_TOKEN value from auth_context
|
|
1951
|
+
* github errors: configure git credentials with the actual GITHUB_TOKEN value from auth_context
|
|
1952
|
+
* kaggle errors: create ~/.kaggle/kaggle.json with the actual KAGGLE_USERNAME and KAGGLE_KEY values from auth_context
|
|
1953
|
+
* API errors: export the appropriate API key as environment variable using the actual value from auth_context
|
|
1954
|
+
|
|
1955
|
+
5. Environment variable exports:
|
|
1956
|
+
- Use export commands for API keys that need to be in environment
|
|
1957
|
+
- ALWAYS use the actual credential values from auth_context, never use placeholders like "YOUR_API_KEY"
|
|
1958
|
+
- Example: export OPENAI_API_KEY="sk-..." (using the actual key from auth_context)
|
|
1959
|
+
|
|
1960
|
+
6. CRITICAL: When using any API key, token, or credential:
|
|
1961
|
+
- Find the exact value in the AVAILABLE CREDENTIALS section
|
|
1962
|
+
- Use that exact value in your command
|
|
1963
|
+
- Do not use generic placeholders or dummy values
|
|
1964
|
+
- The auth_context contains real, usable credentials
|
|
1965
|
+
|
|
1966
|
+
7. For Git SSH authentication failures:
|
|
1967
|
+
- If the error contains "Host key verification failed" or "Could not read from remote repository"
|
|
1968
|
+
- ALWAYS convert SSH URLs to HTTPS URLs for public repositories
|
|
1969
|
+
- Replace git@github.com:username/repo.git with https://github.com/username/repo.git
|
|
1970
|
+
- This works for public repositories without authentication
|
|
1971
|
+
- Example: git clone https://github.com/xg-chu/ARTalk.git
|
|
1972
|
+
|
|
1973
|
+
Do not provide any explanations, just the exact command to run.
|
|
1974
|
+
"""
|
|
1975
|
+
|
|
1976
|
+
print("๐ค Calling Claude-4-Sonnet to debug the failed command...")
|
|
1977
|
+
message = client.messages.create(
|
|
1978
|
+
model="claude-3-5-sonnet-20241022",
|
|
1979
|
+
max_tokens=300,
|
|
1980
|
+
messages=[
|
|
1981
|
+
{"role": "user", "content": claude_prompt}
|
|
1982
|
+
]
|
|
1983
|
+
)
|
|
1984
|
+
|
|
1985
|
+
fix_command = message.content[0].text.strip()
|
|
1986
|
+
print(f"๐ DEBUG: Raw Claude response content: {fix_command}")
|
|
1987
|
+
|
|
1988
|
+
# Process the response similar to OpenAI
|
|
1989
|
+
original_response = fix_command
|
|
1990
|
+
|
|
1991
|
+
# Extract just the command if it's wrapped in backticks or explanation
|
|
1992
|
+
if "```" in fix_command:
|
|
1993
|
+
# Extract content between backticks
|
|
1994
|
+
import re
|
|
1995
|
+
code_blocks = re.findall(r'```(?:bash|sh)?\s*(.*?)\s*```', fix_command, re.DOTALL)
|
|
1996
|
+
if code_blocks:
|
|
1997
|
+
fix_command = code_blocks[0].strip()
|
|
1998
|
+
print(f"โ
Extracted command from code block: {fix_command}")
|
|
1999
|
+
|
|
2000
|
+
# If the response still has explanatory text, try to extract just the command
|
|
2001
|
+
if len(fix_command.split('\n')) > 1:
|
|
2002
|
+
# First try to find lines that look like commands (start with common command prefixes)
|
|
2003
|
+
command_prefixes = ['sudo', 'apt', 'pip', 'npm', 'yarn', 'git', 'cd', 'mv', 'cp', 'rm', 'mkdir', 'touch',
|
|
2004
|
+
'chmod', 'chown', 'echo', 'cat', 'python', 'python3', 'node', 'export',
|
|
2005
|
+
'curl', 'wget', 'docker', 'make', 'gcc', 'g++', 'javac', 'java',
|
|
2006
|
+
'conda', 'uv', 'poetry', 'nvm', 'rbenv', 'pyenv', 'rustup']
|
|
2007
|
+
|
|
2008
|
+
# Check for lines that start with common command prefixes
|
|
2009
|
+
command_lines = [line.strip() for line in fix_command.split('\n')
|
|
2010
|
+
if any(line.strip().startswith(prefix) for prefix in command_prefixes)]
|
|
2011
|
+
|
|
2012
|
+
if command_lines:
|
|
2013
|
+
# Use the first command line found
|
|
2014
|
+
fix_command = command_lines[0]
|
|
2015
|
+
print(f"โ
Identified command by prefix: {fix_command}")
|
|
2016
|
+
else:
|
|
2017
|
+
# Try to find lines that look like commands (contain common shell patterns)
|
|
2018
|
+
shell_patterns = [' | ', ' > ', ' >> ', ' && ', ' || ', ' ; ', '$(', '`', ' -y ', ' --yes ']
|
|
2019
|
+
command_lines = [line.strip() for line in fix_command.split('\n')
|
|
2020
|
+
if any(pattern in line for pattern in shell_patterns)]
|
|
2021
|
+
|
|
2022
|
+
if command_lines:
|
|
2023
|
+
# Use the first command line found
|
|
2024
|
+
fix_command = command_lines[0]
|
|
2025
|
+
print(f"โ
Identified command by shell pattern: {fix_command}")
|
|
2026
|
+
else:
|
|
2027
|
+
# Fall back to the shortest non-empty line as it's likely the command
|
|
2028
|
+
lines = [line.strip() for line in fix_command.split('\n') if line.strip()]
|
|
2029
|
+
if lines:
|
|
2030
|
+
# Exclude very short lines that are likely not commands
|
|
2031
|
+
valid_lines = [line for line in lines if len(line) > 5]
|
|
2032
|
+
if valid_lines:
|
|
2033
|
+
fix_command = min(valid_lines, key=len)
|
|
2034
|
+
else:
|
|
2035
|
+
fix_command = min(lines, key=len)
|
|
2036
|
+
print(f"โ
Selected shortest line as command: {fix_command}")
|
|
2037
|
+
|
|
2038
|
+
# Clean up the command - remove any trailing periods or quotes
|
|
2039
|
+
fix_command = fix_command.rstrip('.;"\'')
|
|
2040
|
+
|
|
2041
|
+
# Remove common prefixes that LLMs sometimes add
|
|
2042
|
+
prefixes_to_remove = [
|
|
2043
|
+
"Run: ", "Execute: ", "Try: ", "Command: ", "Fix: ", "Solution: ",
|
|
2044
|
+
"You should run: ", "You can run: ", "You need to run: "
|
|
2045
|
+
]
|
|
2046
|
+
for prefix in prefixes_to_remove:
|
|
2047
|
+
if fix_command.startswith(prefix):
|
|
2048
|
+
fix_command = fix_command[len(prefix):].strip()
|
|
2049
|
+
print(f"โ
Removed prefix: {prefix}")
|
|
2050
|
+
break
|
|
2051
|
+
|
|
2052
|
+
# If the command is still multi-line or very long, it might not be a valid command
|
|
2053
|
+
if len(fix_command.split('\n')) > 1 or len(fix_command) > 500:
|
|
2054
|
+
print("โ ๏ธ Extracted command appears invalid (multi-line or too long)")
|
|
2055
|
+
print("๐ Original response from Claude:")
|
|
2056
|
+
print("-" * 60)
|
|
2057
|
+
print(original_response)
|
|
2058
|
+
print("-" * 60)
|
|
2059
|
+
print("โ ๏ธ Using best guess for command")
|
|
2060
|
+
|
|
2061
|
+
print(f"๐ง Claude suggested fix: {fix_command}")
|
|
2062
|
+
print(f"๐ DEBUG: Returning Claude fix command: {fix_command}")
|
|
2063
|
+
return fix_command
|
|
2064
|
+
|
|
2065
|
+
except Exception as e:
|
|
2066
|
+
print(f"โ Claude API call failed: {e}")
|
|
2067
|
+
return None
|
|
2068
|
+
else:
|
|
2069
|
+
print("โ ๏ธ Claude fallback not available (anthropic library not installed)")
|
|
2070
|
+
return None
|
|
1785
2071
|
|
|
1786
2072
|
# Process the response
|
|
1787
2073
|
try:
|
|
@@ -1988,7 +2274,65 @@ Provide fixes for all {len(failed_commands)} failed commands:"""
|
|
|
1988
2274
|
return fixes
|
|
1989
2275
|
else:
|
|
1990
2276
|
print(f"โ OpenAI API error: {response.status_code} - {response.text}")
|
|
1991
|
-
|
|
2277
|
+
|
|
2278
|
+
# Try Claude as fallback if available
|
|
2279
|
+
if anthropic is not None:
|
|
2280
|
+
print("๐ Trying Claude-4-Sonnet as fallback for batch debugging...")
|
|
2281
|
+
try:
|
|
2282
|
+
# Get Anthropic API key
|
|
2283
|
+
anthropic_api_key = os.environ.get("ANTHROPIC_API_KEY")
|
|
2284
|
+
if not anthropic_api_key:
|
|
2285
|
+
print("โ ๏ธ No ANTHROPIC_API_KEY found in environment")
|
|
2286
|
+
return []
|
|
2287
|
+
|
|
2288
|
+
# Create Anthropic client
|
|
2289
|
+
client = anthropic.Anthropic(api_key=anthropic_api_key)
|
|
2290
|
+
|
|
2291
|
+
print("๐ค Calling Claude-4-Sonnet for batch debugging...")
|
|
2292
|
+
message = client.messages.create(
|
|
2293
|
+
model="claude-3-5-sonnet-20241022",
|
|
2294
|
+
max_tokens=1000,
|
|
2295
|
+
messages=[
|
|
2296
|
+
{"role": "user", "content": prompt}
|
|
2297
|
+
]
|
|
2298
|
+
)
|
|
2299
|
+
|
|
2300
|
+
content = message.content[0].text
|
|
2301
|
+
print(f"โ
Claude batch analysis completed")
|
|
2302
|
+
|
|
2303
|
+
# Parse the response to extract fix commands
|
|
2304
|
+
fixes = []
|
|
2305
|
+
for i in range(1, len(failed_commands) + 1):
|
|
2306
|
+
fix_pattern = f"FIX_COMMAND_{i}: (.+)"
|
|
2307
|
+
reason_pattern = f"REASON_{i}: (.+)"
|
|
2308
|
+
|
|
2309
|
+
fix_match = re.search(fix_pattern, content, re.MULTILINE)
|
|
2310
|
+
reason_match = re.search(reason_pattern, content, re.MULTILINE)
|
|
2311
|
+
|
|
2312
|
+
if fix_match:
|
|
2313
|
+
fix_command = fix_match.group(1).strip()
|
|
2314
|
+
reason = reason_match.group(1).strip() if reason_match else "Claude suggested fix"
|
|
2315
|
+
|
|
2316
|
+
# Clean up the fix command
|
|
2317
|
+
if fix_command.startswith('`') and fix_command.endswith('`'):
|
|
2318
|
+
fix_command = fix_command[1:-1]
|
|
2319
|
+
|
|
2320
|
+
fixes.append({
|
|
2321
|
+
'original_command': failed_commands[i-1]['command'],
|
|
2322
|
+
'fix_command': fix_command,
|
|
2323
|
+
'reason': reason,
|
|
2324
|
+
'command_index': i-1
|
|
2325
|
+
})
|
|
2326
|
+
|
|
2327
|
+
print(f"๐ง Generated {len(fixes)} fix commands from Claude batch analysis")
|
|
2328
|
+
return fixes
|
|
2329
|
+
|
|
2330
|
+
except Exception as e:
|
|
2331
|
+
print(f"โ Claude API call failed: {e}")
|
|
2332
|
+
return []
|
|
2333
|
+
else:
|
|
2334
|
+
print("โ ๏ธ Claude fallback not available (anthropic library not installed)")
|
|
2335
|
+
return []
|
|
1992
2336
|
|
|
1993
2337
|
except Exception as e:
|
|
1994
2338
|
print(f"โ Error during batch debugging: {e}")
|
|
@@ -3565,21 +3909,57 @@ Return only the JSON array, no other text.
|
|
|
3565
3909
|
print("โ ๏ธ No OpenAI API key available for command preprocessing")
|
|
3566
3910
|
return setup_commands
|
|
3567
3911
|
|
|
3568
|
-
#
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3912
|
+
# Try OpenAI API first
|
|
3913
|
+
try:
|
|
3914
|
+
import openai
|
|
3915
|
+
client = openai.OpenAI(api_key=api_key)
|
|
3916
|
+
|
|
3917
|
+
response = client.chat.completions.create(
|
|
3918
|
+
model="gpt-3.5-turbo",
|
|
3919
|
+
messages=[
|
|
3920
|
+
{"role": "system", "content": "You are a command preprocessing assistant that modifies setup commands to use available credentials and make them non-interactive."},
|
|
3921
|
+
{"role": "user", "content": prompt}
|
|
3922
|
+
],
|
|
3923
|
+
temperature=0.1,
|
|
3924
|
+
max_tokens=2000
|
|
3925
|
+
)
|
|
3926
|
+
|
|
3927
|
+
result = response.choices[0].message.content.strip()
|
|
3928
|
+
|
|
3929
|
+
except Exception as e:
|
|
3930
|
+
print(f"โ ๏ธ OpenAI API call failed: {e}")
|
|
3931
|
+
|
|
3932
|
+
# Try Claude as fallback if available
|
|
3933
|
+
if anthropic is not None:
|
|
3934
|
+
print("๐ Trying Claude-4-Sonnet as fallback for command preprocessing...")
|
|
3935
|
+
try:
|
|
3936
|
+
# Get Anthropic API key
|
|
3937
|
+
anthropic_api_key = os.environ.get("ANTHROPIC_API_KEY")
|
|
3938
|
+
if not anthropic_api_key:
|
|
3939
|
+
print("โ ๏ธ No ANTHROPIC_API_KEY found in environment")
|
|
3940
|
+
return fallback_preprocess_commands(setup_commands, stored_credentials)
|
|
3941
|
+
|
|
3942
|
+
# Create Anthropic client
|
|
3943
|
+
client = anthropic.Anthropic(api_key=anthropic_api_key)
|
|
3944
|
+
|
|
3945
|
+
print("๐ค Calling Claude-4-Sonnet for command preprocessing...")
|
|
3946
|
+
message = client.messages.create(
|
|
3947
|
+
model="claude-3-5-sonnet-20241022",
|
|
3948
|
+
max_tokens=2000,
|
|
3949
|
+
messages=[
|
|
3950
|
+
{"role": "user", "content": prompt}
|
|
3951
|
+
]
|
|
3952
|
+
)
|
|
3953
|
+
|
|
3954
|
+
result = message.content[0].text.strip()
|
|
3955
|
+
print("โ
Claude preprocessing completed")
|
|
3956
|
+
|
|
3957
|
+
except Exception as claude_error:
|
|
3958
|
+
print(f"โ Claude API call failed: {claude_error}")
|
|
3959
|
+
return fallback_preprocess_commands(setup_commands, stored_credentials)
|
|
3960
|
+
else:
|
|
3961
|
+
print("โ ๏ธ Claude fallback not available (anthropic library not installed)")
|
|
3962
|
+
return fallback_preprocess_commands(setup_commands, stored_credentials)
|
|
3583
3963
|
|
|
3584
3964
|
# Debug: Print the raw response
|
|
3585
3965
|
print(f"๐ LLM Response: {result[:200]}...")
|
|
@@ -3929,19 +4309,7 @@ if __name__ == "__main__":
|
|
|
3929
4309
|
print(f"Setup Commands: {len(args.setup_commands)} custom commands")
|
|
3930
4310
|
else:
|
|
3931
4311
|
print("Setup Commands: Auto-detect from repository")
|
|
3932
|
-
|
|
3933
|
-
# Confirm settings
|
|
3934
|
-
if not args.yes:
|
|
3935
|
-
try:
|
|
3936
|
-
proceed = input("Proceed with these settings? (Y/n): ").strip().lower()
|
|
3937
|
-
if proceed in ('n', 'no'):
|
|
3938
|
-
print("๐ Operation cancelled by user.")
|
|
3939
|
-
sys.exit(0)
|
|
3940
|
-
except KeyboardInterrupt:
|
|
3941
|
-
print("\n๐ Operation cancelled by user.")
|
|
3942
|
-
sys.exit(0)
|
|
3943
|
-
else:
|
|
3944
|
-
print("โ
Skipping confirmation prompt (--yes flag used)")
|
|
4312
|
+
|
|
3945
4313
|
|
|
3946
4314
|
# Interactive mode or missing required arguments
|
|
3947
4315
|
if args.interactive or not args.repo_url or not args.volume_name:
|