wcgw 2.8.7__py3-none-any.whl → 2.8.10__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.
- mcp_wcgw/types.py +1 -1
- wcgw/client/tools.py +43 -6
- {wcgw-2.8.7.dist-info → wcgw-2.8.10.dist-info}/METADATA +1 -1
- {wcgw-2.8.7.dist-info → wcgw-2.8.10.dist-info}/RECORD +8 -8
- wcgw_cli/anthropic_client.py +82 -10
- {wcgw-2.8.7.dist-info → wcgw-2.8.10.dist-info}/WHEEL +0 -0
- {wcgw-2.8.7.dist-info → wcgw-2.8.10.dist-info}/entry_points.txt +0 -0
- {wcgw-2.8.7.dist-info → wcgw-2.8.10.dist-info}/licenses/LICENSE +0 -0
mcp_wcgw/types.py
CHANGED
wcgw/client/tools.py
CHANGED
|
@@ -6,8 +6,10 @@ import importlib.metadata
|
|
|
6
6
|
import json
|
|
7
7
|
import mimetypes
|
|
8
8
|
import os
|
|
9
|
+
import platform
|
|
9
10
|
import re
|
|
10
11
|
import shlex
|
|
12
|
+
import subprocess
|
|
11
13
|
import time
|
|
12
14
|
import traceback
|
|
13
15
|
import uuid
|
|
@@ -133,19 +135,43 @@ def ask_confirmation(prompt: Confirmation) -> str:
|
|
|
133
135
|
PROMPT_CONST = "#" + "@wcgw@#"
|
|
134
136
|
|
|
135
137
|
|
|
136
|
-
def
|
|
138
|
+
def is_mac() -> bool:
|
|
139
|
+
return platform.system() == "Darwin"
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def get_tmpdir() -> str:
|
|
143
|
+
current_tmpdir = os.environ.get("TMPDIR", "")
|
|
144
|
+
if current_tmpdir or not is_mac():
|
|
145
|
+
return current_tmpdir
|
|
146
|
+
try:
|
|
147
|
+
# Fix issue while running ocrmypdf -> tesseract -> leptonica, set TMPDIR
|
|
148
|
+
# https://github.com/tesseract-ocr/tesseract/issues/4333
|
|
149
|
+
result = subprocess.check_output(
|
|
150
|
+
["getconf", "DARWIN_USER_TEMP_DIR"],
|
|
151
|
+
text=True,
|
|
152
|
+
).strip()
|
|
153
|
+
return result
|
|
154
|
+
except subprocess.CalledProcessError:
|
|
155
|
+
return "//tmp"
|
|
156
|
+
except Exception:
|
|
157
|
+
return ""
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def start_shell(is_restricted_mode: bool, initial_dir: str) -> pexpect.spawn: # type: ignore[type-arg]
|
|
137
161
|
cmd = "/bin/bash"
|
|
138
162
|
if is_restricted_mode:
|
|
139
163
|
cmd += " -r"
|
|
140
164
|
|
|
165
|
+
overrideenv = {**os.environ, "PS1": PROMPT_CONST, "TMPDIR": get_tmpdir()}
|
|
141
166
|
try:
|
|
142
167
|
shell = pexpect.spawn(
|
|
143
168
|
cmd,
|
|
144
|
-
env=
|
|
169
|
+
env=overrideenv, # type: ignore[arg-type]
|
|
145
170
|
echo=False,
|
|
146
171
|
encoding="utf-8",
|
|
147
172
|
timeout=TIMEOUT,
|
|
148
173
|
cwd=initial_dir,
|
|
174
|
+
codec_errors="backslashreplace",
|
|
149
175
|
)
|
|
150
176
|
shell.sendline(
|
|
151
177
|
f"export PROMPT_COMMAND= PS1={PROMPT_CONST}"
|
|
@@ -157,10 +183,11 @@ def start_shell(is_restricted_mode: bool, initial_dir: str) -> pexpect.spawn: #
|
|
|
157
183
|
|
|
158
184
|
shell = pexpect.spawn(
|
|
159
185
|
"/bin/bash --noprofile --norc",
|
|
160
|
-
env=
|
|
186
|
+
env=overrideenv, # type: ignore[arg-type]
|
|
161
187
|
echo=False,
|
|
162
188
|
encoding="utf-8",
|
|
163
189
|
timeout=TIMEOUT,
|
|
190
|
+
codec_errors="backslashreplace",
|
|
164
191
|
)
|
|
165
192
|
shell.sendline(f"export PS1={PROMPT_CONST}")
|
|
166
193
|
shell.expect(PROMPT_CONST, timeout=TIMEOUT)
|
|
@@ -256,7 +283,9 @@ class BashState:
|
|
|
256
283
|
before = "\n".join(before_lines).strip()
|
|
257
284
|
counts += 1
|
|
258
285
|
if counts > 100:
|
|
259
|
-
raise ValueError(
|
|
286
|
+
raise ValueError(
|
|
287
|
+
"Error in understanding shell output. This shouldn't happen, likely shell is in a bad state, please reset it"
|
|
288
|
+
)
|
|
260
289
|
|
|
261
290
|
try:
|
|
262
291
|
return int(before)
|
|
@@ -273,7 +302,7 @@ class BashState:
|
|
|
273
302
|
self._bash_command_mode.bash_mode == "restricted_mode",
|
|
274
303
|
self._cwd,
|
|
275
304
|
)
|
|
276
|
-
|
|
305
|
+
|
|
277
306
|
self._pending_output = ""
|
|
278
307
|
|
|
279
308
|
# Get exit info to ensure shell is ready
|
|
@@ -414,7 +443,9 @@ class BashState:
|
|
|
414
443
|
index = self.shell.expect([self._prompt, pexpect.TIMEOUT], timeout=0.2)
|
|
415
444
|
counts += 1
|
|
416
445
|
if counts > 100:
|
|
417
|
-
raise ValueError(
|
|
446
|
+
raise ValueError(
|
|
447
|
+
"Error in understanding shell output. This shouldn't happen, likely shell is in a bad state, please reset it"
|
|
448
|
+
)
|
|
418
449
|
console.print(f"Prompt updated to: {self._prompt}")
|
|
419
450
|
return True
|
|
420
451
|
return False
|
|
@@ -457,6 +488,12 @@ def initialize(
|
|
|
457
488
|
folder_to_start = None
|
|
458
489
|
if any_workspace_path:
|
|
459
490
|
if os.path.exists(any_workspace_path):
|
|
491
|
+
if os.path.isfile(any_workspace_path):
|
|
492
|
+
# Set any_workspace_path to the directory containing the file
|
|
493
|
+
# Add the file to read_files_ only if empty to avoid duplicates
|
|
494
|
+
if not read_files_:
|
|
495
|
+
read_files_ = [any_workspace_path]
|
|
496
|
+
any_workspace_path = os.path.dirname(any_workspace_path)
|
|
460
497
|
repo_context, folder_to_start = get_repo_context(any_workspace_path, 200)
|
|
461
498
|
|
|
462
499
|
repo_context = f"---\n# Workspace structure\n{repo_context}\n---\n"
|
|
@@ -7,7 +7,7 @@ wcgw/client/diff-instructions.txt,sha256=tmJ9Fu9XdO_72lYXQQNY9RZyx91bjxrXJf9d_KB
|
|
|
7
7
|
wcgw/client/memory.py,sha256=8LdYsOhvCOoC1kfvDr85kNy07WnhPMvE6B2FRM2w85Y,2902
|
|
8
8
|
wcgw/client/modes.py,sha256=FkDJIgjKrlJEufLq3abWfqV25BdF2pH-HnoHafy9LrA,10484
|
|
9
9
|
wcgw/client/sys_utils.py,sha256=GajPntKhaTUMn6EOmopENWZNR2G_BJyuVbuot0x6veI,1376
|
|
10
|
-
wcgw/client/tools.py,sha256=
|
|
10
|
+
wcgw/client/tools.py,sha256=c-LipooLb6XBF9l-qwJlzBLzznlqQQuCXRB9HSZJFT0,53450
|
|
11
11
|
wcgw/client/file_ops/diff_edit.py,sha256=OlJCpPSE_3T41q9H0yDORm6trjm3w6zh1EkuPTxik2A,16832
|
|
12
12
|
wcgw/client/file_ops/search_replace.py,sha256=Napa7IWaYPGMNdttunKyRDkb90elZE7r23B_o_htRxo,5585
|
|
13
13
|
wcgw/client/mcp_server/Readme.md,sha256=I8N4dHkTUVGNQ63BQkBMBhCCBTgqGOSF_pUR6iOEiUk,2495
|
|
@@ -22,13 +22,13 @@ wcgw/relay/serve.py,sha256=Z5EwtaCAtKFBSnUw4mPYw0sze3Coc4Fa8gObRRG_bT0,9525
|
|
|
22
22
|
wcgw/relay/static/privacy.txt,sha256=s9qBdbx2SexCpC_z33sg16TptmAwDEehMCLz4L50JLc,529
|
|
23
23
|
wcgw_cli/__init__.py,sha256=TNxXsTPgb52OhakIda9wTRh91cqoBqgQRx5TxjzQQFU,21
|
|
24
24
|
wcgw_cli/__main__.py,sha256=wcCrL4PjG51r5wVKqJhcoJPTLfHW0wNbD31DrUN0MWI,28
|
|
25
|
-
wcgw_cli/anthropic_client.py,sha256=
|
|
25
|
+
wcgw_cli/anthropic_client.py,sha256=ZMFHD6g6h_PAReL8tubI8LnxVeRA3hcFLGNitjt9XhQ,24047
|
|
26
26
|
wcgw_cli/cli.py,sha256=GEje9ZBIaD5_-HK3zxZCGYaeDF8bfFxImloOR3O66Fw,1019
|
|
27
27
|
wcgw_cli/openai_client.py,sha256=wp4XDf3t3W6XG5LHgr6bFckePyty24BGtsOEjOrIrk0,17955
|
|
28
28
|
wcgw_cli/openai_utils.py,sha256=xGOb3W5ALrIozV7oszfGYztpj0FnXdD7jAxm5lEIVKY,2439
|
|
29
29
|
mcp_wcgw/__init__.py,sha256=fKCgOdN7cn7gR3YGFaGyV5Goe8A2sEyllLcsRkN0i-g,2601
|
|
30
30
|
mcp_wcgw/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
31
|
-
mcp_wcgw/types.py,sha256=
|
|
31
|
+
mcp_wcgw/types.py,sha256=f4nENSEc2Zu9YJkrXcSPgwUbc9pPhqhzy-_CBGGttoQ,30570
|
|
32
32
|
mcp_wcgw/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
33
33
|
mcp_wcgw/client/__main__.py,sha256=9ne6Gk1udW_GSwq7O7aZSYwwqSBTgyMVjNVc6s6GJoQ,2280
|
|
34
34
|
mcp_wcgw/client/session.py,sha256=RqdIN6ov9vklKY2B0-hvjIM5JtUetbyhhrF7a0HKAJ8,8044
|
|
@@ -48,8 +48,8 @@ mcp_wcgw/shared/memory.py,sha256=dBsOghxHz8-tycdSVo9kSujbsC8xb_tYsGmuJobuZnw,281
|
|
|
48
48
|
mcp_wcgw/shared/progress.py,sha256=ymxOsb8XO5Mhlop7fRfdbmvPodANj7oq6O4dD0iUcnw,1048
|
|
49
49
|
mcp_wcgw/shared/session.py,sha256=e44a0LQOW8gwdLs9_DE9oDsxqW2U8mXG3d5KT95bn5o,10393
|
|
50
50
|
mcp_wcgw/shared/version.py,sha256=d2LZii-mgsPIxpshjkXnOTUmk98i0DT4ff8VpA_kAvE,111
|
|
51
|
-
wcgw-2.8.
|
|
52
|
-
wcgw-2.8.
|
|
53
|
-
wcgw-2.8.
|
|
54
|
-
wcgw-2.8.
|
|
55
|
-
wcgw-2.8.
|
|
51
|
+
wcgw-2.8.10.dist-info/METADATA,sha256=-bjtkmro5UQVMBYYzxbakEIADO-VOpONetLtwxb1vX4,13054
|
|
52
|
+
wcgw-2.8.10.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
53
|
+
wcgw-2.8.10.dist-info/entry_points.txt,sha256=vd3tj1_Kzfp55LscJ8-6WFMM5hm9cWTfNGFCrWBnH3Q,124
|
|
54
|
+
wcgw-2.8.10.dist-info/licenses/LICENSE,sha256=BvY8xqjOfc3X2qZpGpX3MZEmF-4Dp0LqgKBbT6L_8oI,11142
|
|
55
|
+
wcgw-2.8.10.dist-info/RECORD,,
|
wcgw_cli/anthropic_client.py
CHANGED
|
@@ -14,15 +14,17 @@ from anthropic import Anthropic
|
|
|
14
14
|
from anthropic.types import (
|
|
15
15
|
ImageBlockParam,
|
|
16
16
|
MessageParam,
|
|
17
|
+
ModelParam,
|
|
17
18
|
TextBlockParam,
|
|
18
19
|
ToolParam,
|
|
19
20
|
ToolResultBlockParam,
|
|
20
21
|
ToolUseBlockParam,
|
|
21
22
|
)
|
|
22
23
|
from dotenv import load_dotenv
|
|
24
|
+
from pydantic import BaseModel
|
|
23
25
|
from typer import Typer
|
|
24
26
|
|
|
25
|
-
from wcgw.client.common import discard_input
|
|
27
|
+
from wcgw.client.common import CostData, discard_input
|
|
26
28
|
from wcgw.client.memory import load_memory
|
|
27
29
|
from wcgw.client.tools import (
|
|
28
30
|
DoneFlag,
|
|
@@ -47,6 +49,14 @@ from wcgw.types_ import (
|
|
|
47
49
|
WriteIfEmpty,
|
|
48
50
|
)
|
|
49
51
|
|
|
52
|
+
|
|
53
|
+
class Config(BaseModel):
|
|
54
|
+
model: ModelParam
|
|
55
|
+
cost_limit: float
|
|
56
|
+
cost_file: dict[ModelParam, CostData]
|
|
57
|
+
cost_unit: str = "$"
|
|
58
|
+
|
|
59
|
+
|
|
50
60
|
History = list[MessageParam]
|
|
51
61
|
|
|
52
62
|
|
|
@@ -150,7 +160,51 @@ def loop(
|
|
|
150
160
|
first_message = ""
|
|
151
161
|
waiting_for_assistant = history[-1]["role"] != "assistant"
|
|
152
162
|
|
|
153
|
-
|
|
163
|
+
config = Config(
|
|
164
|
+
model="claude-3-5-sonnet-20241022",
|
|
165
|
+
cost_limit=0.1,
|
|
166
|
+
cost_unit="$",
|
|
167
|
+
cost_file={
|
|
168
|
+
# Claude 3.5 Haiku
|
|
169
|
+
"claude-3-5-haiku-latest": CostData(
|
|
170
|
+
cost_per_1m_input_tokens=0.80, cost_per_1m_output_tokens=4
|
|
171
|
+
),
|
|
172
|
+
"claude-3-5-haiku-20241022": CostData(
|
|
173
|
+
cost_per_1m_input_tokens=0.80, cost_per_1m_output_tokens=4
|
|
174
|
+
),
|
|
175
|
+
# Claude 3.5 Sonnet
|
|
176
|
+
"claude-3-5-sonnet-latest": CostData(
|
|
177
|
+
cost_per_1m_input_tokens=3.0, cost_per_1m_output_tokens=15.0
|
|
178
|
+
),
|
|
179
|
+
"claude-3-5-sonnet-20241022": CostData(
|
|
180
|
+
cost_per_1m_input_tokens=3.0, cost_per_1m_output_tokens=15.0
|
|
181
|
+
),
|
|
182
|
+
"claude-3-5-sonnet-20240620": CostData(
|
|
183
|
+
cost_per_1m_input_tokens=3.0, cost_per_1m_output_tokens=15.0
|
|
184
|
+
),
|
|
185
|
+
# Claude 3 Opus
|
|
186
|
+
"claude-3-opus-latest": CostData(
|
|
187
|
+
cost_per_1m_input_tokens=15.0, cost_per_1m_output_tokens=75.0
|
|
188
|
+
),
|
|
189
|
+
"claude-3-opus-20240229": CostData(
|
|
190
|
+
cost_per_1m_input_tokens=15.0, cost_per_1m_output_tokens=75.0
|
|
191
|
+
),
|
|
192
|
+
# Legacy Models
|
|
193
|
+
"claude-3-haiku-20240307": CostData(
|
|
194
|
+
cost_per_1m_input_tokens=0.25, cost_per_1m_output_tokens=1.25
|
|
195
|
+
),
|
|
196
|
+
"claude-2.1": CostData(
|
|
197
|
+
cost_per_1m_input_tokens=8.0, cost_per_1m_output_tokens=24.0
|
|
198
|
+
),
|
|
199
|
+
"claude-2.0": CostData(
|
|
200
|
+
cost_per_1m_input_tokens=8.0, cost_per_1m_output_tokens=24.0
|
|
201
|
+
),
|
|
202
|
+
},
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
if limit is not None:
|
|
206
|
+
config.cost_limit = limit
|
|
207
|
+
limit = config.cost_limit
|
|
154
208
|
|
|
155
209
|
tools = [
|
|
156
210
|
ToolParam(
|
|
@@ -321,9 +375,15 @@ Saves provided description and file contents of all the relevant file paths or g
|
|
|
321
375
|
while True:
|
|
322
376
|
if cost > limit:
|
|
323
377
|
system_console.print(
|
|
324
|
-
f"\nCost limit exceeded. Current cost: {
|
|
378
|
+
f"\nCost limit exceeded. Current cost: {config.cost_unit}{cost:.4f}, "
|
|
379
|
+
f"input tokens: {input_toks}"
|
|
380
|
+
f"output tokens: {output_toks}"
|
|
325
381
|
)
|
|
326
382
|
break
|
|
383
|
+
else:
|
|
384
|
+
system_console.print(
|
|
385
|
+
f"\nTotal cost: {config.cost_unit}{cost:.4f}, input tokens: {input_toks}, output tokens: {output_toks}"
|
|
386
|
+
)
|
|
327
387
|
|
|
328
388
|
if not waiting_for_assistant:
|
|
329
389
|
if first_message:
|
|
@@ -335,13 +395,8 @@ Saves provided description and file contents of all the relevant file paths or g
|
|
|
335
395
|
history.append(parse_user_message_special(msg))
|
|
336
396
|
else:
|
|
337
397
|
waiting_for_assistant = False
|
|
338
|
-
|
|
339
|
-
cost_, input_toks_ = 0, 0
|
|
340
|
-
cost += cost_
|
|
341
|
-
input_toks += input_toks_
|
|
342
|
-
|
|
343
398
|
stream = client.messages.stream(
|
|
344
|
-
model=
|
|
399
|
+
model=config.model,
|
|
345
400
|
messages=history,
|
|
346
401
|
tools=tools,
|
|
347
402
|
max_tokens=8096,
|
|
@@ -361,7 +416,24 @@ Saves provided description and file contents of all the relevant file paths or g
|
|
|
361
416
|
with stream as stream_:
|
|
362
417
|
for chunk in stream_:
|
|
363
418
|
type_ = chunk.type
|
|
364
|
-
if type_
|
|
419
|
+
if type_ == "message_start":
|
|
420
|
+
message_start = chunk.message
|
|
421
|
+
# Update cost based on token usage from the API response
|
|
422
|
+
input_tokens = message_start.usage.input_tokens
|
|
423
|
+
input_toks += input_tokens
|
|
424
|
+
cost += (
|
|
425
|
+
input_tokens
|
|
426
|
+
* config.cost_file[config.model].cost_per_1m_input_tokens
|
|
427
|
+
) / 1_000_000
|
|
428
|
+
elif type_ == "message_stop":
|
|
429
|
+
message_stop = chunk.message
|
|
430
|
+
# Update cost based on output tokens
|
|
431
|
+
output_tokens = message_stop.usage.output_tokens
|
|
432
|
+
output_toks += output_tokens
|
|
433
|
+
cost += (
|
|
434
|
+
output_tokens
|
|
435
|
+
* config.cost_file[config.model].cost_per_1m_output_tokens
|
|
436
|
+
) / 1_000_000
|
|
365
437
|
continue
|
|
366
438
|
elif type_ == "content_block_start" and hasattr(
|
|
367
439
|
chunk, "content_block"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|