auto-coder 0.1.352__py3-none-any.whl → 0.1.353__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 auto-coder might be problematic. Click here for more details.
- {auto_coder-0.1.352.dist-info → auto_coder-0.1.353.dist-info}/METADATA +1 -1
- {auto_coder-0.1.352.dist-info → auto_coder-0.1.353.dist-info}/RECORD +10 -9
- autocoder/common/git_utils.py +1 -1
- autocoder/common/v2/agent/agentic_edit.py +33 -0
- autocoder/common/v2/agent/agentic_edit_tools/test_search_files_tool_resolver.py +163 -0
- autocoder/version.py +1 -1
- {auto_coder-0.1.352.dist-info → auto_coder-0.1.353.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.352.dist-info → auto_coder-0.1.353.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.352.dist-info → auto_coder-0.1.353.dist-info}/entry_points.txt +0 -0
- {auto_coder-0.1.352.dist-info → auto_coder-0.1.353.dist-info}/top_level.txt +0 -0
|
@@ -14,7 +14,7 @@ autocoder/command_parser.py,sha256=fx1g9E6GaM273lGTcJqaFQ-hoksS_Ik2glBMnVltPCE,1
|
|
|
14
14
|
autocoder/lang.py,sha256=PFtATuOhHRnfpqHQkXr6p4C893JvpsgwTMif3l-GEi0,14321
|
|
15
15
|
autocoder/models.py,sha256=W-IboD3wv6-P4U4I2o2vXOasD3pUAL8DTGhwJqL2AvI,13428
|
|
16
16
|
autocoder/run_context.py,sha256=IUfSO6_gp2Wt1blFWAmOpN0b0nDrTTk4LmtCYUBIoro,1643
|
|
17
|
-
autocoder/version.py,sha256=
|
|
17
|
+
autocoder/version.py,sha256=URYMbZX7bnhE4C9LpFn-XkzqSqHAptfB4MYvmtImMcM,23
|
|
18
18
|
autocoder/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
19
|
autocoder/agent/agentic_edit.py,sha256=XsfePZ-t6M-uBSdG1VLZXk1goqXk2HPeJ_A8IYyBuWQ,58896
|
|
20
20
|
autocoder/agent/agentic_edit_types.py,sha256=oFcDd_cxJ2yH9Ed1uTpD3BipudgoIEWDMPb5pAkq4gI,3288
|
|
@@ -80,7 +80,7 @@ autocoder/common/const.py,sha256=eTjhjh4Aj4CUzviJ81jaf3Y5cwqsLATySn2wJxaS6RQ,291
|
|
|
80
80
|
autocoder/common/context_pruner.py,sha256=HlU5BmxpCX7uVTJUsTFLlXvkwcOQuidI9uCKZaFxh6s,19874
|
|
81
81
|
autocoder/common/conversation_pruner.py,sha256=pzmrQEa7pFzA66eYSS_h7VqP6ZwUABeooDQzm0PGu0A,5770
|
|
82
82
|
autocoder/common/files.py,sha256=nPiKcnUcYZbSUn3TskKeTVnAxCJRtuehPuB_5d2imX8,4618
|
|
83
|
-
autocoder/common/git_utils.py,sha256=
|
|
83
|
+
autocoder/common/git_utils.py,sha256=lGFLxxoyMKjIRgLeE_9176T3rdKI13DZMd28Lowk218,27093
|
|
84
84
|
autocoder/common/global_cancel.py,sha256=TyjYQPESwo04D1BOTmC9hH7IbkKDDM-b2zPacEHGIQ8,3264
|
|
85
85
|
autocoder/common/image_to_page.py,sha256=yWiTJQ49Lm3j0FngiJhQ9u7qayqE_bOGb8Rk0TmSWx0,14123
|
|
86
86
|
autocoder/common/index_import_export.py,sha256=h758AYY1df6JMTKUXYmMkSgxItfymDt82XT7O-ygEuw,4565
|
|
@@ -129,7 +129,7 @@ autocoder/common/v2/code_editblock_manager.py,sha256=G0CIuV9Ki0FqMLnpA8nBT4pnkCN
|
|
|
129
129
|
autocoder/common/v2/code_manager.py,sha256=C403bS-f6urixwitlKHcml-J03hci-UyNwHJOqBiY6Q,9182
|
|
130
130
|
autocoder/common/v2/code_strict_diff_manager.py,sha256=v-J1kDyLg7tLGg_6_lbO9S4fNkx7M_L8Xr2G7fPptiU,9347
|
|
131
131
|
autocoder/common/v2/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
132
|
-
autocoder/common/v2/agent/agentic_edit.py,sha256=
|
|
132
|
+
autocoder/common/v2/agent/agentic_edit.py,sha256=yGG8csvDppZ5VilV7FuF0nhBLzj1hDR3GMVONPD3vXA,99880
|
|
133
133
|
autocoder/common/v2/agent/agentic_edit_conversation.py,sha256=pFgWPWHKhZ4J9EcFmIdiGsrSolTZuYcH1qkgKdD8nwk,7726
|
|
134
134
|
autocoder/common/v2/agent/agentic_edit_types.py,sha256=VJMrictg6hJ3mC45VgQGRd43DyDUPDUvPV1Rf3z72NI,4776
|
|
135
135
|
autocoder/common/v2/agent/agentic_tool_display.py,sha256=WKirt-2V346KLnbHgH3NVJiK3xvriD9oaCWj2IdvzLU,7309
|
|
@@ -146,6 +146,7 @@ autocoder/common/v2/agent/agentic_edit_tools/plan_mode_respond_tool_resolver.py,
|
|
|
146
146
|
autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py,sha256=9Bh0KVbL0qiIqwChlb77biiBiETQ3zekxGe5Fj7hXAg,2800
|
|
147
147
|
autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py,sha256=lpD4fCbVR8GTrynqXON69IjM94nPy3nuUL62Ashm5O4,7988
|
|
148
148
|
autocoder/common/v2/agent/agentic_edit_tools/search_files_tool_resolver.py,sha256=mKztNjR5dmbyJ2aPcMkOuf5krhYPWbNRvIGY8Qp8dWU,5502
|
|
149
|
+
autocoder/common/v2/agent/agentic_edit_tools/test_search_files_tool_resolver.py,sha256=9eBo3WLkrr77iNotwIwVmH1ZL3UY0JQgLpdAIc9wTTM,6127
|
|
149
150
|
autocoder/common/v2/agent/agentic_edit_tools/use_mcp_tool_resolver.py,sha256=wM2Xy4bcnD0TSLEmcM8rvvyyWenN5_KQnJMO6hJ8lTE,1716
|
|
150
151
|
autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py,sha256=UO4SrkDek3WDlRdlHH022W1roSNMdMcipJqDxRBlheM,3044
|
|
151
152
|
autocoder/compilers/__init__.py,sha256=C0HOms70QA747XD0uZEMmGtRFcIPenohyqECNStv0Bw,1647
|
|
@@ -289,9 +290,9 @@ autocoder/utils/types.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
289
290
|
autocoder/utils/auto_coder_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
290
291
|
autocoder/utils/auto_coder_utils/chat_stream_out.py,sha256=KW0mlmcHlStXi8-_6fXZ2-ifeJ5mgP0OV7DQFzCtIsw,14008
|
|
291
292
|
autocoder/utils/chat_auto_coder_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
292
|
-
auto_coder-0.1.
|
|
293
|
-
auto_coder-0.1.
|
|
294
|
-
auto_coder-0.1.
|
|
295
|
-
auto_coder-0.1.
|
|
296
|
-
auto_coder-0.1.
|
|
297
|
-
auto_coder-0.1.
|
|
293
|
+
auto_coder-0.1.353.dist-info/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
|
|
294
|
+
auto_coder-0.1.353.dist-info/METADATA,sha256=NFUb4YANaxUxKYGsaxYoCntgWok0LCW5sSZv1WRpxLs,2751
|
|
295
|
+
auto_coder-0.1.353.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
296
|
+
auto_coder-0.1.353.dist-info/entry_points.txt,sha256=0nzHtHH4pNcM7xq4EBA2toS28Qelrvcbrr59GqD_0Ak,350
|
|
297
|
+
auto_coder-0.1.353.dist-info/top_level.txt,sha256=Jqc0_uJSw2GwoFQAa9iJxYns-2mWla-9ok_Y3Gcznjk,10
|
|
298
|
+
auto_coder-0.1.353.dist-info/RECORD,,
|
autocoder/common/git_utils.py
CHANGED
|
@@ -64,7 +64,7 @@ def commit_changes(repo_path: str, message: str) -> CommitResult:
|
|
|
64
64
|
try:
|
|
65
65
|
repo.git.add(all=True)
|
|
66
66
|
if repo.is_dirty():
|
|
67
|
-
commit = repo.index.commit(message)
|
|
67
|
+
commit = repo.index.commit(message,skip_hooks=True)
|
|
68
68
|
result = CommitResult(
|
|
69
69
|
success=True,
|
|
70
70
|
commit_message=message,
|
|
@@ -1177,6 +1177,8 @@ Below are some files the user is focused on, and the content is up to date. Thes
|
|
|
1177
1177
|
# If no event was processed in this iteration, break inner loop
|
|
1178
1178
|
if not found_event:
|
|
1179
1179
|
break
|
|
1180
|
+
|
|
1181
|
+
yield TokenUsageEvent(usage=meta_holder.meta)
|
|
1180
1182
|
|
|
1181
1183
|
# After generator exhausted, yield any remaining content
|
|
1182
1184
|
if in_thinking_block:
|
|
@@ -1440,6 +1442,37 @@ Below are some files the user is focused on, and the content is up to date. Thes
|
|
|
1440
1442
|
self.apply_pre_changes()
|
|
1441
1443
|
event_stream = self.analyze(request)
|
|
1442
1444
|
for event in event_stream:
|
|
1445
|
+
if isinstance(event, TokenUsageEvent):
|
|
1446
|
+
last_meta: SingleOutputMeta = event.usage
|
|
1447
|
+
# Get model info for pricing
|
|
1448
|
+
from autocoder.utils import llms as llm_utils
|
|
1449
|
+
model_name = ",".join(llm_utils.get_llm_names(self.llm))
|
|
1450
|
+
model_info = llm_utils.get_model_info(
|
|
1451
|
+
model_name, self.args.product_mode) or {}
|
|
1452
|
+
input_price = model_info.get(
|
|
1453
|
+
"input_price", 0.0) if model_info else 0.0
|
|
1454
|
+
output_price = model_info.get(
|
|
1455
|
+
"output_price", 0.0) if model_info else 0.0
|
|
1456
|
+
|
|
1457
|
+
# Calculate costs
|
|
1458
|
+
input_cost = (last_meta.input_tokens_count *
|
|
1459
|
+
input_price) / 1000000 # Convert to millions
|
|
1460
|
+
# Convert to millions
|
|
1461
|
+
output_cost = (
|
|
1462
|
+
last_meta.generated_tokens_count * output_price) / 1000000
|
|
1463
|
+
|
|
1464
|
+
self.printer.print_in_terminal(
|
|
1465
|
+
"code_generation_complete",
|
|
1466
|
+
duration=0.0,
|
|
1467
|
+
input_tokens=last_meta.input_tokens_count,
|
|
1468
|
+
output_tokens=last_meta.generated_tokens_count,
|
|
1469
|
+
input_cost=input_cost,
|
|
1470
|
+
output_cost=output_cost,
|
|
1471
|
+
speed=0.0,
|
|
1472
|
+
model_names=model_name,
|
|
1473
|
+
sampling_count=1
|
|
1474
|
+
)
|
|
1475
|
+
|
|
1443
1476
|
if isinstance(event, LLMThinkingEvent):
|
|
1444
1477
|
# Render thinking within a less prominent style, maybe grey?
|
|
1445
1478
|
console.print(f"[grey50]{event.text}[/grey50]", end="")
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import os
|
|
3
|
+
import tempfile
|
|
4
|
+
import shutil
|
|
5
|
+
from unittest.mock import patch, MagicMock
|
|
6
|
+
|
|
7
|
+
from autocoder.common.v2.agent.agentic_edit_tools.search_files_tool_resolver import SearchFilesToolResolver
|
|
8
|
+
from autocoder.common.v2.agent.agentic_edit_types import SearchFilesTool, ToolResult
|
|
9
|
+
from autocoder.common import AutoCoderArgs
|
|
10
|
+
|
|
11
|
+
# Helper function to create a directory structure with files for testing
|
|
12
|
+
def create_test_files(base_dir, structure):
|
|
13
|
+
"""
|
|
14
|
+
Creates a directory structure with files based on the provided dictionary.
|
|
15
|
+
Keys are filenames (relative to base_dir), values are file contents.
|
|
16
|
+
Directories are created automatically.
|
|
17
|
+
"""
|
|
18
|
+
for path, content in structure.items():
|
|
19
|
+
full_path = os.path.join(base_dir, path)
|
|
20
|
+
os.makedirs(os.path.dirname(full_path), exist_ok=True)
|
|
21
|
+
with open(full_path, 'w') as f:
|
|
22
|
+
f.write(content)
|
|
23
|
+
|
|
24
|
+
@pytest.fixture
|
|
25
|
+
def search_tool_resolver(temp_search_dir):
|
|
26
|
+
"""Fixture to provide an instance of SearchFilesToolResolver."""
|
|
27
|
+
# Create AutoCoderArgs with the temp directory as source_dir to allow the security check to pass
|
|
28
|
+
args = AutoCoderArgs()
|
|
29
|
+
args.source_dir = temp_search_dir # Set the source_dir to our temp directory
|
|
30
|
+
return SearchFilesToolResolver(None, SearchFilesTool(path="", regex=""), args)
|
|
31
|
+
|
|
32
|
+
@pytest.fixture(scope="function")
|
|
33
|
+
def temp_search_dir():
|
|
34
|
+
"""Fixture to create a temporary directory with test files for searching."""
|
|
35
|
+
temp_dir = tempfile.mkdtemp()
|
|
36
|
+
test_structure = {
|
|
37
|
+
"file1.txt": "Hello world\nThis is a test file.",
|
|
38
|
+
"subdir/file2.py": "import sys\n\ndef main():\n print('Python script')\n",
|
|
39
|
+
"subdir/another.txt": "Another text file with world.",
|
|
40
|
+
".hiddenfile": "This should be ignored by default",
|
|
41
|
+
"no_match.md": "Markdown file."
|
|
42
|
+
}
|
|
43
|
+
create_test_files(temp_dir, test_structure)
|
|
44
|
+
yield temp_dir # Provide the path to the test function
|
|
45
|
+
shutil.rmtree(temp_dir) # Cleanup after test
|
|
46
|
+
|
|
47
|
+
# --- Test Cases ---
|
|
48
|
+
|
|
49
|
+
def test_resolve_finds_matches(search_tool_resolver, temp_search_dir):
|
|
50
|
+
"""Test that resolve finds matches correctly."""
|
|
51
|
+
# Set up the tool with the pattern we want to search for
|
|
52
|
+
tool = SearchFilesTool(
|
|
53
|
+
path="", # Use empty path to search in the source_dir itself
|
|
54
|
+
regex="world",
|
|
55
|
+
file_pattern="*.txt"
|
|
56
|
+
)
|
|
57
|
+
search_tool_resolver.tool = tool
|
|
58
|
+
|
|
59
|
+
# Call the resolve method directly
|
|
60
|
+
response = search_tool_resolver.resolve()
|
|
61
|
+
|
|
62
|
+
# Check the response
|
|
63
|
+
assert isinstance(response, ToolResult)
|
|
64
|
+
assert response.success
|
|
65
|
+
assert "Search completed. Found 2 matches" in response.message
|
|
66
|
+
|
|
67
|
+
# Check that the correct files were found
|
|
68
|
+
assert len(response.content) == 2
|
|
69
|
+
paths = [result["path"] for result in response.content]
|
|
70
|
+
assert any("file1.txt" in path for path in paths)
|
|
71
|
+
assert any("another.txt" in path for path in paths)
|
|
72
|
+
|
|
73
|
+
# Check that the match lines contain our search pattern
|
|
74
|
+
for result in response.content:
|
|
75
|
+
assert "world" in result["match_line"]
|
|
76
|
+
|
|
77
|
+
def test_resolve_no_matches(search_tool_resolver, temp_search_dir):
|
|
78
|
+
"""Test that resolve handles no matches correctly."""
|
|
79
|
+
tool = SearchFilesTool(
|
|
80
|
+
path="", # Use empty path to search in the source_dir itself
|
|
81
|
+
regex="nonexistent_pattern",
|
|
82
|
+
file_pattern="*"
|
|
83
|
+
)
|
|
84
|
+
search_tool_resolver.tool = tool
|
|
85
|
+
|
|
86
|
+
response = search_tool_resolver.resolve()
|
|
87
|
+
|
|
88
|
+
assert isinstance(response, ToolResult)
|
|
89
|
+
assert response.success # Still success, just no results
|
|
90
|
+
assert "Search completed. Found 0 matches" in response.message
|
|
91
|
+
assert len(response.content) == 0
|
|
92
|
+
|
|
93
|
+
def test_resolve_file_pattern(search_tool_resolver, temp_search_dir):
|
|
94
|
+
"""Test that the file_pattern is correctly applied."""
|
|
95
|
+
# Test .txt pattern
|
|
96
|
+
tool_txt = SearchFilesTool(
|
|
97
|
+
path="", # Use empty path to search in the source_dir itself
|
|
98
|
+
regex="world",
|
|
99
|
+
file_pattern="*.txt" # Only search .txt files
|
|
100
|
+
)
|
|
101
|
+
search_tool_resolver.tool = tool_txt
|
|
102
|
+
|
|
103
|
+
response_txt = search_tool_resolver.resolve()
|
|
104
|
+
|
|
105
|
+
assert isinstance(response_txt, ToolResult)
|
|
106
|
+
assert response_txt.success
|
|
107
|
+
assert "Search completed. Found 2 matches" in response_txt.message
|
|
108
|
+
# Ensure only .txt files were matched
|
|
109
|
+
for result in response_txt.content:
|
|
110
|
+
assert result["path"].endswith(".txt")
|
|
111
|
+
|
|
112
|
+
# Test .py pattern
|
|
113
|
+
tool_py = SearchFilesTool(
|
|
114
|
+
path="", # Use empty path to search in the source_dir itself
|
|
115
|
+
regex="print",
|
|
116
|
+
file_pattern="*.py" # Only search .py files
|
|
117
|
+
)
|
|
118
|
+
search_tool_resolver.tool = tool_py
|
|
119
|
+
|
|
120
|
+
response_py = search_tool_resolver.resolve()
|
|
121
|
+
|
|
122
|
+
assert isinstance(response_py, ToolResult)
|
|
123
|
+
assert response_py.success
|
|
124
|
+
assert "Search completed. Found 1 matches" in response_py.message
|
|
125
|
+
# Ensure only .py files were matched
|
|
126
|
+
for result in response_py.content:
|
|
127
|
+
assert result["path"].endswith(".py")
|
|
128
|
+
|
|
129
|
+
def test_invalid_regex(search_tool_resolver, temp_search_dir):
|
|
130
|
+
"""Test that an invalid regex pattern is properly handled."""
|
|
131
|
+
tool = SearchFilesTool(
|
|
132
|
+
path="", # Use empty path to search in the source_dir itself
|
|
133
|
+
regex="[invalid regex", # Invalid regex pattern
|
|
134
|
+
file_pattern="*"
|
|
135
|
+
)
|
|
136
|
+
search_tool_resolver.tool = tool
|
|
137
|
+
|
|
138
|
+
response = search_tool_resolver.resolve()
|
|
139
|
+
|
|
140
|
+
assert isinstance(response, ToolResult)
|
|
141
|
+
assert not response.success
|
|
142
|
+
assert "Invalid regex pattern" in response.message
|
|
143
|
+
|
|
144
|
+
def test_nonexistent_path(search_tool_resolver, temp_search_dir):
|
|
145
|
+
"""Test that a nonexistent path is properly handled."""
|
|
146
|
+
# Create a path that we know doesn't exist under temp_search_dir
|
|
147
|
+
nonexistent_path = "nonexistent_subdirectory"
|
|
148
|
+
|
|
149
|
+
tool = SearchFilesTool(
|
|
150
|
+
path=nonexistent_path, # This path doesn't exist in our temp directory
|
|
151
|
+
regex="pattern",
|
|
152
|
+
file_pattern="*"
|
|
153
|
+
)
|
|
154
|
+
search_tool_resolver.tool = tool
|
|
155
|
+
|
|
156
|
+
response = search_tool_resolver.resolve()
|
|
157
|
+
|
|
158
|
+
assert isinstance(response, ToolResult)
|
|
159
|
+
assert not response.success
|
|
160
|
+
assert "Error: Search path not found" in response.message
|
|
161
|
+
|
|
162
|
+
# Add more tests as needed
|
|
163
|
+
|
autocoder/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.1.
|
|
1
|
+
__version__ = "0.1.353"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|