weco 0.2.23__tar.gz → 0.2.24__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.
- {weco-0.2.23 → weco-0.2.24}/.gitignore +2 -0
- {weco-0.2.23 → weco-0.2.24}/PKG-INFO +31 -18
- {weco-0.2.23 → weco-0.2.24}/README.md +28 -17
- weco-0.2.24/contributing.md +23 -0
- {weco-0.2.23 → weco-0.2.24}/pyproject.toml +3 -1
- {weco-0.2.23 → weco-0.2.24}/weco/api.py +62 -25
- {weco-0.2.23 → weco-0.2.24}/weco/auth.py +7 -5
- {weco-0.2.23 → weco-0.2.24}/weco/chatbot.py +34 -23
- {weco-0.2.23 → weco-0.2.24}/weco/cli.py +10 -1
- weco-0.2.24/weco/constants.py +7 -0
- {weco-0.2.23 → weco-0.2.24}/weco/optimizer.py +26 -21
- {weco-0.2.23 → weco-0.2.24}/weco/panels.py +105 -59
- {weco-0.2.23 → weco-0.2.24}/weco/utils.py +47 -12
- {weco-0.2.23 → weco-0.2.24}/weco.egg-info/PKG-INFO +31 -18
- {weco-0.2.23 → weco-0.2.24}/weco.egg-info/SOURCES.txt +2 -1
- {weco-0.2.23 → weco-0.2.24}/weco.egg-info/requires.txt +2 -0
- weco-0.2.23/.repomixignore +0 -4
- {weco-0.2.23 → weco-0.2.24}/.github/workflows/lint.yml +0 -0
- {weco-0.2.23 → weco-0.2.24}/.github/workflows/release.yml +0 -0
- {weco-0.2.23 → weco-0.2.24}/LICENSE +0 -0
- {weco-0.2.23 → weco-0.2.24}/assets/example-optimization.gif +0 -0
- {weco-0.2.23 → weco-0.2.24}/assets/weco.svg +0 -0
- {weco-0.2.23 → weco-0.2.24}/examples/cuda/README.md +0 -0
- {weco-0.2.23 → weco-0.2.24}/examples/cuda/evaluate.py +0 -0
- {weco-0.2.23 → weco-0.2.24}/examples/cuda/guide.md +0 -0
- {weco-0.2.23 → weco-0.2.24}/examples/cuda/optimize.py +0 -0
- {weco-0.2.23 → weco-0.2.24}/examples/hello-kernel-world/colab_notebook_walkthrough.ipynb +0 -0
- {weco-0.2.23 → weco-0.2.24}/examples/hello-kernel-world/evaluate.py +0 -0
- {weco-0.2.23 → weco-0.2.24}/examples/hello-kernel-world/optimize.py +0 -0
- {weco-0.2.23 → weco-0.2.24}/examples/prompt/README.md +0 -0
- {weco-0.2.23 → weco-0.2.24}/examples/prompt/eval.py +0 -0
- {weco-0.2.23 → weco-0.2.24}/examples/prompt/optimize.py +0 -0
- {weco-0.2.23 → weco-0.2.24}/examples/prompt/prompt_guide.md +0 -0
- {weco-0.2.23 → weco-0.2.24}/examples/spaceship-titanic/README.md +0 -0
- {weco-0.2.23 → weco-0.2.24}/examples/spaceship-titanic/competition_description.md +0 -0
- {weco-0.2.23 → weco-0.2.24}/examples/spaceship-titanic/data/sample_submission.csv +0 -0
- {weco-0.2.23 → weco-0.2.24}/examples/spaceship-titanic/data/test.csv +0 -0
- {weco-0.2.23 → weco-0.2.24}/examples/spaceship-titanic/data/train.csv +0 -0
- {weco-0.2.23 → weco-0.2.24}/examples/spaceship-titanic/evaluate.py +0 -0
- {weco-0.2.23 → weco-0.2.24}/examples/spaceship-titanic/train.py +0 -0
- {weco-0.2.23 → weco-0.2.24}/examples/triton/README.md +0 -0
- {weco-0.2.23 → weco-0.2.24}/examples/triton/evaluate.py +0 -0
- {weco-0.2.23 → weco-0.2.24}/examples/triton/optimize.py +0 -0
- {weco-0.2.23 → weco-0.2.24}/setup.cfg +0 -0
- {weco-0.2.23 → weco-0.2.24}/weco/__init__.py +0 -0
- {weco-0.2.23 → weco-0.2.24}/weco.egg-info/dependency_links.txt +0 -0
- {weco-0.2.23 → weco-0.2.24}/weco.egg-info/entry_points.txt +0 -0
- {weco-0.2.23 → weco-0.2.24}/weco.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: weco
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.24
|
|
4
4
|
Summary: Documentation for `weco`, a CLI for using Weco AI's code optimizer.
|
|
5
5
|
Author-email: Weco AI Team <contact@weco.ai>
|
|
6
6
|
License: MIT
|
|
@@ -16,6 +16,8 @@ Requires-Dist: requests
|
|
|
16
16
|
Requires-Dist: rich
|
|
17
17
|
Requires-Dist: packaging
|
|
18
18
|
Requires-Dist: gitingest
|
|
19
|
+
Requires-Dist: fastapi
|
|
20
|
+
Requires-Dist: slowapi
|
|
19
21
|
Provides-Extra: dev
|
|
20
22
|
Requires-Dist: ruff; extra == "dev"
|
|
21
23
|
Requires-Dist: build; extra == "dev"
|
|
@@ -158,6 +160,7 @@ For more advanced examples, including [Triton](/examples/triton/README.md), [CUD
|
|
|
158
160
|
| `-M, --model` | Model identifier for the LLM to use (e.g., `o4-mini`, `claude-sonnet-4-0`). | `o4-mini` when `OPENAI_API_KEY` is set; `claude-sonnet-4-0` when `ANTHROPIC_API_KEY` is set; `gemini-2.5-pro` when `GEMINI_API_KEY` is set. | `-M o4-mini` |
|
|
159
161
|
| `-i, --additional-instructions`| Natural language description of specific instructions **or** path to a file containing detailed instructions to guide the LLM. | `None` | `-i instructions.md` or `-i "Optimize the model for faster inference"`|
|
|
160
162
|
| `-l, --log-dir` | Path to the directory to log intermediate steps and final optimization result. | `.runs/` | `-l ./logs/` |
|
|
163
|
+
| `--eval-timeout` | Timeout in seconds for each step in evaluation. | No timeout (unlimited) | `--eval-timeout 3600` |
|
|
161
164
|
|
|
162
165
|
---
|
|
163
166
|
|
|
@@ -248,28 +251,38 @@ Final speedup value = 1.5
|
|
|
248
251
|
|
|
249
252
|
Weco will parse this output to extract the numerical value (1.5 in this case) associated with the metric name ('speedup').
|
|
250
253
|
|
|
251
|
-
##
|
|
254
|
+
## Supported Models
|
|
252
255
|
|
|
253
|
-
|
|
256
|
+
Weco supports the following LLM models:
|
|
254
257
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
258
|
+
### OpenAI Models
|
|
259
|
+
- `o3`
|
|
260
|
+
- `o3-mini`
|
|
261
|
+
- `o4-mini`
|
|
262
|
+
- `o1-pro`
|
|
263
|
+
- `o1`
|
|
264
|
+
- `gpt-4.1`
|
|
265
|
+
- `gpt-4.1-mini`
|
|
266
|
+
- `gpt-4.1-nano`
|
|
267
|
+
- `gpt-4o`
|
|
268
|
+
- `gpt-4o-mini`
|
|
260
269
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
270
|
+
### Anthropic Models
|
|
271
|
+
- `claude-opus-4-0`
|
|
272
|
+
- `claude-sonnet-4-0`
|
|
273
|
+
- `claude-3-7-sonnet-latest`
|
|
265
274
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
275
|
+
### Gemini Models
|
|
276
|
+
- `gemini-2.5-pro`
|
|
277
|
+
- `gemini-2.5-flash`
|
|
278
|
+
- `gemini-2.5-flash-lite`
|
|
279
|
+
|
|
280
|
+
You can specify any of these models using the `-M` or `--model` flag. Ensure you have the corresponding API key set as an environment variable for the model provider you wish to use.
|
|
270
281
|
|
|
271
|
-
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## Contributing
|
|
272
285
|
|
|
273
|
-
|
|
286
|
+
We welcome contributions! Please see [contributing.md](contributing.md) for detailed guidelines on how to contribute to this project.
|
|
274
287
|
|
|
275
288
|
---
|
|
@@ -134,6 +134,7 @@ For more advanced examples, including [Triton](/examples/triton/README.md), [CUD
|
|
|
134
134
|
| `-M, --model` | Model identifier for the LLM to use (e.g., `o4-mini`, `claude-sonnet-4-0`). | `o4-mini` when `OPENAI_API_KEY` is set; `claude-sonnet-4-0` when `ANTHROPIC_API_KEY` is set; `gemini-2.5-pro` when `GEMINI_API_KEY` is set. | `-M o4-mini` |
|
|
135
135
|
| `-i, --additional-instructions`| Natural language description of specific instructions **or** path to a file containing detailed instructions to guide the LLM. | `None` | `-i instructions.md` or `-i "Optimize the model for faster inference"`|
|
|
136
136
|
| `-l, --log-dir` | Path to the directory to log intermediate steps and final optimization result. | `.runs/` | `-l ./logs/` |
|
|
137
|
+
| `--eval-timeout` | Timeout in seconds for each step in evaluation. | No timeout (unlimited) | `--eval-timeout 3600` |
|
|
137
138
|
|
|
138
139
|
---
|
|
139
140
|
|
|
@@ -224,28 +225,38 @@ Final speedup value = 1.5
|
|
|
224
225
|
|
|
225
226
|
Weco will parse this output to extract the numerical value (1.5 in this case) associated with the metric name ('speedup').
|
|
226
227
|
|
|
227
|
-
##
|
|
228
|
+
## Supported Models
|
|
228
229
|
|
|
229
|
-
|
|
230
|
+
Weco supports the following LLM models:
|
|
230
231
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
232
|
+
### OpenAI Models
|
|
233
|
+
- `o3`
|
|
234
|
+
- `o3-mini`
|
|
235
|
+
- `o4-mini`
|
|
236
|
+
- `o1-pro`
|
|
237
|
+
- `o1`
|
|
238
|
+
- `gpt-4.1`
|
|
239
|
+
- `gpt-4.1-mini`
|
|
240
|
+
- `gpt-4.1-nano`
|
|
241
|
+
- `gpt-4o`
|
|
242
|
+
- `gpt-4o-mini`
|
|
236
243
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
244
|
+
### Anthropic Models
|
|
245
|
+
- `claude-opus-4-0`
|
|
246
|
+
- `claude-sonnet-4-0`
|
|
247
|
+
- `claude-3-7-sonnet-latest`
|
|
241
248
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
249
|
+
### Gemini Models
|
|
250
|
+
- `gemini-2.5-pro`
|
|
251
|
+
- `gemini-2.5-flash`
|
|
252
|
+
- `gemini-2.5-flash-lite`
|
|
253
|
+
|
|
254
|
+
You can specify any of these models using the `-M` or `--model` flag. Ensure you have the corresponding API key set as an environment variable for the model provider you wish to use.
|
|
246
255
|
|
|
247
|
-
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Contributing
|
|
248
259
|
|
|
249
|
-
|
|
260
|
+
We welcome contributions! Please see [contributing.md](contributing.md) for detailed guidelines on how to contribute to this project.
|
|
250
261
|
|
|
251
262
|
---
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
We welcome your contributions! To get started:
|
|
4
|
+
|
|
5
|
+
1. **Fork & Clone the Repository:**
|
|
6
|
+
```bash
|
|
7
|
+
git clone https://github.com/WecoAI/weco-cli.git
|
|
8
|
+
cd weco-cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
2. **Install Dependencies:**
|
|
12
|
+
```bash
|
|
13
|
+
pip install -e ".[dev]"
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
3. **Create a Feature Branch:**
|
|
17
|
+
```bash
|
|
18
|
+
git checkout -b feature/your-feature-name
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
4. **Make Changes:** Ensure your code adheres to our style guidelines and includes relevant tests.
|
|
22
|
+
|
|
23
|
+
5. **Commit, Push & Open a PR**: Commit your changes, and open a pull request with a clear description of your enhancements.
|
|
@@ -8,7 +8,7 @@ name = "weco"
|
|
|
8
8
|
authors = [{ name = "Weco AI Team", email = "contact@weco.ai" }]
|
|
9
9
|
description = "Documentation for `weco`, a CLI for using Weco AI's code optimizer."
|
|
10
10
|
readme = "README.md"
|
|
11
|
-
version = "0.2.
|
|
11
|
+
version = "0.2.24"
|
|
12
12
|
license = { text = "MIT" }
|
|
13
13
|
requires-python = ">=3.8"
|
|
14
14
|
dependencies = [
|
|
@@ -16,6 +16,8 @@ dependencies = [
|
|
|
16
16
|
"rich",
|
|
17
17
|
"packaging",
|
|
18
18
|
"gitingest",
|
|
19
|
+
"fastapi",
|
|
20
|
+
"slowapi",
|
|
19
21
|
]
|
|
20
22
|
keywords = ["AI", "Code Optimization", "Code Generation"]
|
|
21
23
|
classifiers = [
|
|
@@ -4,6 +4,7 @@ import requests
|
|
|
4
4
|
from rich.console import Console
|
|
5
5
|
|
|
6
6
|
from weco import __pkg_version__, __base_url__
|
|
7
|
+
from .constants import DEFAULT_API_TIMEOUT
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
def handle_api_error(e: requests.exceptions.HTTPError, console: Console) -> None:
|
|
@@ -30,7 +31,7 @@ def start_optimization_run(
|
|
|
30
31
|
additional_instructions: str = None,
|
|
31
32
|
api_keys: Dict[str, Any] = {},
|
|
32
33
|
auth_headers: dict = {},
|
|
33
|
-
timeout: Union[int, Tuple[int, int]] =
|
|
34
|
+
timeout: Union[int, Tuple[int, int]] = DEFAULT_API_TIMEOUT,
|
|
34
35
|
) -> Dict[str, Any]:
|
|
35
36
|
"""Start the optimization run."""
|
|
36
37
|
with console.status("[bold green]Starting Optimization..."):
|
|
@@ -53,8 +54,14 @@ def start_optimization_run(
|
|
|
53
54
|
timeout=timeout,
|
|
54
55
|
)
|
|
55
56
|
response.raise_for_status()
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
result = response.json()
|
|
58
|
+
# Handle None values for code and plan fields
|
|
59
|
+
if result.get("plan") is None:
|
|
60
|
+
result["plan"] = ""
|
|
61
|
+
if result.get("code") is None:
|
|
62
|
+
result["code"] = ""
|
|
63
|
+
return result
|
|
64
|
+
except Exception as e:
|
|
58
65
|
handle_api_error(e, console)
|
|
59
66
|
sys.exit(1)
|
|
60
67
|
except Exception as e:
|
|
@@ -63,12 +70,13 @@ def start_optimization_run(
|
|
|
63
70
|
|
|
64
71
|
|
|
65
72
|
def evaluate_feedback_then_suggest_next_solution(
|
|
73
|
+
console: Console,
|
|
66
74
|
run_id: str,
|
|
67
75
|
execution_output: str,
|
|
68
76
|
additional_instructions: str = None,
|
|
69
77
|
api_keys: Dict[str, Any] = {},
|
|
70
78
|
auth_headers: dict = {},
|
|
71
|
-
timeout: Union[int, Tuple[int, int]] =
|
|
79
|
+
timeout: Union[int, Tuple[int, int]] = DEFAULT_API_TIMEOUT,
|
|
72
80
|
) -> Dict[str, Any]:
|
|
73
81
|
"""Evaluate the feedback and suggest the next solution."""
|
|
74
82
|
try:
|
|
@@ -83,10 +91,17 @@ def evaluate_feedback_then_suggest_next_solution(
|
|
|
83
91
|
timeout=timeout,
|
|
84
92
|
)
|
|
85
93
|
response.raise_for_status()
|
|
86
|
-
|
|
94
|
+
result = response.json()
|
|
95
|
+
# Handle None values for code and plan fields
|
|
96
|
+
if result.get("plan") is None:
|
|
97
|
+
result["plan"] = ""
|
|
98
|
+
if result.get("code") is None:
|
|
99
|
+
result["code"] = ""
|
|
100
|
+
|
|
101
|
+
return result
|
|
87
102
|
except requests.exceptions.HTTPError as e:
|
|
88
103
|
# Allow caller to handle suggest errors, maybe retry or terminate
|
|
89
|
-
handle_api_error(e,
|
|
104
|
+
handle_api_error(e, console) # Use default console if none passed
|
|
90
105
|
raise # Re-raise the exception
|
|
91
106
|
except Exception as e:
|
|
92
107
|
print(f"Error: {e}") # Use print as console might not be available
|
|
@@ -94,7 +109,11 @@ def evaluate_feedback_then_suggest_next_solution(
|
|
|
94
109
|
|
|
95
110
|
|
|
96
111
|
def get_optimization_run_status(
|
|
97
|
-
|
|
112
|
+
console: Console,
|
|
113
|
+
run_id: str,
|
|
114
|
+
include_history: bool = False,
|
|
115
|
+
auth_headers: dict = {},
|
|
116
|
+
timeout: Union[int, Tuple[int, int]] = DEFAULT_API_TIMEOUT,
|
|
98
117
|
) -> Dict[str, Any]:
|
|
99
118
|
"""Get the current status of the optimization run."""
|
|
100
119
|
try:
|
|
@@ -102,16 +121,30 @@ def get_optimization_run_status(
|
|
|
102
121
|
f"{__base_url__}/runs/{run_id}", params={"include_history": include_history}, headers=auth_headers, timeout=timeout
|
|
103
122
|
)
|
|
104
123
|
response.raise_for_status()
|
|
105
|
-
|
|
124
|
+
result = response.json()
|
|
125
|
+
# Handle None values for code and plan fields in best_result and nodes
|
|
126
|
+
if result.get("best_result"):
|
|
127
|
+
if result["best_result"].get("code") is None:
|
|
128
|
+
result["best_result"]["code"] = ""
|
|
129
|
+
if result["best_result"].get("plan") is None:
|
|
130
|
+
result["best_result"]["plan"] = ""
|
|
131
|
+
# Handle None values for code and plan fields in nodes array
|
|
132
|
+
if result.get("nodes"):
|
|
133
|
+
for i, node in enumerate(result["nodes"]):
|
|
134
|
+
if node.get("plan") is None:
|
|
135
|
+
result["nodes"][i]["plan"] = ""
|
|
136
|
+
if node.get("code") is None:
|
|
137
|
+
result["nodes"][i]["code"] = ""
|
|
138
|
+
return result
|
|
106
139
|
except requests.exceptions.HTTPError as e:
|
|
107
|
-
handle_api_error(e,
|
|
140
|
+
handle_api_error(e, console) # Use default console
|
|
108
141
|
raise # Re-raise
|
|
109
142
|
except Exception as e:
|
|
110
143
|
print(f"Error getting run status: {e}")
|
|
111
144
|
raise # Re-raise
|
|
112
145
|
|
|
113
146
|
|
|
114
|
-
def send_heartbeat(run_id: str, auth_headers: dict = {}, timeout: Union[int, Tuple[int, int]] = 10) -> bool:
|
|
147
|
+
def send_heartbeat(run_id: str, auth_headers: dict = {}, timeout: Union[int, Tuple[int, int]] = (10, 10)) -> bool:
|
|
115
148
|
"""Send a heartbeat signal to the backend."""
|
|
116
149
|
try:
|
|
117
150
|
response = requests.put(f"{__base_url__}/runs/{run_id}/heartbeat", headers=auth_headers, timeout=timeout)
|
|
@@ -119,9 +152,9 @@ def send_heartbeat(run_id: str, auth_headers: dict = {}, timeout: Union[int, Tup
|
|
|
119
152
|
return True
|
|
120
153
|
except requests.exceptions.HTTPError as e:
|
|
121
154
|
if e.response.status_code == 409:
|
|
122
|
-
print(
|
|
155
|
+
print("Polling ignore: Run {run_id} is not running.", file=sys.stderr)
|
|
123
156
|
else:
|
|
124
|
-
print(f"
|
|
157
|
+
print(f"Polling failed for run {run_id}: HTTP {e.response.status_code}", file=sys.stderr)
|
|
125
158
|
return False
|
|
126
159
|
except Exception as e:
|
|
127
160
|
print(f"Error sending heartbeat for run {run_id}: {e}", file=sys.stderr)
|
|
@@ -134,7 +167,7 @@ def report_termination(
|
|
|
134
167
|
reason: str,
|
|
135
168
|
details: Optional[str] = None,
|
|
136
169
|
auth_headers: dict = {},
|
|
137
|
-
timeout: Union[int, Tuple[int, int]] = 30,
|
|
170
|
+
timeout: Union[int, Tuple[int, int]] = (10, 30),
|
|
138
171
|
) -> bool:
|
|
139
172
|
"""Report the termination reason to the backend."""
|
|
140
173
|
try:
|
|
@@ -172,20 +205,21 @@ def _determine_model_and_api_key() -> tuple[str, dict[str, str]]:
|
|
|
172
205
|
api_key_dict = {"GEMINI_API_KEY": llm_api_keys["GEMINI_API_KEY"]}
|
|
173
206
|
else:
|
|
174
207
|
# This should never happen if determine_default_model works correctly
|
|
175
|
-
raise ValueError(f"Unknown model
|
|
208
|
+
raise ValueError(f"Unknown default model choice: {model}")
|
|
176
209
|
|
|
177
210
|
return model, api_key_dict
|
|
178
211
|
|
|
179
212
|
|
|
180
213
|
def get_optimization_suggestions_from_codebase(
|
|
214
|
+
console: Console,
|
|
181
215
|
gitingest_summary: str,
|
|
182
216
|
gitingest_tree: str,
|
|
183
217
|
gitingest_content_str: str,
|
|
184
|
-
console: Console,
|
|
185
218
|
auth_headers: dict = {},
|
|
186
|
-
timeout: Union[int, Tuple[int, int]] =
|
|
219
|
+
timeout: Union[int, Tuple[int, int]] = DEFAULT_API_TIMEOUT,
|
|
187
220
|
) -> Optional[List[Dict[str, Any]]]:
|
|
188
221
|
"""Analyze codebase and get optimization suggestions using the model-agnostic backend API."""
|
|
222
|
+
model, api_key_dict = _determine_model_and_api_key()
|
|
189
223
|
try:
|
|
190
224
|
model, api_key_dict = _determine_model_and_api_key()
|
|
191
225
|
response = requests.post(
|
|
@@ -204,7 +238,7 @@ def get_optimization_suggestions_from_codebase(
|
|
|
204
238
|
result = response.json()
|
|
205
239
|
return [option for option in result.get("options", [])]
|
|
206
240
|
|
|
207
|
-
except
|
|
241
|
+
except Exception as e:
|
|
208
242
|
handle_api_error(e, console)
|
|
209
243
|
return None
|
|
210
244
|
except Exception as e:
|
|
@@ -213,14 +247,15 @@ def get_optimization_suggestions_from_codebase(
|
|
|
213
247
|
|
|
214
248
|
|
|
215
249
|
def generate_evaluation_script_and_metrics(
|
|
250
|
+
console: Console,
|
|
216
251
|
target_file: str,
|
|
217
252
|
description: str,
|
|
218
253
|
gitingest_content_str: str,
|
|
219
|
-
console: Console,
|
|
220
254
|
auth_headers: dict = {},
|
|
221
|
-
timeout: Union[int, Tuple[int, int]] =
|
|
255
|
+
timeout: Union[int, Tuple[int, int]] = DEFAULT_API_TIMEOUT,
|
|
222
256
|
) -> Tuple[Optional[str], Optional[str], Optional[str], Optional[str]]:
|
|
223
257
|
"""Generate evaluation script and determine metrics using the model-agnostic backend API."""
|
|
258
|
+
model, api_key_dict = _determine_model_and_api_key()
|
|
224
259
|
try:
|
|
225
260
|
model, api_key_dict = _determine_model_and_api_key()
|
|
226
261
|
response = requests.post(
|
|
@@ -247,16 +282,17 @@ def generate_evaluation_script_and_metrics(
|
|
|
247
282
|
|
|
248
283
|
|
|
249
284
|
def analyze_evaluation_environment(
|
|
285
|
+
console: Console,
|
|
250
286
|
target_file: str,
|
|
251
287
|
description: str,
|
|
252
288
|
gitingest_summary: str,
|
|
253
289
|
gitingest_tree: str,
|
|
254
290
|
gitingest_content_str: str,
|
|
255
|
-
console: Console,
|
|
256
291
|
auth_headers: dict = {},
|
|
257
|
-
timeout: Union[int, Tuple[int, int]] =
|
|
292
|
+
timeout: Union[int, Tuple[int, int]] = DEFAULT_API_TIMEOUT,
|
|
258
293
|
) -> Optional[Dict[str, Any]]:
|
|
259
294
|
"""Analyze existing evaluation scripts and environment using the model-agnostic backend API."""
|
|
295
|
+
model, api_key_dict = _determine_model_and_api_key()
|
|
260
296
|
try:
|
|
261
297
|
model, api_key_dict = _determine_model_and_api_key()
|
|
262
298
|
response = requests.post(
|
|
@@ -276,7 +312,7 @@ def analyze_evaluation_environment(
|
|
|
276
312
|
response.raise_for_status()
|
|
277
313
|
return response.json()
|
|
278
314
|
|
|
279
|
-
except
|
|
315
|
+
except Exception as e:
|
|
280
316
|
handle_api_error(e, console)
|
|
281
317
|
return None
|
|
282
318
|
except Exception as e:
|
|
@@ -285,14 +321,15 @@ def analyze_evaluation_environment(
|
|
|
285
321
|
|
|
286
322
|
|
|
287
323
|
def analyze_script_execution_requirements(
|
|
324
|
+
console: Console,
|
|
288
325
|
script_content: str,
|
|
289
326
|
script_path: str,
|
|
290
327
|
target_file: str,
|
|
291
|
-
console: Console,
|
|
292
328
|
auth_headers: dict = {},
|
|
293
|
-
timeout: Union[int, Tuple[int, int]] =
|
|
329
|
+
timeout: Union[int, Tuple[int, int]] = DEFAULT_API_TIMEOUT,
|
|
294
330
|
) -> Optional[str]:
|
|
295
331
|
"""Analyze script to determine proper execution command using the model-agnostic backend API."""
|
|
332
|
+
model, api_key_dict = _determine_model_and_api_key()
|
|
296
333
|
try:
|
|
297
334
|
model, api_key_dict = _determine_model_and_api_key()
|
|
298
335
|
response = requests.post(
|
|
@@ -311,7 +348,7 @@ def analyze_script_execution_requirements(
|
|
|
311
348
|
result = response.json()
|
|
312
349
|
return result.get("command", f"python {script_path}")
|
|
313
350
|
|
|
314
|
-
except
|
|
351
|
+
except Exception as e:
|
|
315
352
|
handle_api_error(e, console)
|
|
316
353
|
return f"python {script_path}"
|
|
317
354
|
except Exception as e:
|
|
@@ -35,7 +35,7 @@ def save_api_key(api_key: str):
|
|
|
35
35
|
# Set file permissions to read/write for owner only (600)
|
|
36
36
|
os.chmod(CREDENTIALS_FILE, stat.S_IRUSR | stat.S_IWUSR)
|
|
37
37
|
except OSError as e:
|
|
38
|
-
print(f"Error:
|
|
38
|
+
print(f"Error: Unable to save credentials file or set permissions on {CREDENTIALS_FILE}: {e}")
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
def load_weco_api_key() -> str | None:
|
|
@@ -53,7 +53,7 @@ def load_weco_api_key() -> str | None:
|
|
|
53
53
|
credentials = json.load(f)
|
|
54
54
|
return credentials.get("api_key")
|
|
55
55
|
except (IOError, json.JSONDecodeError, OSError) as e:
|
|
56
|
-
print(f"Warning:
|
|
56
|
+
print(f"Warning: Unable to read credentials file at {CREDENTIALS_FILE}: {e}")
|
|
57
57
|
return None
|
|
58
58
|
|
|
59
59
|
|
|
@@ -64,7 +64,7 @@ def clear_api_key():
|
|
|
64
64
|
os.remove(CREDENTIALS_FILE)
|
|
65
65
|
print("Logged out successfully.")
|
|
66
66
|
except OSError as e:
|
|
67
|
-
print(f"Error:
|
|
67
|
+
print(f"Error: Unable to remove credentials file at {CREDENTIALS_FILE}: {e}")
|
|
68
68
|
else:
|
|
69
69
|
print("Already logged out.")
|
|
70
70
|
|
|
@@ -129,7 +129,9 @@ def perform_login(console: Console):
|
|
|
129
129
|
continue # Continue polling
|
|
130
130
|
else:
|
|
131
131
|
# Unexpected 202 response format
|
|
132
|
-
console.print(
|
|
132
|
+
console.print(
|
|
133
|
+
f"\n[bold red]Error:[/] Received unexpected response from authentication server: {token_data}"
|
|
134
|
+
)
|
|
133
135
|
return False
|
|
134
136
|
# Check for standard OAuth2 errors (often 400 Bad Request)
|
|
135
137
|
elif token_response.status_code == 400:
|
|
@@ -146,7 +148,7 @@ def perform_login(console: Console):
|
|
|
146
148
|
console.print("\n[bold red]Error:[/] Authorization denied by user.")
|
|
147
149
|
return False
|
|
148
150
|
else: # invalid_grant, etc.
|
|
149
|
-
error_desc = token_data.get("error_description", "Unknown error
|
|
151
|
+
error_desc = token_data.get("error_description", "Unknown authentication error occurred.")
|
|
150
152
|
console.print(f"\n[bold red]Error:[/] {error_desc} ({error_code})")
|
|
151
153
|
return False
|
|
152
154
|
|
|
@@ -50,7 +50,7 @@ class UserInteractionHelper:
|
|
|
50
50
|
|
|
51
51
|
if attempts >= max_retries:
|
|
52
52
|
self.console.print(f"[red]Maximum retry attempts ({max_retries}) reached. Exiting.[/]")
|
|
53
|
-
raise Exception("Maximum retry attempts exceeded")
|
|
53
|
+
raise Exception("Maximum retry attempts exceeded. Please try again.")
|
|
54
54
|
|
|
55
55
|
# Show available options without the full prompt
|
|
56
56
|
if choices:
|
|
@@ -66,7 +66,7 @@ class UserInteractionHelper:
|
|
|
66
66
|
continue
|
|
67
67
|
|
|
68
68
|
# This should never be reached due to the exception above, but just in case
|
|
69
|
-
raise Exception("Unexpected error
|
|
69
|
+
raise Exception("Unexpected error while selecting a choice")
|
|
70
70
|
|
|
71
71
|
def get_choice_numeric(self, prompt: str, max_number: int, default: int = None, max_retries: int = 5) -> int:
|
|
72
72
|
"""Get numeric choice with validation and error handling."""
|
|
@@ -87,7 +87,7 @@ class UserInteractionHelper:
|
|
|
87
87
|
|
|
88
88
|
if attempts >= max_retries:
|
|
89
89
|
self.console.print(f"[red]Maximum retry attempts ({max_retries}) reached. Exiting.[/]")
|
|
90
|
-
raise Exception("Maximum retry attempts exceeded")
|
|
90
|
+
raise Exception("Maximum retry attempts exceeded. Please try again.")
|
|
91
91
|
|
|
92
92
|
# Show valid range
|
|
93
93
|
self.console.print(f"Please enter a number between [bold]1[/] and [bold]{max_number}[/]")
|
|
@@ -115,7 +115,7 @@ class UserInteractionHelper:
|
|
|
115
115
|
|
|
116
116
|
if attempts >= max_retries:
|
|
117
117
|
self.console.print(f"[red]Maximum retry attempts ({max_retries}) reached. Exiting.[/]")
|
|
118
|
-
raise Exception("Maximum retry attempts exceeded")
|
|
118
|
+
raise Exception("Maximum retry attempts exceeded. Please try again.")
|
|
119
119
|
|
|
120
120
|
self.console.print("Valid options: [bold]y[/] / [bold]n[/]")
|
|
121
121
|
if default:
|
|
@@ -123,7 +123,7 @@ class UserInteractionHelper:
|
|
|
123
123
|
|
|
124
124
|
continue
|
|
125
125
|
|
|
126
|
-
raise Exception("Unexpected error
|
|
126
|
+
raise Exception("Unexpected error while selecting an option")
|
|
127
127
|
|
|
128
128
|
def display_optimization_options_table(self, options: List[Dict[str, str]]) -> None:
|
|
129
129
|
"""Display optimization options in a formatted table."""
|
|
@@ -215,7 +215,10 @@ class Chatbot:
|
|
|
215
215
|
|
|
216
216
|
with self.console.status("[bold green]Generating optimization suggestions...[/]"):
|
|
217
217
|
result = get_optimization_suggestions_from_codebase(
|
|
218
|
-
self.
|
|
218
|
+
console=self.console,
|
|
219
|
+
gitingest_summary=self.gitingest_summary,
|
|
220
|
+
gitingest_tree=self.gitingest_tree,
|
|
221
|
+
gitingest_content_str=self.gitingest_content_str,
|
|
219
222
|
)
|
|
220
223
|
|
|
221
224
|
if result and isinstance(result, list):
|
|
@@ -224,7 +227,7 @@ class Chatbot:
|
|
|
224
227
|
options = None
|
|
225
228
|
|
|
226
229
|
if not options or not isinstance(options, list):
|
|
227
|
-
self.console.print("[red]
|
|
230
|
+
self.console.print("[red]Unable to retrieve valid optimization options from the backend.[/]")
|
|
228
231
|
return None
|
|
229
232
|
|
|
230
233
|
if not options:
|
|
@@ -324,17 +327,19 @@ class Chatbot:
|
|
|
324
327
|
elif action == "g" or action == "r":
|
|
325
328
|
with self.console.status("[bold green]Generating evaluation script and determining metrics...[/]"):
|
|
326
329
|
result = generate_evaluation_script_and_metrics(
|
|
327
|
-
|
|
328
|
-
selected_option["
|
|
329
|
-
|
|
330
|
-
self.
|
|
330
|
+
console=self.console,
|
|
331
|
+
target_file=selected_option["target_file"],
|
|
332
|
+
description=selected_option["description"],
|
|
333
|
+
gitingest_content_str=self.gitingest_content_str,
|
|
331
334
|
)
|
|
332
335
|
if result and result[0]:
|
|
333
336
|
eval_script_content, metric_name, goal, reasoning = result
|
|
334
337
|
if reasoning:
|
|
335
338
|
self.console.print(f"[dim]Reasoning: {reasoning}[/]")
|
|
336
339
|
else:
|
|
337
|
-
self.console.print(
|
|
340
|
+
self.console.print(
|
|
341
|
+
"[red]Unable to generate an evaluation script. Please try providing a custom script path instead.[/]"
|
|
342
|
+
)
|
|
338
343
|
eval_script_content = None
|
|
339
344
|
metric_name = None
|
|
340
345
|
goal = None
|
|
@@ -371,7 +376,10 @@ class Chatbot:
|
|
|
371
376
|
# Analyze the script to determine the proper execution command
|
|
372
377
|
with self.console.status("[bold green]Analyzing script execution requirements...[/]"):
|
|
373
378
|
eval_command = analyze_script_execution_requirements(
|
|
374
|
-
|
|
379
|
+
console=self.console,
|
|
380
|
+
script_content=eval_script_content,
|
|
381
|
+
script_path=eval_script_path_str,
|
|
382
|
+
target_file=selected_option["target_file"],
|
|
375
383
|
)
|
|
376
384
|
|
|
377
385
|
return {
|
|
@@ -386,16 +394,16 @@ class Chatbot:
|
|
|
386
394
|
"""Get or create evaluation script configuration using intelligent conversation-guided approach."""
|
|
387
395
|
with self.console.status("[bold green]Analyzing evaluation environment...[/]"):
|
|
388
396
|
analysis = analyze_evaluation_environment(
|
|
389
|
-
|
|
390
|
-
selected_option["
|
|
391
|
-
|
|
392
|
-
self.
|
|
393
|
-
self.
|
|
394
|
-
self.
|
|
397
|
+
console=self.console,
|
|
398
|
+
target_file=selected_option["target_file"],
|
|
399
|
+
description=selected_option["description"],
|
|
400
|
+
gitingest_summary=self.gitingest_summary,
|
|
401
|
+
gitingest_tree=self.gitingest_tree,
|
|
402
|
+
gitingest_content_str=self.gitingest_content_str,
|
|
395
403
|
)
|
|
396
404
|
|
|
397
405
|
if not analysis:
|
|
398
|
-
self.console.print("[yellow]
|
|
406
|
+
self.console.print("[yellow]Unable to analyze evaluation environment. Falling back to script generation.[/]")
|
|
399
407
|
return self.handle_script_generation_workflow(selected_option)
|
|
400
408
|
|
|
401
409
|
self.evaluation_analysis = analysis
|
|
@@ -529,7 +537,10 @@ class Chatbot:
|
|
|
529
537
|
if not eval_command or eval_command == f"python {script_path}":
|
|
530
538
|
with self.console.status("[bold green]Analyzing script execution requirements...[/]"):
|
|
531
539
|
eval_command = analyze_script_execution_requirements(
|
|
532
|
-
|
|
540
|
+
console=self.console,
|
|
541
|
+
script_content=script_content,
|
|
542
|
+
script_path=script_path,
|
|
543
|
+
target_file=selected_option["target_file"],
|
|
533
544
|
)
|
|
534
545
|
|
|
535
546
|
self.current_step = "confirmation"
|
|
@@ -711,7 +722,7 @@ class Chatbot:
|
|
|
711
722
|
"""Setup evaluation environment for the selected optimization."""
|
|
712
723
|
eval_config = self.get_evaluation_configuration(selected_option)
|
|
713
724
|
if not eval_config:
|
|
714
|
-
self.console.print("[red]Evaluation script setup failed.[/]")
|
|
725
|
+
self.console.print("[red]Evaluation script setup failed. Please check your script configuration and try again.[/]")
|
|
715
726
|
return None
|
|
716
727
|
|
|
717
728
|
eval_config = self.confirm_and_finalize_evaluation_config(eval_config)
|
|
@@ -791,7 +802,7 @@ def run_onboarding_chatbot(
|
|
|
791
802
|
chatbot = Chatbot(project_path, console, run_parser, model)
|
|
792
803
|
chatbot.start()
|
|
793
804
|
except Exception as e:
|
|
794
|
-
console.print(f"[bold red]An unexpected error occurred
|
|
805
|
+
console.print(f"[bold red]An unexpected error occurred: {e}[/]")
|
|
795
806
|
import traceback
|
|
796
807
|
|
|
797
808
|
traceback.print_exc()
|