wcgw 1.1.2__py3-none-any.whl → 1.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of wcgw might be problematic. Click here for more details.
- wcgw/client/anthropic_client.py +16 -7
- wcgw/client/diff-instructions.txt +6 -15
- wcgw/client/openai_client.py +27 -12
- wcgw/client/tools.py +72 -31
- wcgw/relay/serve.py +46 -8
- wcgw/types_.py +16 -3
- {wcgw-1.1.2.dist-info → wcgw-1.2.0.dist-info}/METADATA +1 -1
- wcgw-1.2.0.dist-info/RECORD +17 -0
- wcgw-1.1.2.dist-info/RECORD +0 -17
- {wcgw-1.1.2.dist-info → wcgw-1.2.0.dist-info}/WHEEL +0 -0
- {wcgw-1.1.2.dist-info → wcgw-1.2.0.dist-info}/entry_points.txt +0 -0
wcgw/client/anthropic_client.py
CHANGED
|
@@ -26,7 +26,8 @@ from ..types_ import (
|
|
|
26
26
|
BashInteraction,
|
|
27
27
|
CreateFileNew,
|
|
28
28
|
FileEditFindReplace,
|
|
29
|
-
|
|
29
|
+
FileEdit,
|
|
30
|
+
ReadFile,
|
|
30
31
|
ReadImage,
|
|
31
32
|
Writefile,
|
|
32
33
|
ResetShell,
|
|
@@ -175,6 +176,14 @@ def loop(
|
|
|
175
176
|
- Send text input to the running program.
|
|
176
177
|
- Send send_specials=["Enter"] to recheck status of a running program.
|
|
177
178
|
- Only one of send_text, send_specials, send_ascii should be provided.
|
|
179
|
+
""",
|
|
180
|
+
),
|
|
181
|
+
ToolParam(
|
|
182
|
+
input_schema=ReadFile.model_json_schema(),
|
|
183
|
+
name="ReadFile",
|
|
184
|
+
description="""
|
|
185
|
+
- Read full file content
|
|
186
|
+
- Provide absolute file path only
|
|
178
187
|
""",
|
|
179
188
|
),
|
|
180
189
|
ToolParam(
|
|
@@ -184,7 +193,7 @@ def loop(
|
|
|
184
193
|
- Write content to a new file. Provide file path and content. Use this instead of BashCommand for writing new files.
|
|
185
194
|
- This doesn't create any directories, please create directories using `mkdir -p` BashCommand.
|
|
186
195
|
- Provide absolute file path only.
|
|
187
|
-
- For editing existing files, use
|
|
196
|
+
- For editing existing files, use FileEdit.
|
|
188
197
|
""",
|
|
189
198
|
),
|
|
190
199
|
ToolParam(
|
|
@@ -198,8 +207,8 @@ def loop(
|
|
|
198
207
|
description="Resets the shell. Use only if all interrupts and prompt reset attempts have failed repeatedly.",
|
|
199
208
|
),
|
|
200
209
|
ToolParam(
|
|
201
|
-
input_schema=
|
|
202
|
-
name="
|
|
210
|
+
input_schema=FileEdit.model_json_schema(),
|
|
211
|
+
name="FileEdit",
|
|
203
212
|
description="""
|
|
204
213
|
- Use absolute file path only.
|
|
205
214
|
- Use SEARCH/REPLACE blocks to edit the file.
|
|
@@ -214,9 +223,9 @@ You're a cli assistant.
|
|
|
214
223
|
|
|
215
224
|
Instructions:
|
|
216
225
|
|
|
217
|
-
- You should use the provided bash execution tool to run script to complete objective.
|
|
218
|
-
-
|
|
219
|
-
-
|
|
226
|
+
- You should use the provided bash execution tool to run script to complete objective.
|
|
227
|
+
- First understand about the project by understanding the folder structure (ignoring node_modules or venv, etc.)
|
|
228
|
+
- Always read relevant files before making any changes.
|
|
220
229
|
|
|
221
230
|
System information:
|
|
222
231
|
- System: {uname_sysname}
|
|
@@ -2,14 +2,6 @@
|
|
|
2
2
|
Instructions for
|
|
3
3
|
Only edit the files using the following SEARCH/REPLACE blocks.
|
|
4
4
|
```
|
|
5
|
-
<<<<<<< SEARCH
|
|
6
|
-
=======
|
|
7
|
-
def hello():
|
|
8
|
-
"print a greeting"
|
|
9
|
-
|
|
10
|
-
print("hello")
|
|
11
|
-
>>>>>>> REPLACE
|
|
12
|
-
|
|
13
5
|
<<<<<<< SEARCH
|
|
14
6
|
def hello():
|
|
15
7
|
"print a greeting"
|
|
@@ -23,16 +15,13 @@ from hello import hello
|
|
|
23
15
|
# *SEARCH/REPLACE block* Rules:
|
|
24
16
|
|
|
25
17
|
Every *SEARCH/REPLACE block* must use this format:
|
|
26
|
-
1. The start of
|
|
27
|
-
2. A contiguous chunk of lines to
|
|
18
|
+
1. The start of match block: <<<<<<< SEARCH
|
|
19
|
+
2. A contiguous chunk of lines to do exact match for in the existing source code
|
|
28
20
|
3. The dividing line: =======
|
|
29
21
|
4. The lines to replace into the source code
|
|
30
22
|
5. The end of the replace block: >>>>>>> REPLACE
|
|
31
23
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
Every *SEARCH* section must *EXACTLY MATCH* the existing file content, character for character, including all comments, docstrings, etc.
|
|
35
|
-
If the file contains code or other data wrapped/escaped in json/xml/quotes or other containers, you need to propose edits to the literal contents of the file, including the container markup.
|
|
24
|
+
Every "<<<<<<< SEARCH" section must *EXACTLY MATCH* the existing file content, character for character, including all comments, docstrings, etc.
|
|
36
25
|
|
|
37
26
|
*SEARCH/REPLACE* blocks will *only* replace the first match occurrence.
|
|
38
27
|
Including multiple unique *SEARCH/REPLACE* blocks if needed.
|
|
@@ -41,4 +30,6 @@ Include enough lines in each SEARCH section to uniquely match each set of lines
|
|
|
41
30
|
Keep *SEARCH/REPLACE* blocks concise.
|
|
42
31
|
Break large *SEARCH/REPLACE* blocks into a series of smaller blocks that each change a small portion of the file.
|
|
43
32
|
Include just the changing lines, and a few surrounding lines if needed for uniqueness.
|
|
44
|
-
Do not include long runs of unchanging lines in *SEARCH/REPLACE* blocks.
|
|
33
|
+
Do not include long runs of unchanging lines in *SEARCH/REPLACE* blocks.
|
|
34
|
+
|
|
35
|
+
Include context lines before and after the code to edit for better matching in "<<<<<<< SEARCH". Recommended to add 3 lines on each side.
|
wcgw/client/openai_client.py
CHANGED
|
@@ -24,8 +24,9 @@ from ..types_ import (
|
|
|
24
24
|
BashCommand,
|
|
25
25
|
BashInteraction,
|
|
26
26
|
CreateFileNew,
|
|
27
|
-
|
|
27
|
+
FileEdit,
|
|
28
28
|
ReadImage,
|
|
29
|
+
ReadFile,
|
|
29
30
|
Writefile,
|
|
30
31
|
ResetShell,
|
|
31
32
|
)
|
|
@@ -58,7 +59,6 @@ from dotenv import load_dotenv
|
|
|
58
59
|
|
|
59
60
|
class Config(BaseModel):
|
|
60
61
|
model: Models
|
|
61
|
-
secondary_model: Models
|
|
62
62
|
cost_limit: float
|
|
63
63
|
cost_file: dict[Models, CostData]
|
|
64
64
|
cost_unit: str = "$"
|
|
@@ -146,10 +146,18 @@ def loop(
|
|
|
146
146
|
waiting_for_assistant = history[-1]["role"] != "assistant"
|
|
147
147
|
|
|
148
148
|
my_dir = os.path.dirname(__file__)
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
149
|
+
|
|
150
|
+
config = Config(
|
|
151
|
+
model=cast(
|
|
152
|
+
Models, os.getenv("OPENAI_MODEL", "gpt-4o-2024-08-06").lower()),
|
|
153
|
+
cost_limit=0.1,
|
|
154
|
+
cost_unit="$",
|
|
155
|
+
cost_file={
|
|
156
|
+
"gpt-4o-2024-08-06": CostData(
|
|
157
|
+
cost_per_1m_input_tokens=5, cost_per_1m_output_tokens=15
|
|
158
|
+
),
|
|
159
|
+
},
|
|
160
|
+
)
|
|
153
161
|
|
|
154
162
|
if limit is not None:
|
|
155
163
|
config.cost_limit = limit
|
|
@@ -178,6 +186,13 @@ def loop(
|
|
|
178
186
|
- Special keys like arrows, interrupts, enter, etc.
|
|
179
187
|
- Send text input to the running program.
|
|
180
188
|
- Only one of send_text, send_specials, send_ascii should be provided.""",
|
|
189
|
+
),
|
|
190
|
+
openai.pydantic_function_tool(
|
|
191
|
+
ReadFile,
|
|
192
|
+
description="""
|
|
193
|
+
- Read full file content
|
|
194
|
+
- Provide absolute file path only
|
|
195
|
+
""",
|
|
181
196
|
),
|
|
182
197
|
openai.pydantic_function_tool(
|
|
183
198
|
CreateFileNew,
|
|
@@ -185,14 +200,14 @@ def loop(
|
|
|
185
200
|
- Write content to a new file. Provide file path and content. Use this instead of BashCommand for writing new files.
|
|
186
201
|
- This doesn't create any directories, please create directories using `mkdir -p` BashCommand.
|
|
187
202
|
- Provide absolute file path only.
|
|
188
|
-
- For editing existing files, use
|
|
203
|
+
- For editing existing files, use FileEdit.""",
|
|
189
204
|
),
|
|
190
205
|
openai.pydantic_function_tool(
|
|
191
|
-
|
|
206
|
+
FileEdit,
|
|
192
207
|
description="""
|
|
193
208
|
- Use absolute file path only.
|
|
194
209
|
- Use ONLY SEARCH/REPLACE blocks to edit the file.
|
|
195
|
-
-
|
|
210
|
+
- file_edit_using_search_replace_blocks should start with <<<<<<< SEARCH
|
|
196
211
|
""",
|
|
197
212
|
),
|
|
198
213
|
openai.pydantic_function_tool(
|
|
@@ -211,9 +226,9 @@ You're a cli assistant.
|
|
|
211
226
|
|
|
212
227
|
Instructions:
|
|
213
228
|
|
|
214
|
-
- You should use the provided bash execution tool to run script to complete objective.
|
|
215
|
-
-
|
|
216
|
-
-
|
|
229
|
+
- You should use the provided bash execution tool to run script to complete objective.
|
|
230
|
+
- First understand about the project by understanding the folder structure (ignoring node_modules or venv, etc.)
|
|
231
|
+
- Always read relevant files before making any changes.
|
|
217
232
|
|
|
218
233
|
System information:
|
|
219
234
|
- System: {uname_sysname}
|
wcgw/client/tools.py
CHANGED
|
@@ -48,19 +48,17 @@ from openai.types.chat import (
|
|
|
48
48
|
from nltk.metrics.distance import edit_distance
|
|
49
49
|
|
|
50
50
|
from ..types_ import (
|
|
51
|
+
BashCommand,
|
|
52
|
+
BashInteraction,
|
|
51
53
|
CreateFileNew,
|
|
52
54
|
FileEditFindReplace,
|
|
53
|
-
|
|
55
|
+
FileEdit,
|
|
56
|
+
ReadFile,
|
|
57
|
+
ReadImage,
|
|
54
58
|
ResetShell,
|
|
55
59
|
Writefile,
|
|
56
60
|
)
|
|
57
61
|
|
|
58
|
-
from ..types_ import BashCommand
|
|
59
|
-
|
|
60
|
-
from ..types_ import BashInteraction
|
|
61
|
-
|
|
62
|
-
from ..types_ import ReadImage
|
|
63
|
-
|
|
64
62
|
from .common import CostData, Models, discard_input
|
|
65
63
|
|
|
66
64
|
from .openai_utils import get_input_cost, get_output_cost
|
|
@@ -320,7 +318,7 @@ def execute_bash(
|
|
|
320
318
|
tokens = enc.encode(text)
|
|
321
319
|
|
|
322
320
|
if max_tokens and len(tokens) >= max_tokens:
|
|
323
|
-
text = "...(truncated)\n" + enc.decode(tokens[-(max_tokens - 1)
|
|
321
|
+
text = "...(truncated)\n" + enc.decode(tokens[-(max_tokens - 1):])
|
|
324
322
|
|
|
325
323
|
if is_interrupt:
|
|
326
324
|
text = (
|
|
@@ -347,7 +345,7 @@ Otherwise, you may want to try Ctrl-c again or program specific exit interactive
|
|
|
347
345
|
|
|
348
346
|
tokens = enc.encode(output)
|
|
349
347
|
if max_tokens and len(tokens) >= max_tokens:
|
|
350
|
-
output = "...(truncated)\n" + enc.decode(tokens[-(max_tokens - 1)
|
|
348
|
+
output = "...(truncated)\n" + enc.decode(tokens[-(max_tokens - 1):])
|
|
351
349
|
|
|
352
350
|
try:
|
|
353
351
|
exit_status = get_status()
|
|
@@ -427,7 +425,7 @@ def read_image_from_shell(file_path: str) -> ImageData:
|
|
|
427
425
|
|
|
428
426
|
def write_file(writefile: Writefile | CreateFileNew, error_on_exist: bool) -> str:
|
|
429
427
|
if not os.path.isabs(writefile.file_path):
|
|
430
|
-
return "Failure: file_path should be absolute path"
|
|
428
|
+
return f"Failure: file_path should be absolute path, current working directory is {CWD}"
|
|
431
429
|
else:
|
|
432
430
|
path_ = writefile.file_path
|
|
433
431
|
|
|
@@ -466,12 +464,14 @@ def find_least_edit_distance_substring(
|
|
|
466
464
|
edit_distance_sum = 0
|
|
467
465
|
for j in range(len(find_lines)):
|
|
468
466
|
if (i + j) < len(content_lines):
|
|
469
|
-
edit_distance_sum += edit_distance(
|
|
467
|
+
edit_distance_sum += edit_distance(
|
|
468
|
+
content_lines[i + j], find_lines[j])
|
|
470
469
|
else:
|
|
471
470
|
edit_distance_sum += len(find_lines[j])
|
|
472
471
|
if edit_distance_sum < min_edit_distance:
|
|
473
472
|
min_edit_distance = edit_distance_sum
|
|
474
|
-
min_edit_distance_lines = orig_content_lines[i
|
|
473
|
+
min_edit_distance_lines = orig_content_lines[i: i + len(
|
|
474
|
+
find_lines)]
|
|
475
475
|
return "\n".join(min_edit_distance_lines), min_edit_distance
|
|
476
476
|
|
|
477
477
|
|
|
@@ -495,11 +495,12 @@ def edit_content(content: str, find_lines: str, replace_with_lines: str) -> str:
|
|
|
495
495
|
return content
|
|
496
496
|
|
|
497
497
|
|
|
498
|
-
def do_diff_edit(fedit:
|
|
498
|
+
def do_diff_edit(fedit: FileEdit) -> str:
|
|
499
499
|
console.log(f"Editing file: {fedit.file_path}")
|
|
500
500
|
|
|
501
501
|
if not os.path.isabs(fedit.file_path):
|
|
502
|
-
raise Exception(
|
|
502
|
+
raise Exception(
|
|
503
|
+
f"Failure: file_path should be absolute path, current working directory is {CWD}")
|
|
503
504
|
else:
|
|
504
505
|
path_ = fedit.file_path
|
|
505
506
|
|
|
@@ -509,9 +510,10 @@ def do_diff_edit(fedit: FullFileEdit) -> str:
|
|
|
509
510
|
with open(path_) as f:
|
|
510
511
|
apply_diff_to = f.read()
|
|
511
512
|
|
|
512
|
-
lines = fedit.
|
|
513
|
+
lines = fedit.file_edit_using_search_replace_blocks.split("\n")
|
|
513
514
|
n_lines = len(lines)
|
|
514
515
|
i = 0
|
|
516
|
+
replacement_count = 0
|
|
515
517
|
while i < n_lines:
|
|
516
518
|
if re.match(r"^<<<<<<+\s*SEARCH\s*$", lines[i]):
|
|
517
519
|
search_block = []
|
|
@@ -535,10 +537,17 @@ def do_diff_edit(fedit: FullFileEdit) -> str:
|
|
|
535
537
|
search_block_ = "\n".join(search_block)
|
|
536
538
|
replace_block_ = "\n".join(replace_block)
|
|
537
539
|
|
|
538
|
-
apply_diff_to = edit_content(
|
|
540
|
+
apply_diff_to = edit_content(
|
|
541
|
+
apply_diff_to, search_block_, replace_block_)
|
|
542
|
+
replacement_count += 1
|
|
539
543
|
else:
|
|
540
544
|
i += 1
|
|
541
545
|
|
|
546
|
+
if replacement_count == 0:
|
|
547
|
+
raise Exception(
|
|
548
|
+
"Error: no valid search-replace blocks found, please check your syntax for FileEdit"
|
|
549
|
+
)
|
|
550
|
+
|
|
542
551
|
with open(path_, "w") as f:
|
|
543
552
|
f.write(apply_diff_to)
|
|
544
553
|
|
|
@@ -547,7 +556,8 @@ def do_diff_edit(fedit: FullFileEdit) -> str:
|
|
|
547
556
|
|
|
548
557
|
def file_edit(fedit: FileEditFindReplace) -> str:
|
|
549
558
|
if not os.path.isabs(fedit.file_path):
|
|
550
|
-
raise Exception(
|
|
559
|
+
raise Exception(
|
|
560
|
+
f"Failure: file_path should be absolute path, current working directory is {CWD}")
|
|
551
561
|
else:
|
|
552
562
|
path_ = fedit.file_path
|
|
553
563
|
|
|
@@ -557,14 +567,17 @@ def file_edit(fedit: FileEditFindReplace) -> str:
|
|
|
557
567
|
if not fedit.find_lines:
|
|
558
568
|
raise Exception("Error: `find_lines` cannot be empty")
|
|
559
569
|
|
|
560
|
-
out_string = "\n".join(
|
|
561
|
-
|
|
570
|
+
out_string = "\n".join(
|
|
571
|
+
"> " + line for line in fedit.find_lines.split("\n"))
|
|
572
|
+
in_string = "\n".join(
|
|
573
|
+
"< " + line for line in fedit.replace_with_lines.split("\n"))
|
|
562
574
|
console.log(f"Editing file: {path_}\n---\n{out_string}\n---\n{in_string}\n---")
|
|
563
575
|
try:
|
|
564
576
|
with open(path_) as f:
|
|
565
577
|
content = f.read()
|
|
566
578
|
|
|
567
|
-
content = edit_content(content, fedit.find_lines,
|
|
579
|
+
content = edit_content(content, fedit.find_lines,
|
|
580
|
+
fedit.replace_with_lines)
|
|
568
581
|
|
|
569
582
|
with open(path_, "w") as f:
|
|
570
583
|
f.write(content)
|
|
@@ -604,10 +617,11 @@ TOOLS = (
|
|
|
604
617
|
| Writefile
|
|
605
618
|
| CreateFileNew
|
|
606
619
|
| FileEditFindReplace
|
|
607
|
-
|
|
|
620
|
+
| FileEdit
|
|
608
621
|
| AIAssistant
|
|
609
622
|
| DoneFlag
|
|
610
623
|
| ReadImage
|
|
624
|
+
| ReadFile
|
|
611
625
|
)
|
|
612
626
|
|
|
613
627
|
|
|
@@ -631,14 +645,16 @@ def which_tool_name(name: str) -> Type[TOOLS]:
|
|
|
631
645
|
return CreateFileNew
|
|
632
646
|
elif name == "FileEditFindReplace":
|
|
633
647
|
return FileEditFindReplace
|
|
634
|
-
elif name == "
|
|
635
|
-
return
|
|
648
|
+
elif name == "FileEdit":
|
|
649
|
+
return FileEdit
|
|
636
650
|
elif name == "AIAssistant":
|
|
637
651
|
return AIAssistant
|
|
638
652
|
elif name == "DoneFlag":
|
|
639
653
|
return DoneFlag
|
|
640
654
|
elif name == "ReadImage":
|
|
641
655
|
return ReadImage
|
|
656
|
+
elif name == "ReadFile":
|
|
657
|
+
return ReadFile
|
|
642
658
|
else:
|
|
643
659
|
raise ValueError(f"Unknown tool name: {name}")
|
|
644
660
|
|
|
@@ -652,10 +668,11 @@ def get_tool_output(
|
|
|
652
668
|
| Writefile
|
|
653
669
|
| CreateFileNew
|
|
654
670
|
| FileEditFindReplace
|
|
655
|
-
|
|
|
671
|
+
| FileEdit
|
|
656
672
|
| AIAssistant
|
|
657
673
|
| DoneFlag
|
|
658
|
-
| ReadImage
|
|
674
|
+
| ReadImage
|
|
675
|
+
| ReadFile,
|
|
659
676
|
enc: tiktoken.Encoding,
|
|
660
677
|
limit: float,
|
|
661
678
|
loop_call: Callable[[str, float], tuple[str, float]],
|
|
@@ -670,10 +687,11 @@ def get_tool_output(
|
|
|
670
687
|
| Writefile
|
|
671
688
|
| CreateFileNew
|
|
672
689
|
| FileEditFindReplace
|
|
673
|
-
|
|
|
690
|
+
| FileEdit
|
|
674
691
|
| AIAssistant
|
|
675
692
|
| DoneFlag
|
|
676
693
|
| ReadImage
|
|
694
|
+
| ReadFile
|
|
677
695
|
](
|
|
678
696
|
Confirmation
|
|
679
697
|
| BashCommand
|
|
@@ -682,10 +700,11 @@ def get_tool_output(
|
|
|
682
700
|
| Writefile
|
|
683
701
|
| CreateFileNew
|
|
684
702
|
| FileEditFindReplace
|
|
685
|
-
|
|
|
703
|
+
| FileEdit
|
|
686
704
|
| AIAssistant
|
|
687
705
|
| DoneFlag
|
|
688
706
|
| ReadImage
|
|
707
|
+
| ReadFile
|
|
689
708
|
)
|
|
690
709
|
arg = adapter.validate_python(args)
|
|
691
710
|
else:
|
|
@@ -706,7 +725,7 @@ def get_tool_output(
|
|
|
706
725
|
elif isinstance(arg, FileEditFindReplace):
|
|
707
726
|
console.print("Calling file edit tool")
|
|
708
727
|
output = file_edit(arg), 0.0
|
|
709
|
-
elif isinstance(arg,
|
|
728
|
+
elif isinstance(arg, FileEdit):
|
|
710
729
|
console.print("Calling full file edit tool")
|
|
711
730
|
output = do_diff_edit(arg), 0.0
|
|
712
731
|
elif isinstance(arg, DoneFlag):
|
|
@@ -718,6 +737,9 @@ def get_tool_output(
|
|
|
718
737
|
elif isinstance(arg, ReadImage):
|
|
719
738
|
console.print("Calling read image tool")
|
|
720
739
|
output = read_image_from_shell(arg.file_path), 0.0
|
|
740
|
+
elif isinstance(arg, ReadFile):
|
|
741
|
+
console.print("Calling read file tool")
|
|
742
|
+
output = read_file(arg), 0.0
|
|
721
743
|
elif isinstance(arg, ResetShell):
|
|
722
744
|
console.print("Calling reset shell tool")
|
|
723
745
|
output = reset_shell(), 0.0
|
|
@@ -732,7 +754,8 @@ History = list[ChatCompletionMessageParam]
|
|
|
732
754
|
|
|
733
755
|
default_enc = tiktoken.encoding_for_model("gpt-4o")
|
|
734
756
|
default_model: Models = "gpt-4o-2024-08-06"
|
|
735
|
-
default_cost = CostData(cost_per_1m_input_tokens=0.15,
|
|
757
|
+
default_cost = CostData(cost_per_1m_input_tokens=0.15,
|
|
758
|
+
cost_per_1m_output_tokens=0.6)
|
|
736
759
|
curr_cost = 0.0
|
|
737
760
|
|
|
738
761
|
|
|
@@ -744,8 +767,9 @@ class Mdata(BaseModel):
|
|
|
744
767
|
| CreateFileNew
|
|
745
768
|
| ResetShell
|
|
746
769
|
| FileEditFindReplace
|
|
747
|
-
|
|
|
770
|
+
| FileEdit
|
|
748
771
|
| str
|
|
772
|
+
| ReadFile
|
|
749
773
|
)
|
|
750
774
|
|
|
751
775
|
|
|
@@ -774,7 +798,8 @@ def register_client(server_url: str, client_uuid: str = "") -> None:
|
|
|
774
798
|
raise Exception(mdata)
|
|
775
799
|
try:
|
|
776
800
|
output, cost = get_tool_output(
|
|
777
|
-
mdata.data, default_enc, 0.0, lambda x, y: (
|
|
801
|
+
mdata.data, default_enc, 0.0, lambda x, y: (
|
|
802
|
+
"", 0), None
|
|
778
803
|
)
|
|
779
804
|
curr_cost += cost
|
|
780
805
|
print(f"{curr_cost=}")
|
|
@@ -805,3 +830,19 @@ def app(
|
|
|
805
830
|
exit()
|
|
806
831
|
|
|
807
832
|
register_client(server_url, client_uuid or "")
|
|
833
|
+
|
|
834
|
+
|
|
835
|
+
def read_file(readfile: ReadFile) -> str:
|
|
836
|
+
|
|
837
|
+
console.print(f"Reading file: {readfile.file_path}")
|
|
838
|
+
|
|
839
|
+
if not os.path.isabs(readfile.file_path):
|
|
840
|
+
return f"Failure: file_path should be absolute path, current working directory is {CWD}"
|
|
841
|
+
|
|
842
|
+
path = Path(readfile.file_path)
|
|
843
|
+
if not path.exists():
|
|
844
|
+
return f"Error: file {readfile.file_path} does not exist"
|
|
845
|
+
|
|
846
|
+
with path.open("r") as f:
|
|
847
|
+
content = f.read()
|
|
848
|
+
return content
|
wcgw/relay/serve.py
CHANGED
|
@@ -19,7 +19,8 @@ from ..types_ import (
|
|
|
19
19
|
BashInteraction,
|
|
20
20
|
CreateFileNew,
|
|
21
21
|
FileEditFindReplace,
|
|
22
|
-
|
|
22
|
+
FileEdit,
|
|
23
|
+
ReadFile,
|
|
23
24
|
ResetShell,
|
|
24
25
|
Writefile,
|
|
25
26
|
Specials,
|
|
@@ -34,7 +35,8 @@ class Mdata(BaseModel):
|
|
|
34
35
|
| CreateFileNew
|
|
35
36
|
| ResetShell
|
|
36
37
|
| FileEditFindReplace
|
|
37
|
-
|
|
|
38
|
+
| FileEdit
|
|
39
|
+
| ReadFile
|
|
38
40
|
| str
|
|
39
41
|
)
|
|
40
42
|
user_id: UUID
|
|
@@ -49,7 +51,7 @@ gpts: dict[UUID, Callable[[str], None]] = {}
|
|
|
49
51
|
images: DefaultDict[UUID, dict[str, dict[str, Any]]] = DefaultDict(dict)
|
|
50
52
|
|
|
51
53
|
|
|
52
|
-
CLIENT_VERSION_MINIMUM = "1.
|
|
54
|
+
CLIENT_VERSION_MINIMUM = "1.2.0"
|
|
53
55
|
|
|
54
56
|
|
|
55
57
|
@app.websocket("/v1/register/{uuid}")
|
|
@@ -63,12 +65,14 @@ async def register_websocket(websocket: WebSocket, uuid: UUID) -> None:
|
|
|
63
65
|
# receive client version
|
|
64
66
|
client_version = await websocket.receive_text()
|
|
65
67
|
sem_version_client = semantic_version.Version.coerce(client_version)
|
|
66
|
-
sem_version_server = semantic_version.Version.coerce(
|
|
68
|
+
sem_version_server = semantic_version.Version.coerce(
|
|
69
|
+
CLIENT_VERSION_MINIMUM)
|
|
67
70
|
if sem_version_client < sem_version_server:
|
|
68
71
|
await websocket.send_text(
|
|
69
72
|
Mdata(
|
|
70
73
|
user_id=uuid,
|
|
71
|
-
data=f"Client version {client_version} is outdated. Please upgrade to {
|
|
74
|
+
data=f"Client version {client_version} is outdated. Please upgrade to {
|
|
75
|
+
CLIENT_VERSION_MINIMUM} or higher.",
|
|
72
76
|
).model_dump_json()
|
|
73
77
|
)
|
|
74
78
|
await websocket.close(
|
|
@@ -88,7 +92,8 @@ async def register_websocket(websocket: WebSocket, uuid: UUID) -> None:
|
|
|
88
92
|
while True:
|
|
89
93
|
received_data = await websocket.receive_text()
|
|
90
94
|
if uuid not in gpts:
|
|
91
|
-
raise fastapi.HTTPException(
|
|
95
|
+
raise fastapi.HTTPException(
|
|
96
|
+
status_code=400, detail="No call made")
|
|
92
97
|
gpts[uuid](received_data)
|
|
93
98
|
except WebSocketDisconnect:
|
|
94
99
|
# Remove the client if the WebSocket is disconnected
|
|
@@ -126,13 +131,13 @@ async def create_file(write_file_data: CreateFileNewWithUUID) -> str:
|
|
|
126
131
|
raise fastapi.HTTPException(status_code=500, detail="Timeout error")
|
|
127
132
|
|
|
128
133
|
|
|
129
|
-
class
|
|
134
|
+
class FileEditWithUUID(FileEdit):
|
|
130
135
|
user_id: UUID
|
|
131
136
|
|
|
132
137
|
|
|
133
138
|
@app.post("/v1/full_file_edit")
|
|
134
139
|
async def file_edit_find_replace(
|
|
135
|
-
file_edit_find_replace:
|
|
140
|
+
file_edit_find_replace: FileEditWithUUID,
|
|
136
141
|
) -> str:
|
|
137
142
|
user_id = file_edit_find_replace.user_id
|
|
138
143
|
if user_id not in clients:
|
|
@@ -257,6 +262,39 @@ async def bash_interaction(bash_interaction: BashInteractionWithUUID) -> str:
|
|
|
257
262
|
raise fastapi.HTTPException(status_code=500, detail="Timeout error")
|
|
258
263
|
|
|
259
264
|
|
|
265
|
+
class ReadFileWithUUID(ReadFile):
|
|
266
|
+
user_id: UUID
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
@app.post("/v1/read_file")
|
|
270
|
+
async def read_file_endpoint(read_file_data: ReadFileWithUUID) -> str:
|
|
271
|
+
user_id = read_file_data.user_id
|
|
272
|
+
if user_id not in clients:
|
|
273
|
+
return "Failure: id not found, ask the user to check it."
|
|
274
|
+
|
|
275
|
+
results: Optional[str] = None
|
|
276
|
+
|
|
277
|
+
def put_results(result: str) -> None:
|
|
278
|
+
nonlocal results
|
|
279
|
+
results = result
|
|
280
|
+
|
|
281
|
+
gpts[user_id] = put_results
|
|
282
|
+
|
|
283
|
+
await clients[user_id](
|
|
284
|
+
Mdata(data=ReadFile(file_path=read_file_data.file_path,
|
|
285
|
+
type=read_file_data.type
|
|
286
|
+
), user_id=user_id)
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
start_time = time.time()
|
|
290
|
+
while time.time() - start_time < 30:
|
|
291
|
+
if results is not None:
|
|
292
|
+
return results
|
|
293
|
+
await asyncio.sleep(0.1)
|
|
294
|
+
|
|
295
|
+
raise fastapi.HTTPException(status_code=500, detail="Timeout error")
|
|
296
|
+
|
|
297
|
+
|
|
260
298
|
app.mount("/static", StaticFiles(directory="static"), name="static")
|
|
261
299
|
|
|
262
300
|
|
wcgw/types_.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import re
|
|
1
2
|
from typing import Literal, Optional, Sequence
|
|
2
3
|
from pydantic import BaseModel
|
|
3
4
|
|
|
@@ -35,7 +36,7 @@ class BashInteraction(BaseModel):
|
|
|
35
36
|
|
|
36
37
|
class ReadImage(BaseModel):
|
|
37
38
|
file_path: str
|
|
38
|
-
type: Literal["ReadImage"]
|
|
39
|
+
type: Literal["ReadImage"]
|
|
39
40
|
|
|
40
41
|
|
|
41
42
|
class Writefile(BaseModel):
|
|
@@ -48,6 +49,11 @@ class CreateFileNew(BaseModel):
|
|
|
48
49
|
file_content: str
|
|
49
50
|
|
|
50
51
|
|
|
52
|
+
class ReadFile(BaseModel):
|
|
53
|
+
file_path: str # The path to the file to read
|
|
54
|
+
type: Literal["ReadFile"]
|
|
55
|
+
|
|
56
|
+
|
|
51
57
|
class FileEditFindReplace(BaseModel):
|
|
52
58
|
file_path: str
|
|
53
59
|
find_lines: str
|
|
@@ -58,6 +64,13 @@ class ResetShell(BaseModel):
|
|
|
58
64
|
should_reset: Literal[True] = True
|
|
59
65
|
|
|
60
66
|
|
|
61
|
-
class
|
|
67
|
+
class FileEdit(BaseModel):
|
|
62
68
|
file_path: str
|
|
63
|
-
|
|
69
|
+
file_edit_using_search_replace_blocks: str
|
|
70
|
+
|
|
71
|
+
def model_post_init(self, __context: object) -> None:
|
|
72
|
+
# Ensure first line is "<<<<<<< SEARCH"
|
|
73
|
+
|
|
74
|
+
if not re.match(r"^<<<<<<+\s*SEARCH\s*$", self.file_edit_using_search_replace_blocks.split("\n")[0]):
|
|
75
|
+
|
|
76
|
+
raise ValueError("First line of file_edit_using_search_replace_blocks must be '<<<<<<< SEARCH'")
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
wcgw/__init__.py,sha256=PNWvBvjUKA3aj4bHOtIqBKCAtOW88pr0hAXZ7RylVr8,68
|
|
2
|
+
wcgw/types_.py,sha256=a0sE4hPj4qylQFqMJwysLYy_FVYsueJTjq5fmHmNWGo,1855
|
|
3
|
+
wcgw/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
wcgw/client/__main__.py,sha256=ngI_vBcLAv7fJgmS4w4U7tuWtalGB8c7W5qebuT6Z6o,30
|
|
5
|
+
wcgw/client/anthropic_client.py,sha256=g5dj7tP_wkLMjQawil4PxhrEemlrIKYttWUXBI5SZ4Q,15260
|
|
6
|
+
wcgw/client/cli.py,sha256=Oja42CHkVO8puqOXflko9NeephYCMa85aBmQTEjBZtI,932
|
|
7
|
+
wcgw/client/common.py,sha256=grH-yV_4tnTQZ29xExn4YicGLxEq98z-HkEZwH0ReSg,1410
|
|
8
|
+
wcgw/client/diff-instructions.txt,sha256=I_qroE6_aMpWtOeywPSDCTxmz-P6VPopdp1kR2r0D8w,1375
|
|
9
|
+
wcgw/client/openai_client.py,sha256=Vsj8TzYj22fawU1LagAU7bn21Va50KXwpYZpGZ0BUZ0,17310
|
|
10
|
+
wcgw/client/openai_utils.py,sha256=YNwCsA-Wqq7jWrxP0rfQmBTb1dI0s7dWXzQqyTzOZT4,2629
|
|
11
|
+
wcgw/client/tools.py,sha256=NeGIZ8AJ9-XAiW6gUXf7GWs1UJjw8f7ecxUBBEAtyEI,25994
|
|
12
|
+
wcgw/relay/serve.py,sha256=sMbERQTM1GhpnPWLzdtpcb23TIsgbP-ZuqVj-vjp5Rw,8186
|
|
13
|
+
wcgw/relay/static/privacy.txt,sha256=s9qBdbx2SexCpC_z33sg16TptmAwDEehMCLz4L50JLc,529
|
|
14
|
+
wcgw-1.2.0.dist-info/METADATA,sha256=1AJa0M-m_tvHNpANqak4-q7bKTgg07_qgjddGwQaOhs,5255
|
|
15
|
+
wcgw-1.2.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
|
16
|
+
wcgw-1.2.0.dist-info/entry_points.txt,sha256=WlIB825-Vm9ZtNzgENQsbHj4DRMkbpVR7uSkQyBlaPA,93
|
|
17
|
+
wcgw-1.2.0.dist-info/RECORD,,
|
wcgw-1.1.2.dist-info/RECORD
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
wcgw/__init__.py,sha256=PNWvBvjUKA3aj4bHOtIqBKCAtOW88pr0hAXZ7RylVr8,68
|
|
2
|
-
wcgw/types_.py,sha256=-RbRO6aglz1tj014vvr6q8J8ly-tTacSu0hhask46qM,1424
|
|
3
|
-
wcgw/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
wcgw/client/__main__.py,sha256=ngI_vBcLAv7fJgmS4w4U7tuWtalGB8c7W5qebuT6Z6o,30
|
|
5
|
-
wcgw/client/anthropic_client.py,sha256=kswf7n1W7z1C8POWUnQLyESg7GtlgAoP-Yc4dCO86ts,14999
|
|
6
|
-
wcgw/client/cli.py,sha256=Oja42CHkVO8puqOXflko9NeephYCMa85aBmQTEjBZtI,932
|
|
7
|
-
wcgw/client/common.py,sha256=grH-yV_4tnTQZ29xExn4YicGLxEq98z-HkEZwH0ReSg,1410
|
|
8
|
-
wcgw/client/diff-instructions.txt,sha256=IcoPUZS5Y_fDlA5BuryIEGYykpnDtGUAP_oUNrZyC8U,1564
|
|
9
|
-
wcgw/client/openai_client.py,sha256=Qu5xjfL3zP7YkdDwowtyq2kAGYY1-YvNOt8FwdScoU8,16972
|
|
10
|
-
wcgw/client/openai_utils.py,sha256=YNwCsA-Wqq7jWrxP0rfQmBTb1dI0s7dWXzQqyTzOZT4,2629
|
|
11
|
-
wcgw/client/tools.py,sha256=-Of_Kknaf27fi76rhDdMeDTuHjTw_AKSesPKCDDqAVM,24833
|
|
12
|
-
wcgw/relay/serve.py,sha256=hUMNnf4KxF-clUYvky09bWQtuRvqJny71D93PppSpec,7236
|
|
13
|
-
wcgw/relay/static/privacy.txt,sha256=s9qBdbx2SexCpC_z33sg16TptmAwDEehMCLz4L50JLc,529
|
|
14
|
-
wcgw-1.1.2.dist-info/METADATA,sha256=f5t2PzR0Oz0BRTAMs2BwQC8Y5p7hXVUobut-PS1HIVQ,5255
|
|
15
|
-
wcgw-1.1.2.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
|
16
|
-
wcgw-1.1.2.dist-info/entry_points.txt,sha256=WlIB825-Vm9ZtNzgENQsbHj4DRMkbpVR7uSkQyBlaPA,93
|
|
17
|
-
wcgw-1.1.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|