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.

@@ -26,7 +26,8 @@ from ..types_ import (
26
26
  BashInteraction,
27
27
  CreateFileNew,
28
28
  FileEditFindReplace,
29
- FullFileEdit,
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 FullFileEdit.
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=FullFileEdit.model_json_schema(),
202
- name="FullFileEdit",
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
- - Do not use sudo. Do not use interactive commands.
219
- - Ask user for confirmation before running anything major
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 search block: <<<<<<< SEARCH
27
- 2. A contiguous chunk of lines to search for in the existing source code
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
- Use the *FULL* file path, as shown to you by the user.
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.
@@ -24,8 +24,9 @@ from ..types_ import (
24
24
  BashCommand,
25
25
  BashInteraction,
26
26
  CreateFileNew,
27
- FullFileEdit,
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
- config_file = os.path.join(my_dir, "..", "..", "..", "config.toml")
150
- with open(config_file) as f:
151
- config_json = toml.load(f)
152
- config = Config.model_validate(config_json)
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 FullFileEdit.""",
203
+ - For editing existing files, use FileEdit.""",
189
204
  ),
190
205
  openai.pydantic_function_tool(
191
- FullFileEdit,
206
+ FileEdit,
192
207
  description="""
193
208
  - Use absolute file path only.
194
209
  - Use ONLY SEARCH/REPLACE blocks to edit the file.
195
- - file_edit_using_searh_replace_blocks should start with <<<<<<< SEARCH
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
- - Do not use sudo. Do not use interactive commands.
216
- - Ask user for confirmation before running anything major
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
- FullFileEdit,
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(content_lines[i + j], find_lines[j])
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 : i + len(find_lines)]
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: FullFileEdit) -> str:
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("Failure: file_path should be absolute path")
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.file_edit_using_searh_replace_blocks.split("\n")
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(apply_diff_to, search_block_, replace_block_)
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("Failure: file_path should be absolute path")
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("> " + line for line in fedit.find_lines.split("\n"))
561
- in_string = "\n".join("< " + line for line in fedit.replace_with_lines.split("\n"))
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, fedit.replace_with_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
- | FullFileEdit
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 == "FullFileEdit":
635
- return FullFileEdit
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
- | FullFileEdit
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
- | FullFileEdit
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
- | FullFileEdit
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, FullFileEdit):
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, cost_per_1m_output_tokens=0.6)
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
- | FullFileEdit
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: ("", 0), None
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
- FullFileEdit,
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
- | FullFileEdit
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.1.0"
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(CLIENT_VERSION_MINIMUM)
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 {CLIENT_VERSION_MINIMUM} or higher.",
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(status_code=400, detail="No call made")
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 FullFileEditWithUUID(FullFileEdit):
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: FullFileEditWithUUID,
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"] = "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 FullFileEdit(BaseModel):
67
+ class FileEdit(BaseModel):
62
68
  file_path: str
63
- file_edit_using_searh_replace_blocks: str
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'")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: wcgw
3
- Version: 1.1.2
3
+ Version: 1.2.0
4
4
  Summary: What could go wrong giving full shell access to chatgpt?
5
5
  Project-URL: Homepage, https://github.com/rusiaaman/wcgw
6
6
  Author-email: Aman Rusia <gapypi@arcfu.com>
@@ -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,,
@@ -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