wcgw 1.2.1__tar.gz → 1.3.0__tar.gz
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-1.2.1 → wcgw-1.3.0}/PKG-INFO +1 -1
- {wcgw-1.2.1 → wcgw-1.3.0}/gpt_action_json_schema.json +74 -2
- {wcgw-1.2.1 → wcgw-1.3.0}/gpt_instructions.txt +31 -8
- {wcgw-1.2.1 → wcgw-1.3.0}/pyproject.toml +1 -1
- {wcgw-1.2.1 → wcgw-1.3.0}/src/wcgw/client/anthropic_client.py +10 -7
- {wcgw-1.2.1 → wcgw-1.3.0}/src/wcgw/client/diff-instructions.txt +28 -4
- {wcgw-1.2.1 → wcgw-1.3.0}/src/wcgw/client/openai_client.py +11 -9
- {wcgw-1.2.1 → wcgw-1.3.0}/src/wcgw/client/tools.py +123 -71
- {wcgw-1.2.1 → wcgw-1.3.0}/src/wcgw/relay/serve.py +35 -6
- {wcgw-1.2.1 → wcgw-1.3.0}/src/wcgw/types_.py +4 -22
- {wcgw-1.2.1 → wcgw-1.3.0}/.github/workflows/python-publish.yml +0 -0
- {wcgw-1.2.1 → wcgw-1.3.0}/.github/workflows/python-tests.yml +0 -0
- {wcgw-1.2.1 → wcgw-1.3.0}/.gitignore +0 -0
- {wcgw-1.2.1 → wcgw-1.3.0}/.python-version +0 -0
- {wcgw-1.2.1 → wcgw-1.3.0}/.vscode/settings.json +0 -0
- {wcgw-1.2.1 → wcgw-1.3.0}/README.md +0 -0
- {wcgw-1.2.1 → wcgw-1.3.0}/src/__init__.py +0 -0
- {wcgw-1.2.1 → wcgw-1.3.0}/src/wcgw/__init__.py +0 -0
- {wcgw-1.2.1 → wcgw-1.3.0}/src/wcgw/client/__init__.py +0 -0
- {wcgw-1.2.1 → wcgw-1.3.0}/src/wcgw/client/__main__.py +0 -0
- {wcgw-1.2.1 → wcgw-1.3.0}/src/wcgw/client/cli.py +0 -0
- {wcgw-1.2.1 → wcgw-1.3.0}/src/wcgw/client/common.py +0 -0
- {wcgw-1.2.1 → wcgw-1.3.0}/src/wcgw/client/openai_utils.py +0 -0
- {wcgw-1.2.1 → wcgw-1.3.0}/src/wcgw/relay/static/privacy.txt +0 -0
- {wcgw-1.2.1 → wcgw-1.3.0}/static/ss1.png +0 -0
- {wcgw-1.2.1 → wcgw-1.3.0}/tests/test_basic.py +0 -0
- {wcgw-1.2.1 → wcgw-1.3.0}/tests/test_tools.py +0 -0
- {wcgw-1.2.1 → wcgw-1.3.0}/uv.lock +0 -0
|
@@ -249,12 +249,60 @@
|
|
|
249
249
|
}
|
|
250
250
|
}
|
|
251
251
|
}
|
|
252
|
+
},
|
|
253
|
+
"/v1/initialize": {
|
|
254
|
+
"post": {
|
|
255
|
+
"x-openai-isConsequential": false,
|
|
256
|
+
"summary": "Initialize",
|
|
257
|
+
"operationId": "initialize_v1_initialize_post",
|
|
258
|
+
"requestBody": {
|
|
259
|
+
"content": {
|
|
260
|
+
"application/json": {
|
|
261
|
+
"schema": {
|
|
262
|
+
"$ref": "#/components/schemas/InitializeWithUUID"
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
"required": true
|
|
267
|
+
},
|
|
268
|
+
"responses": {
|
|
269
|
+
"200": {
|
|
270
|
+
"description": "Successful Response",
|
|
271
|
+
"content": {
|
|
272
|
+
"application/json": {
|
|
273
|
+
"schema": {
|
|
274
|
+
"type": "string",
|
|
275
|
+
"title": "Response Initialize V1 Initialize Post"
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
"422": {
|
|
281
|
+
"description": "Validation Error",
|
|
282
|
+
"content": {
|
|
283
|
+
"application/json": {
|
|
284
|
+
"schema": {
|
|
285
|
+
"$ref": "#/components/schemas/HTTPValidationError"
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
252
292
|
}
|
|
253
293
|
},
|
|
254
294
|
"components": {
|
|
255
295
|
"schemas": {
|
|
256
296
|
"BashInteractionWithUUID": {
|
|
257
297
|
"properties": {
|
|
298
|
+
"type": {
|
|
299
|
+
"type": "string",
|
|
300
|
+
"enum": [
|
|
301
|
+
"BashInteraction"
|
|
302
|
+
],
|
|
303
|
+
"const": "BashInteraction",
|
|
304
|
+
"title": "Type"
|
|
305
|
+
},
|
|
258
306
|
"send_text": {
|
|
259
307
|
"anyOf": [
|
|
260
308
|
{
|
|
@@ -312,6 +360,7 @@
|
|
|
312
360
|
},
|
|
313
361
|
"type": "object",
|
|
314
362
|
"required": [
|
|
363
|
+
"type",
|
|
315
364
|
"user_id"
|
|
316
365
|
],
|
|
317
366
|
"title": "BashInteractionWithUUID"
|
|
@@ -396,6 +445,29 @@
|
|
|
396
445
|
"type": "object",
|
|
397
446
|
"title": "HTTPValidationError"
|
|
398
447
|
},
|
|
448
|
+
"InitializeWithUUID": {
|
|
449
|
+
"properties": {
|
|
450
|
+
"type": {
|
|
451
|
+
"type": "string",
|
|
452
|
+
"enum": [
|
|
453
|
+
"Initialize"
|
|
454
|
+
],
|
|
455
|
+
"const": "Initialize",
|
|
456
|
+
"title": "Type"
|
|
457
|
+
},
|
|
458
|
+
"user_id": {
|
|
459
|
+
"type": "string",
|
|
460
|
+
"format": "uuid",
|
|
461
|
+
"title": "User Id"
|
|
462
|
+
}
|
|
463
|
+
},
|
|
464
|
+
"type": "object",
|
|
465
|
+
"required": [
|
|
466
|
+
"type",
|
|
467
|
+
"user_id"
|
|
468
|
+
],
|
|
469
|
+
"title": "InitializeWithUUID"
|
|
470
|
+
},
|
|
399
471
|
"ReadFileWithUUID": {
|
|
400
472
|
"properties": {
|
|
401
473
|
"file_path": {
|
|
@@ -432,8 +504,7 @@
|
|
|
432
504
|
true
|
|
433
505
|
],
|
|
434
506
|
"const": true,
|
|
435
|
-
"title": "Should Reset"
|
|
436
|
-
"default": true
|
|
507
|
+
"title": "Should Reset"
|
|
437
508
|
},
|
|
438
509
|
"user_id": {
|
|
439
510
|
"type": "string",
|
|
@@ -443,6 +514,7 @@
|
|
|
443
514
|
},
|
|
444
515
|
"type": "object",
|
|
445
516
|
"required": [
|
|
517
|
+
"should_reset",
|
|
446
518
|
"user_id"
|
|
447
519
|
],
|
|
448
520
|
"title": "ResetShellWithUUID"
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
You're an expert software engineer with shell and code knowledge.
|
|
2
2
|
|
|
3
3
|
Instructions:
|
|
4
|
-
|
|
4
|
+
|
|
5
5
|
- You should use the provided bash execution, reading and writing file tools to complete objective.
|
|
6
6
|
- First understand about the project by getting the folder structure (ignoring .git, node_modules, venv, etc.)
|
|
7
7
|
- Always read relevant files before editing.
|
|
8
8
|
- Do not provide code snippets unless asked by the user, instead directly edit the code.
|
|
9
9
|
|
|
10
|
+
Instructions for `Initialize`:
|
|
11
|
+
- Always call this at the start of the conversation.
|
|
10
12
|
|
|
11
13
|
Instructions for `BashCommand`:
|
|
12
14
|
- Execute a bash command. This is stateful (beware with subsequent calls).
|
|
@@ -14,6 +16,7 @@ Instructions for `BashCommand`:
|
|
|
14
16
|
- Status of the command and the current working directory will always be returned at the end.
|
|
15
17
|
- Optionally `exit shell has restarted` is the output, in which case environment resets, you can run fresh commands.
|
|
16
18
|
- The first line might be `(...truncated)` if the output is too long.
|
|
19
|
+
- The control will return to you in 5 seconds regardless of the status. For heavy commands, keep checking status using BashInteraction till they are finished.
|
|
17
20
|
|
|
18
21
|
Instructions for `Read File`
|
|
19
22
|
- Read full content of a file.
|
|
@@ -39,16 +42,35 @@ Instructions for `FileEdit`:
|
|
|
39
42
|
- Use absolute file path only.
|
|
40
43
|
- Use SEARCH/REPLACE blocks to edit the file.
|
|
41
44
|
Only edit the files using the following SEARCH/REPLACE blocks.
|
|
45
|
+
|
|
42
46
|
```
|
|
43
47
|
<<<<<<< SEARCH
|
|
44
|
-
|
|
48
|
+
def hello():
|
|
49
|
+
"print a greeting"
|
|
50
|
+
|
|
51
|
+
print("hello")
|
|
52
|
+
=======
|
|
53
|
+
from hello import hello as hello_renamed
|
|
54
|
+
>>>>>>> REPLACE
|
|
55
|
+
<<<<<<< SEARCH
|
|
56
|
+
def call_hello():
|
|
57
|
+
"call hello"
|
|
58
|
+
|
|
59
|
+
hello()
|
|
45
60
|
=======
|
|
46
|
-
|
|
61
|
+
def call_hello_renamed():
|
|
62
|
+
"call hello renamed"
|
|
63
|
+
|
|
64
|
+
hello_renamed()
|
|
47
65
|
>>>>>>> REPLACE
|
|
48
66
|
<<<<<<< SEARCH
|
|
49
|
-
|
|
67
|
+
impl1()
|
|
68
|
+
hello()
|
|
69
|
+
impl2()
|
|
50
70
|
=======
|
|
51
|
-
|
|
71
|
+
impl1()
|
|
72
|
+
hello_renamed()
|
|
73
|
+
impl2()
|
|
52
74
|
>>>>>>> REPLACE
|
|
53
75
|
```
|
|
54
76
|
|
|
@@ -61,7 +83,7 @@ Instructions for `FileEdit`:
|
|
|
61
83
|
4. The lines to replace into the source code
|
|
62
84
|
5. The end of the replace block: >>>>>>> REPLACE
|
|
63
85
|
|
|
64
|
-
Every "<<<<<<< SEARCH" section must *EXACTLY MATCH* the existing file content, character for character, including all comments, docstrings, etc.
|
|
86
|
+
Every "<<<<<<< SEARCH" section must *EXACTLY MATCH* the existing file content, character for character, including all comments, docstrings, whitespaces etc.
|
|
65
87
|
|
|
66
88
|
*SEARCH/REPLACE* blocks will *only* replace the first match occurrence.
|
|
67
89
|
Including multiple unique *SEARCH/REPLACE* blocks if needed.
|
|
@@ -72,11 +94,12 @@ Instructions for `FileEdit`:
|
|
|
72
94
|
Include just the changing lines, and a few surrounding lines if needed for uniqueness.
|
|
73
95
|
Do not include long runs of unchanging lines in *SEARCH/REPLACE* blocks.
|
|
74
96
|
|
|
75
|
-
|
|
97
|
+
Preserve leading spaces and indentations in both SEARCH and REPLACE blocks.
|
|
76
98
|
|
|
77
99
|
---
|
|
78
100
|
Always run `pwd` if you get any file or directory not found error to make sure you're not lost, or to get absolute cwd.
|
|
79
101
|
|
|
80
102
|
Always write production ready, syntactically correct code.
|
|
81
103
|
---
|
|
82
|
-
Ask the user for the user_id `UUID` if they haven't provided in the first message.
|
|
104
|
+
- Ask the user for the user_id `UUID` if they haven't provided in the first message.
|
|
105
|
+
- Call "Initialize" as soon as you get the UUID.
|
|
@@ -165,6 +165,7 @@ def loop(
|
|
|
165
165
|
- Optionally `exit shell has restarted` is the output, in which case environment resets, you can run fresh commands.
|
|
166
166
|
- The first line might be `(...truncated)` if the output is too long.
|
|
167
167
|
- Always run `pwd` if you get any file or directory not found error to make sure you're not lost.
|
|
168
|
+
- The control will return to you in 5 seconds regardless of the status. For heavy commands, keep checking status using BashInteraction till they are finished.
|
|
168
169
|
""",
|
|
169
170
|
),
|
|
170
171
|
ToolParam(
|
|
@@ -193,7 +194,7 @@ def loop(
|
|
|
193
194
|
- Write content to a new file. Provide file path and content. Use this instead of BashCommand for writing new files.
|
|
194
195
|
- This doesn't create any directories, please create directories using `mkdir -p` BashCommand.
|
|
195
196
|
- Provide absolute file path only.
|
|
196
|
-
- For editing existing files, use FileEdit.
|
|
197
|
+
- For editing existing files, use FileEdit instead of this tool.
|
|
197
198
|
""",
|
|
198
199
|
),
|
|
199
200
|
ToolParam(
|
|
@@ -219,17 +220,19 @@ def loop(
|
|
|
219
220
|
uname_machine = os.uname().machine
|
|
220
221
|
|
|
221
222
|
system = f"""
|
|
222
|
-
You're
|
|
223
|
+
You're an expert software engineer with shell and code knowledge.
|
|
223
224
|
|
|
224
225
|
Instructions:
|
|
225
226
|
|
|
226
|
-
- You should use the provided bash execution
|
|
227
|
-
- First understand about the project by
|
|
228
|
-
- Always read relevant files before
|
|
229
|
-
|
|
227
|
+
- You should use the provided bash execution, reading and writing file tools to complete objective.
|
|
228
|
+
- First understand about the project by getting the folder structure (ignoring .git, node_modules, venv, etc.)
|
|
229
|
+
- Always read relevant files before editing.
|
|
230
|
+
- Do not provide code snippets unless asked by the user, instead directly edit the code.
|
|
231
|
+
|
|
230
232
|
System information:
|
|
231
233
|
- System: {uname_sysname}
|
|
232
234
|
- Machine: {uname_machine}
|
|
235
|
+
- Current directory: {os.getcwd()}
|
|
233
236
|
"""
|
|
234
237
|
|
|
235
238
|
with open(os.path.join(os.path.dirname(__file__), "diff-instructions.txt")) as f:
|
|
@@ -360,7 +363,7 @@ System information:
|
|
|
360
363
|
enc,
|
|
361
364
|
limit - cost,
|
|
362
365
|
loop,
|
|
363
|
-
max_tokens=
|
|
366
|
+
max_tokens=8000,
|
|
364
367
|
)
|
|
365
368
|
except Exception as e:
|
|
366
369
|
output_or_done = (
|
|
@@ -1,15 +1,39 @@
|
|
|
1
1
|
|
|
2
|
-
Instructions for
|
|
2
|
+
Instructions for editing files.
|
|
3
|
+
|
|
4
|
+
|
|
3
5
|
Only edit the files using the following SEARCH/REPLACE blocks.
|
|
4
6
|
```
|
|
7
|
+
file_edit_using_search_replace_blocks="""
|
|
5
8
|
<<<<<<< SEARCH
|
|
6
9
|
def hello():
|
|
7
10
|
"print a greeting"
|
|
8
11
|
|
|
9
12
|
print("hello")
|
|
10
13
|
=======
|
|
11
|
-
from hello import hello
|
|
14
|
+
from hello import hello as hello_renamed
|
|
15
|
+
>>>>>>> REPLACE
|
|
16
|
+
<<<<<<< SEARCH
|
|
17
|
+
def call_hello():
|
|
18
|
+
"call hello"
|
|
19
|
+
|
|
20
|
+
hello()
|
|
21
|
+
=======
|
|
22
|
+
def call_hello_renamed():
|
|
23
|
+
"call hello renamed"
|
|
24
|
+
|
|
25
|
+
hello_renamed()
|
|
26
|
+
>>>>>>> REPLACE
|
|
27
|
+
<<<<<<< SEARCH
|
|
28
|
+
impl1()
|
|
29
|
+
hello()
|
|
30
|
+
impl2()
|
|
31
|
+
=======
|
|
32
|
+
impl1()
|
|
33
|
+
hello_renamed()
|
|
34
|
+
impl2()
|
|
12
35
|
>>>>>>> REPLACE
|
|
36
|
+
"""
|
|
13
37
|
```
|
|
14
38
|
|
|
15
39
|
# *SEARCH/REPLACE block* Rules:
|
|
@@ -21,7 +45,7 @@ Every *SEARCH/REPLACE block* must use this format:
|
|
|
21
45
|
4. The lines to replace into the source code
|
|
22
46
|
5. The end of the replace block: >>>>>>> REPLACE
|
|
23
47
|
|
|
24
|
-
Every "<<<<<<< SEARCH" section must *EXACTLY MATCH* the existing file content, character for character, including all comments, docstrings, etc.
|
|
48
|
+
Every "<<<<<<< SEARCH" section must *EXACTLY MATCH* the existing file content, character for character, including all comments, docstrings, whitespaces, etc.
|
|
25
49
|
|
|
26
50
|
*SEARCH/REPLACE* blocks will *only* replace the first match occurrence.
|
|
27
51
|
Including multiple unique *SEARCH/REPLACE* blocks if needed.
|
|
@@ -32,4 +56,4 @@ Break large *SEARCH/REPLACE* blocks into a series of smaller blocks that each ch
|
|
|
32
56
|
Include just the changing lines, and a few surrounding lines if needed for uniqueness.
|
|
33
57
|
Do not include long runs of unchanging lines in *SEARCH/REPLACE* blocks.
|
|
34
58
|
|
|
35
|
-
|
|
59
|
+
Preserve leading spaces and indentations in both SEARCH and REPLACE blocks.
|
|
@@ -148,8 +148,7 @@ def loop(
|
|
|
148
148
|
my_dir = os.path.dirname(__file__)
|
|
149
149
|
|
|
150
150
|
config = Config(
|
|
151
|
-
model=cast(
|
|
152
|
-
Models, os.getenv("OPENAI_MODEL", "gpt-4o-2024-08-06").lower()),
|
|
151
|
+
model=cast(Models, os.getenv("OPENAI_MODEL", "gpt-4o-2024-08-06").lower()),
|
|
153
152
|
cost_limit=0.1,
|
|
154
153
|
cost_unit="$",
|
|
155
154
|
cost_file={
|
|
@@ -177,6 +176,7 @@ def loop(
|
|
|
177
176
|
- Optionally `exit shell has restarted` is the output, in which case environment resets, you can run fresh commands.
|
|
178
177
|
- The first line might be `(...truncated)` if the output is too long.
|
|
179
178
|
- Always run `pwd` if you get any file or directory not found error to make sure you're not lost.
|
|
179
|
+
- The control will return to you in 5 seconds regardless of the status. For heavy commands, keep checking status using BashInteraction till they are finished.
|
|
180
180
|
""",
|
|
181
181
|
),
|
|
182
182
|
openai.pydantic_function_tool(
|
|
@@ -201,7 +201,7 @@ def loop(
|
|
|
201
201
|
- Write content to a new file. Provide file path and content. Use this instead of BashCommand for writing new files.
|
|
202
202
|
- This doesn't create any directories, please create directories using `mkdir -p` BashCommand.
|
|
203
203
|
- Provide absolute file path only.
|
|
204
|
-
- For editing existing files, use FileEdit.""",
|
|
204
|
+
- For editing existing files, use FileEdit instead of this tool.""",
|
|
205
205
|
),
|
|
206
206
|
openai.pydantic_function_tool(
|
|
207
207
|
FileEdit,
|
|
@@ -223,17 +223,19 @@ def loop(
|
|
|
223
223
|
uname_machine = os.uname().machine
|
|
224
224
|
|
|
225
225
|
system = f"""
|
|
226
|
-
You're
|
|
226
|
+
You're an expert software engineer with shell and code knowledge.
|
|
227
227
|
|
|
228
228
|
Instructions:
|
|
229
229
|
|
|
230
|
-
- You should use the provided bash execution
|
|
231
|
-
- First understand about the project by
|
|
232
|
-
- Always read relevant files before
|
|
233
|
-
|
|
230
|
+
- You should use the provided bash execution, reading and writing file tools to complete objective.
|
|
231
|
+
- First understand about the project by getting the folder structure (ignoring .git, node_modules, venv, etc.)
|
|
232
|
+
- Always read relevant files before editing.
|
|
233
|
+
- Do not provide code snippets unless asked by the user, instead directly edit the code.
|
|
234
|
+
|
|
234
235
|
System information:
|
|
235
236
|
- System: {uname_sysname}
|
|
236
237
|
- Machine: {uname_machine}
|
|
238
|
+
- Current directory: {os.getcwd()}
|
|
237
239
|
|
|
238
240
|
"""
|
|
239
241
|
|
|
@@ -340,7 +342,7 @@ System information:
|
|
|
340
342
|
enc,
|
|
341
343
|
limit - cost,
|
|
342
344
|
loop,
|
|
343
|
-
max_tokens=
|
|
345
|
+
max_tokens=8000,
|
|
344
346
|
)
|
|
345
347
|
except Exception as e:
|
|
346
348
|
output_or_done = (
|
|
@@ -45,7 +45,7 @@ from openai.types.chat import (
|
|
|
45
45
|
ChatCompletionMessage,
|
|
46
46
|
ParsedChatCompletionMessage,
|
|
47
47
|
)
|
|
48
|
-
from nltk.metrics.distance import edit_distance
|
|
48
|
+
from nltk.metrics.distance import edit_distance # type: ignore[import-untyped]
|
|
49
49
|
|
|
50
50
|
from ..types_ import (
|
|
51
51
|
BashCommand,
|
|
@@ -53,6 +53,7 @@ from ..types_ import (
|
|
|
53
53
|
CreateFileNew,
|
|
54
54
|
FileEditFindReplace,
|
|
55
55
|
FileEdit,
|
|
56
|
+
Initialize,
|
|
56
57
|
ReadFile,
|
|
57
58
|
ReadImage,
|
|
58
59
|
ResetShell,
|
|
@@ -155,6 +156,16 @@ BASH_STATE: BASH_CLF_OUTPUT = "repl"
|
|
|
155
156
|
CWD = os.getcwd()
|
|
156
157
|
|
|
157
158
|
|
|
159
|
+
def initial_info() -> str:
|
|
160
|
+
uname_sysname = os.uname().sysname
|
|
161
|
+
uname_machine = os.uname().machine
|
|
162
|
+
return f"""
|
|
163
|
+
System: {uname_sysname}
|
|
164
|
+
Machine: {uname_machine}
|
|
165
|
+
Current working directory: {CWD}
|
|
166
|
+
"""
|
|
167
|
+
|
|
168
|
+
|
|
158
169
|
def reset_shell() -> str:
|
|
159
170
|
global SHELL, BASH_STATE, CWD
|
|
160
171
|
SHELL.close(True)
|
|
@@ -251,55 +262,69 @@ def execute_bash(
|
|
|
251
262
|
)
|
|
252
263
|
|
|
253
264
|
SHELL.sendline(command)
|
|
254
|
-
|
|
255
|
-
console.print(f"Sending special sequence: {bash_arg.send_specials}")
|
|
256
|
-
for char in bash_arg.send_specials:
|
|
257
|
-
if char == "Key-up":
|
|
258
|
-
SHELL.send("\033[A")
|
|
259
|
-
elif char == "Key-down":
|
|
260
|
-
SHELL.send("\033[B")
|
|
261
|
-
elif char == "Key-left":
|
|
262
|
-
SHELL.send("\033[D")
|
|
263
|
-
elif char == "Key-right":
|
|
264
|
-
SHELL.send("\033[C")
|
|
265
|
-
elif char == "Enter":
|
|
266
|
-
SHELL.send("\n")
|
|
267
|
-
elif char == "Ctrl-c":
|
|
268
|
-
SHELL.sendintr()
|
|
269
|
-
is_interrupt = True
|
|
270
|
-
elif char == "Ctrl-d":
|
|
271
|
-
SHELL.sendintr()
|
|
272
|
-
is_interrupt = True
|
|
273
|
-
elif char == "Ctrl-z":
|
|
274
|
-
SHELL.send("\x1a")
|
|
275
|
-
else:
|
|
276
|
-
raise Exception(f"Unknown special character: {char}")
|
|
277
|
-
elif bash_arg.send_ascii:
|
|
278
|
-
console.print(f"Sending ASCII sequence: {bash_arg.send_ascii}")
|
|
279
|
-
for ascii_char in bash_arg.send_ascii:
|
|
280
|
-
SHELL.send(chr(ascii_char))
|
|
281
|
-
if ascii_char == 3:
|
|
282
|
-
is_interrupt = True
|
|
265
|
+
|
|
283
266
|
else:
|
|
284
|
-
if
|
|
267
|
+
if (
|
|
268
|
+
sum(
|
|
269
|
+
[
|
|
270
|
+
int(bool(bash_arg.send_text)),
|
|
271
|
+
int(bool(bash_arg.send_specials)),
|
|
272
|
+
int(bool(bash_arg.send_ascii)),
|
|
273
|
+
]
|
|
274
|
+
)
|
|
275
|
+
!= 1
|
|
276
|
+
):
|
|
285
277
|
return (
|
|
286
|
-
"Failure:
|
|
278
|
+
"Failure: exactly one of send_text, send_specials or send_ascii should be provided",
|
|
287
279
|
0.0,
|
|
288
280
|
)
|
|
281
|
+
if bash_arg.send_specials:
|
|
282
|
+
console.print(f"Sending special sequence: {bash_arg.send_specials}")
|
|
283
|
+
for char in bash_arg.send_specials:
|
|
284
|
+
if char == "Key-up":
|
|
285
|
+
SHELL.send("\033[A")
|
|
286
|
+
elif char == "Key-down":
|
|
287
|
+
SHELL.send("\033[B")
|
|
288
|
+
elif char == "Key-left":
|
|
289
|
+
SHELL.send("\033[D")
|
|
290
|
+
elif char == "Key-right":
|
|
291
|
+
SHELL.send("\033[C")
|
|
292
|
+
elif char == "Enter":
|
|
293
|
+
SHELL.send("\n")
|
|
294
|
+
elif char == "Ctrl-c":
|
|
295
|
+
SHELL.sendintr()
|
|
296
|
+
is_interrupt = True
|
|
297
|
+
elif char == "Ctrl-d":
|
|
298
|
+
SHELL.sendintr()
|
|
299
|
+
is_interrupt = True
|
|
300
|
+
elif char == "Ctrl-z":
|
|
301
|
+
SHELL.send("\x1a")
|
|
302
|
+
else:
|
|
303
|
+
raise Exception(f"Unknown special character: {char}")
|
|
304
|
+
elif bash_arg.send_ascii:
|
|
305
|
+
console.print(f"Sending ASCII sequence: {bash_arg.send_ascii}")
|
|
306
|
+
for ascii_char in bash_arg.send_ascii:
|
|
307
|
+
SHELL.send(chr(ascii_char))
|
|
308
|
+
if ascii_char == 3:
|
|
309
|
+
is_interrupt = True
|
|
310
|
+
else:
|
|
311
|
+
if bash_arg.send_text is None:
|
|
312
|
+
return (
|
|
313
|
+
"Failure: at least one of send_text, send_specials or send_ascii should be provided",
|
|
314
|
+
0.0,
|
|
315
|
+
)
|
|
289
316
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
)
|
|
301
|
-
console.print(f"Interact text: {bash_arg.send_text}")
|
|
302
|
-
SHELL.sendline(bash_arg.send_text)
|
|
317
|
+
updated_repl_mode = update_repl_prompt(bash_arg.send_text)
|
|
318
|
+
if updated_repl_mode:
|
|
319
|
+
BASH_STATE = "repl"
|
|
320
|
+
response = "Prompt updated, you can execute REPL lines using BashCommand now"
|
|
321
|
+
console.print(response)
|
|
322
|
+
return (
|
|
323
|
+
response,
|
|
324
|
+
0,
|
|
325
|
+
)
|
|
326
|
+
console.print(f"Interact text: {bash_arg.send_text}")
|
|
327
|
+
SHELL.sendline(bash_arg.send_text)
|
|
303
328
|
|
|
304
329
|
BASH_STATE = "repl"
|
|
305
330
|
|
|
@@ -318,7 +343,7 @@ def execute_bash(
|
|
|
318
343
|
tokens = enc.encode(text)
|
|
319
344
|
|
|
320
345
|
if max_tokens and len(tokens) >= max_tokens:
|
|
321
|
-
text = "...(truncated)\n" + enc.decode(tokens[-(max_tokens - 1):])
|
|
346
|
+
text = "...(truncated)\n" + enc.decode(tokens[-(max_tokens - 1) :])
|
|
322
347
|
|
|
323
348
|
if is_interrupt:
|
|
324
349
|
text = (
|
|
@@ -345,7 +370,7 @@ Otherwise, you may want to try Ctrl-c again or program specific exit interactive
|
|
|
345
370
|
|
|
346
371
|
tokens = enc.encode(output)
|
|
347
372
|
if max_tokens and len(tokens) >= max_tokens:
|
|
348
|
-
output = "...(truncated)\n" + enc.decode(tokens[-(max_tokens - 1):])
|
|
373
|
+
output = "...(truncated)\n" + enc.decode(tokens[-(max_tokens - 1) :])
|
|
349
374
|
|
|
350
375
|
try:
|
|
351
376
|
exit_status = get_status()
|
|
@@ -420,7 +445,7 @@ def read_image_from_shell(file_path: str) -> ImageData:
|
|
|
420
445
|
image_bytes = image_file.read()
|
|
421
446
|
image_b64 = base64.b64encode(image_bytes).decode("utf-8")
|
|
422
447
|
image_type = mimetypes.guess_type(file_path)[0]
|
|
423
|
-
return ImageData(media_type=image_type, data=image_b64)
|
|
448
|
+
return ImageData(media_type=image_type, data=image_b64) # type: ignore
|
|
424
449
|
|
|
425
450
|
|
|
426
451
|
def write_file(writefile: Writefile | CreateFileNew, error_on_exist: bool) -> str:
|
|
@@ -472,15 +497,21 @@ def find_least_edit_distance_substring(
|
|
|
472
497
|
edit_distance_sum = 0
|
|
473
498
|
for j in range(len(find_lines)):
|
|
474
499
|
if (i + j) < len(content_lines):
|
|
475
|
-
edit_distance_sum += edit_distance(
|
|
476
|
-
content_lines[i + j], find_lines[j])
|
|
500
|
+
edit_distance_sum += edit_distance(content_lines[i + j], find_lines[j])
|
|
477
501
|
else:
|
|
478
502
|
edit_distance_sum += len(find_lines[j])
|
|
479
503
|
if edit_distance_sum < min_edit_distance:
|
|
480
504
|
min_edit_distance = edit_distance_sum
|
|
481
505
|
orig_start_index = new_to_original_indices[i]
|
|
482
|
-
orig_end_index =
|
|
483
|
-
|
|
506
|
+
orig_end_index = (
|
|
507
|
+
new_to_original_indices.get(
|
|
508
|
+
i + len(find_lines) - 1, len(orig_content_lines) - 1
|
|
509
|
+
)
|
|
510
|
+
+ 1
|
|
511
|
+
)
|
|
512
|
+
min_edit_distance_lines = orig_content_lines[
|
|
513
|
+
orig_start_index:orig_end_index
|
|
514
|
+
]
|
|
484
515
|
return "\n".join(min_edit_distance_lines), min_edit_distance
|
|
485
516
|
|
|
486
517
|
|
|
@@ -509,7 +540,8 @@ def do_diff_edit(fedit: FileEdit) -> str:
|
|
|
509
540
|
|
|
510
541
|
if not os.path.isabs(fedit.file_path):
|
|
511
542
|
raise Exception(
|
|
512
|
-
f"Failure: file_path should be absolute path, current working directory is {CWD}"
|
|
543
|
+
f"Failure: file_path should be absolute path, current working directory is {CWD}"
|
|
544
|
+
)
|
|
513
545
|
else:
|
|
514
546
|
path_ = fedit.file_path
|
|
515
547
|
|
|
@@ -519,7 +551,16 @@ def do_diff_edit(fedit: FileEdit) -> str:
|
|
|
519
551
|
with open(path_) as f:
|
|
520
552
|
apply_diff_to = f.read()
|
|
521
553
|
|
|
554
|
+
fedit.file_edit_using_search_replace_blocks = (
|
|
555
|
+
fedit.file_edit_using_search_replace_blocks.strip()
|
|
556
|
+
)
|
|
522
557
|
lines = fedit.file_edit_using_search_replace_blocks.split("\n")
|
|
558
|
+
|
|
559
|
+
if not lines or not re.match(r"^<<<<<<+\s*SEARCH\s*$", lines[0]):
|
|
560
|
+
raise Exception(
|
|
561
|
+
"Error: first line should be `<<<<<< SEARCH` to start a search-replace block"
|
|
562
|
+
)
|
|
563
|
+
|
|
523
564
|
n_lines = len(lines)
|
|
524
565
|
i = 0
|
|
525
566
|
replacement_count = 0
|
|
@@ -539,15 +580,14 @@ def do_diff_edit(fedit: FileEdit) -> str:
|
|
|
539
580
|
|
|
540
581
|
for line in search_block:
|
|
541
582
|
console.log("> " + line)
|
|
542
|
-
console.log("
|
|
583
|
+
console.log("=======")
|
|
543
584
|
for line in replace_block:
|
|
544
585
|
console.log("< " + line)
|
|
545
|
-
|
|
586
|
+
console.log("\n\n\n\n")
|
|
546
587
|
search_block_ = "\n".join(search_block)
|
|
547
588
|
replace_block_ = "\n".join(replace_block)
|
|
548
589
|
|
|
549
|
-
apply_diff_to = edit_content(
|
|
550
|
-
apply_diff_to, search_block_, replace_block_)
|
|
590
|
+
apply_diff_to = edit_content(apply_diff_to, search_block_, replace_block_)
|
|
551
591
|
replacement_count += 1
|
|
552
592
|
else:
|
|
553
593
|
i += 1
|
|
@@ -566,7 +606,8 @@ def do_diff_edit(fedit: FileEdit) -> str:
|
|
|
566
606
|
def file_edit(fedit: FileEditFindReplace) -> str:
|
|
567
607
|
if not os.path.isabs(fedit.file_path):
|
|
568
608
|
raise Exception(
|
|
569
|
-
f"Failure: file_path should be absolute path, current working directory is {CWD}"
|
|
609
|
+
f"Failure: file_path should be absolute path, current working directory is {CWD}"
|
|
610
|
+
)
|
|
570
611
|
else:
|
|
571
612
|
path_ = fedit.file_path
|
|
572
613
|
|
|
@@ -576,17 +617,14 @@ def file_edit(fedit: FileEditFindReplace) -> str:
|
|
|
576
617
|
if not fedit.find_lines:
|
|
577
618
|
raise Exception("Error: `find_lines` cannot be empty")
|
|
578
619
|
|
|
579
|
-
out_string = "\n".join(
|
|
580
|
-
|
|
581
|
-
in_string = "\n".join(
|
|
582
|
-
"< " + line for line in fedit.replace_with_lines.split("\n"))
|
|
620
|
+
out_string = "\n".join("> " + line for line in fedit.find_lines.split("\n"))
|
|
621
|
+
in_string = "\n".join("< " + line for line in fedit.replace_with_lines.split("\n"))
|
|
583
622
|
console.log(f"Editing file: {path_}\n---\n{out_string}\n---\n{in_string}\n---")
|
|
584
623
|
try:
|
|
585
624
|
with open(path_) as f:
|
|
586
625
|
content = f.read()
|
|
587
626
|
|
|
588
|
-
content = edit_content(content, fedit.find_lines,
|
|
589
|
-
fedit.replace_with_lines)
|
|
627
|
+
content = edit_content(content, fedit.find_lines, fedit.replace_with_lines)
|
|
590
628
|
|
|
591
629
|
with open(path_, "w") as f:
|
|
592
630
|
f.write(content)
|
|
@@ -631,6 +669,7 @@ TOOLS = (
|
|
|
631
669
|
| DoneFlag
|
|
632
670
|
| ReadImage
|
|
633
671
|
| ReadFile
|
|
672
|
+
| Initialize
|
|
634
673
|
)
|
|
635
674
|
|
|
636
675
|
|
|
@@ -664,6 +703,8 @@ def which_tool_name(name: str) -> Type[TOOLS]:
|
|
|
664
703
|
return ReadImage
|
|
665
704
|
elif name == "ReadFile":
|
|
666
705
|
return ReadFile
|
|
706
|
+
elif name == "Initialize":
|
|
707
|
+
return Initialize
|
|
667
708
|
else:
|
|
668
709
|
raise ValueError(f"Unknown tool name: {name}")
|
|
669
710
|
|
|
@@ -681,6 +722,7 @@ def get_tool_output(
|
|
|
681
722
|
| AIAssistant
|
|
682
723
|
| DoneFlag
|
|
683
724
|
| ReadImage
|
|
725
|
+
| Initialize
|
|
684
726
|
| ReadFile,
|
|
685
727
|
enc: tiktoken.Encoding,
|
|
686
728
|
limit: float,
|
|
@@ -701,6 +743,7 @@ def get_tool_output(
|
|
|
701
743
|
| DoneFlag
|
|
702
744
|
| ReadImage
|
|
703
745
|
| ReadFile
|
|
746
|
+
| Initialize
|
|
704
747
|
](
|
|
705
748
|
Confirmation
|
|
706
749
|
| BashCommand
|
|
@@ -714,6 +757,7 @@ def get_tool_output(
|
|
|
714
757
|
| DoneFlag
|
|
715
758
|
| ReadImage
|
|
716
759
|
| ReadFile
|
|
760
|
+
| Initialize
|
|
717
761
|
)
|
|
718
762
|
arg = adapter.validate_python(args)
|
|
719
763
|
else:
|
|
@@ -748,10 +792,13 @@ def get_tool_output(
|
|
|
748
792
|
output = read_image_from_shell(arg.file_path), 0.0
|
|
749
793
|
elif isinstance(arg, ReadFile):
|
|
750
794
|
console.print("Calling read file tool")
|
|
751
|
-
output = read_file(arg), 0.0
|
|
795
|
+
output = read_file(arg, max_tokens), 0.0
|
|
752
796
|
elif isinstance(arg, ResetShell):
|
|
753
797
|
console.print("Calling reset shell tool")
|
|
754
798
|
output = reset_shell(), 0.0
|
|
799
|
+
elif isinstance(arg, Initialize):
|
|
800
|
+
console.print("Calling initial info tool")
|
|
801
|
+
output = initial_info(), 0.0
|
|
755
802
|
else:
|
|
756
803
|
raise ValueError(f"Unknown tool: {arg}")
|
|
757
804
|
|
|
@@ -763,8 +810,7 @@ History = list[ChatCompletionMessageParam]
|
|
|
763
810
|
|
|
764
811
|
default_enc = tiktoken.encoding_for_model("gpt-4o")
|
|
765
812
|
default_model: Models = "gpt-4o-2024-08-06"
|
|
766
|
-
default_cost = CostData(cost_per_1m_input_tokens=0.15,
|
|
767
|
-
cost_per_1m_output_tokens=0.6)
|
|
813
|
+
default_cost = CostData(cost_per_1m_input_tokens=0.15, cost_per_1m_output_tokens=0.6)
|
|
768
814
|
curr_cost = 0.0
|
|
769
815
|
|
|
770
816
|
|
|
@@ -779,6 +825,7 @@ class Mdata(BaseModel):
|
|
|
779
825
|
| FileEdit
|
|
780
826
|
| str
|
|
781
827
|
| ReadFile
|
|
828
|
+
| Initialize
|
|
782
829
|
)
|
|
783
830
|
|
|
784
831
|
|
|
@@ -807,8 +854,7 @@ def register_client(server_url: str, client_uuid: str = "") -> None:
|
|
|
807
854
|
raise Exception(mdata)
|
|
808
855
|
try:
|
|
809
856
|
output, cost = get_tool_output(
|
|
810
|
-
mdata.data, default_enc, 0.0, lambda x, y: (
|
|
811
|
-
"", 0), None
|
|
857
|
+
mdata.data, default_enc, 0.0, lambda x, y: ("", 0), 8000
|
|
812
858
|
)
|
|
813
859
|
curr_cost += cost
|
|
814
860
|
print(f"{curr_cost=}")
|
|
@@ -841,8 +887,7 @@ def app(
|
|
|
841
887
|
register_client(server_url, client_uuid or "")
|
|
842
888
|
|
|
843
889
|
|
|
844
|
-
def read_file(readfile: ReadFile) -> str:
|
|
845
|
-
|
|
890
|
+
def read_file(readfile: ReadFile, max_tokens: Optional[int]) -> str:
|
|
846
891
|
console.print(f"Reading file: {readfile.file_path}")
|
|
847
892
|
|
|
848
893
|
if not os.path.isabs(readfile.file_path):
|
|
@@ -854,4 +899,11 @@ def read_file(readfile: ReadFile) -> str:
|
|
|
854
899
|
|
|
855
900
|
with path.open("r") as f:
|
|
856
901
|
content = f.read()
|
|
902
|
+
|
|
903
|
+
if max_tokens is not None:
|
|
904
|
+
tokens = default_enc.encode(content)
|
|
905
|
+
if len(tokens) > max_tokens:
|
|
906
|
+
content = default_enc.decode(tokens[: max_tokens - 5])
|
|
907
|
+
content += "\n...(truncated)"
|
|
908
|
+
|
|
857
909
|
return content
|
|
@@ -20,6 +20,7 @@ from ..types_ import (
|
|
|
20
20
|
CreateFileNew,
|
|
21
21
|
FileEditFindReplace,
|
|
22
22
|
FileEdit,
|
|
23
|
+
Initialize,
|
|
23
24
|
ReadFile,
|
|
24
25
|
ResetShell,
|
|
25
26
|
Writefile,
|
|
@@ -37,6 +38,7 @@ class Mdata(BaseModel):
|
|
|
37
38
|
| FileEditFindReplace
|
|
38
39
|
| FileEdit
|
|
39
40
|
| ReadFile
|
|
41
|
+
| Initialize
|
|
40
42
|
| str
|
|
41
43
|
)
|
|
42
44
|
user_id: UUID
|
|
@@ -51,7 +53,7 @@ gpts: dict[UUID, Callable[[str], None]] = {}
|
|
|
51
53
|
images: DefaultDict[UUID, dict[str, dict[str, Any]]] = DefaultDict(dict)
|
|
52
54
|
|
|
53
55
|
|
|
54
|
-
CLIENT_VERSION_MINIMUM = "1.
|
|
56
|
+
CLIENT_VERSION_MINIMUM = "1.3.0"
|
|
55
57
|
|
|
56
58
|
|
|
57
59
|
@app.websocket("/v1/register/{uuid}")
|
|
@@ -71,8 +73,7 @@ async def register_websocket(websocket: WebSocket, uuid: UUID) -> None:
|
|
|
71
73
|
await websocket.send_text(
|
|
72
74
|
Mdata(
|
|
73
75
|
user_id=uuid,
|
|
74
|
-
data=f"Client version {client_version} is outdated. Please upgrade to {
|
|
75
|
-
CLIENT_VERSION_MINIMUM} or higher.",
|
|
76
|
+
data=f"Client version {client_version} is outdated. Please upgrade to {CLIENT_VERSION_MINIMUM} or higher.",
|
|
76
77
|
).model_dump_json()
|
|
77
78
|
)
|
|
78
79
|
await websocket.close(
|
|
@@ -281,9 +282,37 @@ async def read_file_endpoint(read_file_data: ReadFileWithUUID) -> str:
|
|
|
281
282
|
gpts[user_id] = put_results
|
|
282
283
|
|
|
283
284
|
await clients[user_id](
|
|
284
|
-
Mdata(data=
|
|
285
|
-
|
|
286
|
-
|
|
285
|
+
Mdata(data=read_file_data, user_id=user_id)
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
start_time = time.time()
|
|
289
|
+
while time.time() - start_time < 30:
|
|
290
|
+
if results is not None:
|
|
291
|
+
return results
|
|
292
|
+
await asyncio.sleep(0.1)
|
|
293
|
+
|
|
294
|
+
raise fastapi.HTTPException(status_code=500, detail="Timeout error")
|
|
295
|
+
|
|
296
|
+
class InitializeWithUUID(Initialize):
|
|
297
|
+
user_id: UUID
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
@app.post("/v1/initialize")
|
|
301
|
+
async def initialize(initialize_data: InitializeWithUUID) -> str:
|
|
302
|
+
user_id = initialize_data.user_id
|
|
303
|
+
if user_id not in clients:
|
|
304
|
+
return "Failure: id not found, ask the user to check it."
|
|
305
|
+
|
|
306
|
+
results: Optional[str] = None
|
|
307
|
+
|
|
308
|
+
def put_results(result: str) -> None:
|
|
309
|
+
nonlocal results
|
|
310
|
+
results = result
|
|
311
|
+
|
|
312
|
+
gpts[user_id] = put_results
|
|
313
|
+
|
|
314
|
+
await clients[user_id](
|
|
315
|
+
Mdata(data=initialize_data, user_id=user_id)
|
|
287
316
|
)
|
|
288
317
|
|
|
289
318
|
start_time = time.time()
|
|
@@ -13,26 +13,11 @@ Specials = Literal[
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class BashInteraction(BaseModel):
|
|
16
|
+
type: Literal["BashInteraction"]
|
|
16
17
|
send_text: Optional[str] = None
|
|
17
18
|
send_specials: Optional[Sequence[Specials]] = None
|
|
18
19
|
send_ascii: Optional[Sequence[int]] = None
|
|
19
20
|
|
|
20
|
-
def model_post_init(self, __context: object) -> None:
|
|
21
|
-
# Ensure only one of the fields is set
|
|
22
|
-
if (
|
|
23
|
-
sum(
|
|
24
|
-
[
|
|
25
|
-
int(bool(self.send_text)),
|
|
26
|
-
int(bool(self.send_specials)),
|
|
27
|
-
int(bool(self.send_ascii)),
|
|
28
|
-
]
|
|
29
|
-
)
|
|
30
|
-
!= 1
|
|
31
|
-
):
|
|
32
|
-
raise ValueError(
|
|
33
|
-
"Exactly one of 'send_text', 'send_specials', or 'send_ascii' must be set"
|
|
34
|
-
)
|
|
35
|
-
|
|
36
21
|
|
|
37
22
|
class ReadImage(BaseModel):
|
|
38
23
|
file_path: str
|
|
@@ -61,16 +46,13 @@ class FileEditFindReplace(BaseModel):
|
|
|
61
46
|
|
|
62
47
|
|
|
63
48
|
class ResetShell(BaseModel):
|
|
64
|
-
should_reset: Literal[True]
|
|
49
|
+
should_reset: Literal[True]
|
|
65
50
|
|
|
66
51
|
|
|
67
52
|
class FileEdit(BaseModel):
|
|
68
53
|
file_path: str
|
|
69
54
|
file_edit_using_search_replace_blocks: str
|
|
70
55
|
|
|
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
56
|
|
|
76
|
-
|
|
57
|
+
class Initialize(BaseModel):
|
|
58
|
+
type: Literal["Initialize"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|