py-adtools 0.3.2__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.
- adtools/__init__.py +1 -0
- adtools/cli.py +61 -0
- adtools/evaluator/__init__.py +2 -0
- adtools/evaluator/auto_server.py +258 -0
- adtools/evaluator/py_evaluator.py +170 -0
- adtools/evaluator/py_evaluator_ray.py +110 -0
- adtools/lm/__init__.py +4 -0
- adtools/lm/lm_base.py +63 -0
- adtools/lm/openai_api.py +118 -0
- adtools/lm/sglang_server.py +423 -0
- adtools/lm/vllm_server.py +452 -0
- adtools/py_code.py +577 -0
- adtools/sandbox/__init__.py +2 -0
- adtools/sandbox/sandbox_executor.py +244 -0
- adtools/sandbox/sandbox_executor_ray.py +194 -0
- adtools/sandbox/utils.py +32 -0
- py_adtools-0.3.2.dist-info/METADATA +567 -0
- py_adtools-0.3.2.dist-info/RECORD +22 -0
- py_adtools-0.3.2.dist-info/WHEEL +5 -0
- py_adtools-0.3.2.dist-info/entry_points.txt +2 -0
- py_adtools-0.3.2.dist-info/licenses/LICENSE +21 -0
- py_adtools-0.3.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: py-adtools
|
|
3
|
+
Version: 0.3.2
|
|
4
|
+
Summary: Useful tools for parsing and evaluating Python programs for LLM-based algorithm design.
|
|
5
|
+
Author-email: Rui Zhang <rzhang.cs@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/RayZhhh/py-adtools
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Topic :: Scientific/Engineering
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: psutil
|
|
16
|
+
Requires-Dist: openai
|
|
17
|
+
Requires-Dist: ray<2.49.0,>=2.48.0
|
|
18
|
+
Requires-Dist: requests
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# Useful code parser, sandbox, and evaluator for LLM-aided algorithm design/code optimization
|
|
22
|
+
|
|
23
|
+
------
|
|
24
|
+
|
|
25
|
+
The figure demonstrates how a Python program is parsed into [PyCodeBlock](./adtools/py_code.py#L18-L33), [PyFunction](./adtools/py_code.py#L38-L115), [PyClass](./adtools/py_code.py#L118-L192), and [PyProgram](./adtools/py_code.py#L195-L242) via `adtools`.
|
|
26
|
+
|
|
27
|
+

|
|
28
|
+
|
|
29
|
+
------
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
> [!TIP]
|
|
34
|
+
>
|
|
35
|
+
> It is recommended to use Python >= 3.10.
|
|
36
|
+
|
|
37
|
+
Run the following instructions to install adtools.
|
|
38
|
+
|
|
39
|
+
```shell
|
|
40
|
+
pip install git+https://github.com/RayZhhh/py-adtools.git
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Or install via pip:
|
|
44
|
+
|
|
45
|
+
```shell
|
|
46
|
+
pip install py-adtools
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Code Parsing with [py_code](./algolm/py-adtools/adtools/py_code.py#L0-L560)
|
|
50
|
+
|
|
51
|
+
[adtools.py_code](./adtools/py_code.py#L0-L560) provides robust parsing of Python programs into structured components
|
|
52
|
+
that can be easily manipulated, modified, and analyzed.
|
|
53
|
+
|
|
54
|
+
### Core Components
|
|
55
|
+
|
|
56
|
+
The parser decomposes Python code into four main data structures:
|
|
57
|
+
|
|
58
|
+
| **Component** | **Description** | **Key Attributes** |
|
|
59
|
+
|-----------------|------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------|
|
|
60
|
+
| **PyProgram** | Represents the entire file. It maintains the exact sequence of scripts, functions, and classes. | `functions`, `classes`, `scripts`, `elements` |
|
|
61
|
+
| **PyFunction** | Represents a top-level function or a class method. You can modify its signature, decorators, docstring, or body dynamically. | `name`, `args`, `body`, `docstring`, `decorator`, `return_type` |
|
|
62
|
+
| **PyClass** | Represents a class definition. It serves as a container for methods and class-level statements. | `name`, `bases`, `functions` (methods), `body` |
|
|
63
|
+
| **PyCodeBlock** | Represents raw code segments, such as imports, global variables, or specific logic blocks inside classes. | `code` |
|
|
64
|
+
|
|
65
|
+
### Basic Usage
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from adtools import PyProgram
|
|
69
|
+
|
|
70
|
+
code = r"""
|
|
71
|
+
import ast, numba # This part will be parsed into PyCodeBlock
|
|
72
|
+
import numpy as np
|
|
73
|
+
|
|
74
|
+
@numba.jit() # This part will be parsed into PyFunction
|
|
75
|
+
def function(arg1, arg2=True):
|
|
76
|
+
'''Docstring.
|
|
77
|
+
This is a function.
|
|
78
|
+
'''
|
|
79
|
+
if arg2:
|
|
80
|
+
return arg1 * 2
|
|
81
|
+
else:
|
|
82
|
+
return arg1 * 4
|
|
83
|
+
|
|
84
|
+
@some.decorators() # This part will be parsed into PyClass
|
|
85
|
+
class PythonClass(BaseClass):
|
|
86
|
+
'''Docstring.'''
|
|
87
|
+
# Comments
|
|
88
|
+
class_var1 = 1 # This part will be parsed into PyCodeBlock
|
|
89
|
+
class_var2 = 2 # and placed in PyClass.body
|
|
90
|
+
|
|
91
|
+
def __init__(self, x): # This part will be parsed into PyFunction
|
|
92
|
+
self.x = x # and placed in PyClass.functions
|
|
93
|
+
|
|
94
|
+
def method1(self):
|
|
95
|
+
'''Docstring.
|
|
96
|
+
This is a class method.
|
|
97
|
+
'''
|
|
98
|
+
return self.x * 10
|
|
99
|
+
|
|
100
|
+
@some.decorators()
|
|
101
|
+
def method2(self, x, y):
|
|
102
|
+
return x + y + self.method1(x)
|
|
103
|
+
|
|
104
|
+
@some.decorators(100)
|
|
105
|
+
class InnerClass: # This part will be parsed into PyCodeBlock
|
|
106
|
+
'''Docstring.'''
|
|
107
|
+
def __init__(self): # and placed in PyClass.body
|
|
108
|
+
...
|
|
109
|
+
|
|
110
|
+
if __name__ == '__main__': # This part will be parsed into PyCodeBlock
|
|
111
|
+
res = function(1)
|
|
112
|
+
print(res)
|
|
113
|
+
res = PythonClass().method2(1, 2)
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
p = PyProgram.from_text(code, debug=True)
|
|
117
|
+
print(p)
|
|
118
|
+
print(f"-------------------------------------")
|
|
119
|
+
print(p.classes[0].functions[1])
|
|
120
|
+
print(f"-------------------------------------")
|
|
121
|
+
print(p.classes[0].functions[2].decorator)
|
|
122
|
+
print(f"-------------------------------------")
|
|
123
|
+
print(p.functions[0].name)
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Key Features
|
|
128
|
+
|
|
129
|
+
- **Preserves Code Structure**: Maintains original indentation and formatting
|
|
130
|
+
- **Handles Multiline Strings**: Properly preserves multiline string content without incorrect indentation
|
|
131
|
+
- **Access to Components**: Easily access functions, classes, and code blocks
|
|
132
|
+
- **Modify Code Elements**: Change function names, docstrings, or body content programmatically
|
|
133
|
+
- **Complete Program Representation**: [PyProgram](./adtools/py_code.py#L195-L242) maintains the exact sequence of
|
|
134
|
+
elements as they appear in the source code
|
|
135
|
+
|
|
136
|
+
## Safe Execution with `sandbox`
|
|
137
|
+
|
|
138
|
+
`adtools.sandbox` provides a secure execution environment for running untrusted code. It isolates execution in a separate process, allowing for timeout management, resource protection, and output redirection.
|
|
139
|
+
|
|
140
|
+
### Basic Usage
|
|
141
|
+
|
|
142
|
+
You can wrap any class or object with `SandboxExecutor` to execute its methods in a separate process.
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
import time
|
|
146
|
+
from typing import Any
|
|
147
|
+
from adtools.sandbox.sandbox_executor import SandboxExecutor
|
|
148
|
+
|
|
149
|
+
class SortAlgorithmEvaluator:
|
|
150
|
+
def evaluate_program(self, program: str) -> Any | None:
|
|
151
|
+
g = {}
|
|
152
|
+
exec(program, g)
|
|
153
|
+
sort_algo = g.get("merge_sort")
|
|
154
|
+
if not sort_algo: return None
|
|
155
|
+
|
|
156
|
+
input_data = [10, 2, 4, 76, 19, 29, 3, 5, 1]
|
|
157
|
+
start = time.time()
|
|
158
|
+
res = sort_algo(input_data)
|
|
159
|
+
duration = time.time() - start
|
|
160
|
+
|
|
161
|
+
return duration if res == sorted(input_data) else None
|
|
162
|
+
|
|
163
|
+
code_generated_by_llm = """
|
|
164
|
+
def merge_sort(arr):
|
|
165
|
+
if len(arr) <= 1: return arr
|
|
166
|
+
mid = len(arr) // 2
|
|
167
|
+
left = merge_sort(arr[:mid])
|
|
168
|
+
right = merge_sort(arr[mid:])
|
|
169
|
+
|
|
170
|
+
def merge(left, right):
|
|
171
|
+
result = []
|
|
172
|
+
i = j = 0
|
|
173
|
+
while i < len(left) and j < len(right):
|
|
174
|
+
if left[i] < right[j]:
|
|
175
|
+
result.append(left[i])
|
|
176
|
+
i += 1
|
|
177
|
+
else:
|
|
178
|
+
result.append(right[j])
|
|
179
|
+
j += 1
|
|
180
|
+
result.extend(left[i:])
|
|
181
|
+
result.extend(right[j:])
|
|
182
|
+
return result
|
|
183
|
+
|
|
184
|
+
return merge(left, right)
|
|
185
|
+
"""
|
|
186
|
+
|
|
187
|
+
if __name__ == "__main__":
|
|
188
|
+
# Initialize SandboxExecutor with the worker instance
|
|
189
|
+
sandbox = SandboxExecutor(SortAlgorithmEvaluator(), debug_mode=True)
|
|
190
|
+
|
|
191
|
+
# Securely execute the method
|
|
192
|
+
score = sandbox.secure_execute(
|
|
193
|
+
"evaluate_program",
|
|
194
|
+
method_args=(code_generated_by_llm,),
|
|
195
|
+
timeout_seconds=10
|
|
196
|
+
)
|
|
197
|
+
print(f"Score: {score}")
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Sandbox Executors
|
|
201
|
+
|
|
202
|
+
`adtools` provides two sandbox implementations:
|
|
203
|
+
|
|
204
|
+
- **[SandboxExecutor](./adtools/sandbox/sandbox_executor.py)**
|
|
205
|
+
- Standard multiprocessing-based sandbox.
|
|
206
|
+
- Captures return values via shared memory.
|
|
207
|
+
- Supports timeout and output redirection.
|
|
208
|
+
|
|
209
|
+
- **[SandboxExecutorRay](./adtools/sandbox/sandbox_executor_ray.py)**
|
|
210
|
+
- Ray-based sandbox for distributed execution.
|
|
211
|
+
- Ideal for scenarios requiring stronger isolation or cluster-based evaluation.
|
|
212
|
+
|
|
213
|
+
## Code Evaluation with `evaluator`
|
|
214
|
+
|
|
215
|
+
`adtools.evaluator` provides multiple secure evaluation options for running and testing Python code.
|
|
216
|
+
|
|
217
|
+
### Basic Usage
|
|
218
|
+
|
|
219
|
+
```python
|
|
220
|
+
import time
|
|
221
|
+
from typing import Dict, Callable, List, Any
|
|
222
|
+
|
|
223
|
+
from adtools.evaluator import PyEvaluator
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
class SortAlgorithmEvaluator(PyEvaluator):
|
|
227
|
+
def evaluate_program(
|
|
228
|
+
self,
|
|
229
|
+
program_str: str,
|
|
230
|
+
callable_functions_dict: Dict[str, Callable] | None,
|
|
231
|
+
callable_functions_list: List[Callable] | None,
|
|
232
|
+
callable_classes_dict: Dict[str, Callable] | None,
|
|
233
|
+
callable_classes_list: List[Callable] | None,
|
|
234
|
+
**kwargs,
|
|
235
|
+
) -> Any | None:
|
|
236
|
+
"""Evaluate a given sort algorithm program.
|
|
237
|
+
Args:
|
|
238
|
+
program_str : The raw program text.
|
|
239
|
+
callable_functions_dict: A dict maps function name to callable function.
|
|
240
|
+
callable_functions_list: A list of callable functions.
|
|
241
|
+
callable_classes_dict : A dict maps class name to callable class.
|
|
242
|
+
callable_classes_list : A list of callable classes.
|
|
243
|
+
Return:
|
|
244
|
+
Returns the evaluation result.
|
|
245
|
+
"""
|
|
246
|
+
# Get the sort algorithm
|
|
247
|
+
sort_algo: Callable = callable_functions_dict["merge_sort"]
|
|
248
|
+
# Test data
|
|
249
|
+
input = [10, 2, 4, 76, 19, 29, 3, 5, 1]
|
|
250
|
+
# Compute execution time
|
|
251
|
+
start = time.time()
|
|
252
|
+
res = sort_algo(input)
|
|
253
|
+
duration = time.time() - start
|
|
254
|
+
if res == sorted(input): # If the result is correct
|
|
255
|
+
return duration # Return the execution time as the score of the algorithm
|
|
256
|
+
else:
|
|
257
|
+
return None # Return None as the algorithm is incorrect
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
code_generated_by_llm = """
|
|
261
|
+
def merge_sort(arr):
|
|
262
|
+
if len(arr) <= 1:
|
|
263
|
+
return arr
|
|
264
|
+
|
|
265
|
+
mid = len(arr) // 2
|
|
266
|
+
left = merge_sort(arr[:mid])
|
|
267
|
+
right = merge_sort(arr[mid:])
|
|
268
|
+
|
|
269
|
+
return merge(left, right)
|
|
270
|
+
|
|
271
|
+
def merge(left, right):
|
|
272
|
+
result = []
|
|
273
|
+
i = j = 0
|
|
274
|
+
|
|
275
|
+
while i < len(left) and j < len(right):
|
|
276
|
+
if left[i] < right[j]:
|
|
277
|
+
result.append(left[i])
|
|
278
|
+
i += 1
|
|
279
|
+
else:
|
|
280
|
+
result.append(right[j])
|
|
281
|
+
j += 1
|
|
282
|
+
|
|
283
|
+
result.extend(left[i:])
|
|
284
|
+
result.extend(right[j:])
|
|
285
|
+
|
|
286
|
+
return result
|
|
287
|
+
"""
|
|
288
|
+
|
|
289
|
+
harmful_code_generated_by_llm = """
|
|
290
|
+
def merge_sort(arr):
|
|
291
|
+
print('I am harmful') # There will be no output since we redirect STDOUT to /dev/null by default.
|
|
292
|
+
while True:
|
|
293
|
+
pass
|
|
294
|
+
"""
|
|
295
|
+
|
|
296
|
+
if __name__ == "__main__":
|
|
297
|
+
evaluator = SortAlgorithmEvaluator()
|
|
298
|
+
|
|
299
|
+
# Evaluate
|
|
300
|
+
score = evaluator._exec_and_get_res(code_generated_by_llm)
|
|
301
|
+
print(f"Score: {score}")
|
|
302
|
+
|
|
303
|
+
# Secure evaluate (the evaluation is executed in a sandbox process)
|
|
304
|
+
score = evaluator.secure_evaluate(code_generated_by_llm, timeout_seconds=10)
|
|
305
|
+
print(f"Score: {score}")
|
|
306
|
+
|
|
307
|
+
# Evaluate a harmful code, the evaluation will be terminated within 10 seconds
|
|
308
|
+
# We will obtain a score of `None` due to the violation of time restriction
|
|
309
|
+
score = evaluator.secure_evaluate(harmful_code_generated_by_llm, timeout_seconds=10)
|
|
310
|
+
print(f"Score: {score}")
|
|
311
|
+
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Evaluator Types and Their Characteristics
|
|
315
|
+
|
|
316
|
+
`adtools` provides two different evaluator implementations, each optimized for different scenarios:
|
|
317
|
+
|
|
318
|
+
- **[PyEvaluator](./adtools/evaluator/py_evaluator.py#L36-L309)**
|
|
319
|
+
- *Uses shared memory* for extremely large return objects (e.g., large tensors)
|
|
320
|
+
- *Avoids pickle serialization overhead* for massive data
|
|
321
|
+
- *Best for high-performance scenarios* with very large result objects
|
|
322
|
+
- *Use case*: Evaluating ML algorithms that produce large tensors or arrays
|
|
323
|
+
|
|
324
|
+
- **[PyEvaluatorRay](./adtools/evaluator/py_evaluator_ray.py#L23-L209)**
|
|
325
|
+
- *Leverages Ray* for distributed, secure evaluation
|
|
326
|
+
- *Supports zero-copy return* of large objects
|
|
327
|
+
- *Ideal for cluster environments* and when maximum isolation is required
|
|
328
|
+
- *Use case*: Large-scale evaluation across multiple machines or when using GPU resources
|
|
329
|
+
|
|
330
|
+
All evaluators share the same interface through the abstract [PyEvaluator](./adtools/evaluator/py_evaluator.py#L36-L309)
|
|
331
|
+
class, making it easy to switch between implementations based on your specific needs.
|
|
332
|
+
|
|
333
|
+
## Practical Applications
|
|
334
|
+
|
|
335
|
+
### Parser for Code Manipulation
|
|
336
|
+
|
|
337
|
+
The parser is designed to handle complex scenarios, including **multiline strings**, **decorators**, and **indentation
|
|
338
|
+
management**.
|
|
339
|
+
|
|
340
|
+
```python
|
|
341
|
+
from adtools import PyProgram
|
|
342
|
+
|
|
343
|
+
# A complex piece of code with imports, decorators, and a class
|
|
344
|
+
code = r'''
|
|
345
|
+
import numpy as np
|
|
346
|
+
|
|
347
|
+
@jit(nopython=True)
|
|
348
|
+
def heuristics(x):
|
|
349
|
+
"""Calculates the heuristic value."""
|
|
350
|
+
return x * 0.5
|
|
351
|
+
|
|
352
|
+
class EvolutionStrategy:
|
|
353
|
+
population_size = 100
|
|
354
|
+
|
|
355
|
+
def __init__(self, mu, lambda_):
|
|
356
|
+
self.mu = mu
|
|
357
|
+
self.lambda_ = lambda_
|
|
358
|
+
|
|
359
|
+
def mutate(self, individual):
|
|
360
|
+
# Apply mutation
|
|
361
|
+
return individual + np.random.normal(0, 1)
|
|
362
|
+
'''
|
|
363
|
+
|
|
364
|
+
# 1. Parse the program
|
|
365
|
+
program = PyProgram.from_text(code)
|
|
366
|
+
|
|
367
|
+
# 2. Access and Modify Functions
|
|
368
|
+
func = program.functions[0]
|
|
369
|
+
print(f"Function detected: {func.name}")
|
|
370
|
+
# Output: Function detected: heuristics
|
|
371
|
+
|
|
372
|
+
# Modify the function programmatically
|
|
373
|
+
func.name = "fast_heuristics"
|
|
374
|
+
func.decorator = None # Remove decorator
|
|
375
|
+
func.docstring = "Optimized heuristic calculation."
|
|
376
|
+
|
|
377
|
+
# 3. Access Class Methods
|
|
378
|
+
cls_obj = program.classes[0]
|
|
379
|
+
init_method = cls_obj.functions[0]
|
|
380
|
+
mutate_method = cls_obj.functions[1]
|
|
381
|
+
|
|
382
|
+
print(f"Class: {cls_obj.name}, Method: {mutate_method.name}")
|
|
383
|
+
# Output: Class: EvolutionStrategy, Method: mutate
|
|
384
|
+
|
|
385
|
+
# 4. Generate the modified code
|
|
386
|
+
# The PyProgram object reconstructs the code preserving the original order
|
|
387
|
+
print("\n--- Reconstructed Code ---")
|
|
388
|
+
print(program)
|
|
389
|
+
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
### Parser for Prompt Construction
|
|
393
|
+
|
|
394
|
+
`adtools` is particularly powerful for LLM-based algorithm design, where you need to manage populations of generated
|
|
395
|
+
code, standardize formats for prompts, or inject generated logic into existing templates.
|
|
396
|
+
|
|
397
|
+
In LLM-based Automated Algorithm Design (LLM-AAD), you often maintain a population of algorithms. You may need to rename
|
|
398
|
+
them (e.g., `v1`, `v2`), standardize their docstrings for the context, or remove docstrings to save token costs before
|
|
399
|
+
feeding them back into the LLM.
|
|
400
|
+
|
|
401
|
+
```python
|
|
402
|
+
from adtools import PyFunction
|
|
403
|
+
|
|
404
|
+
# Assume LLM generated two variants of a crossover algorithm
|
|
405
|
+
llm_output_1 = '''
|
|
406
|
+
def crossover(p1, p2):
|
|
407
|
+
"""Single point crossover."""
|
|
408
|
+
point = len(p1) // 2
|
|
409
|
+
return p1[:point] + p2[point:], p2[:point] + p1[point:]
|
|
410
|
+
'''
|
|
411
|
+
|
|
412
|
+
llm_output_2 = """
|
|
413
|
+
def crossover_op(parent_a, parent_b):
|
|
414
|
+
# This is a uniform crossover
|
|
415
|
+
mask = [True, False] * (len(parent_a) // 2)
|
|
416
|
+
return [a if m else b for a, b, m in zip(parent_a, parent_b, mask)]
|
|
417
|
+
"""
|
|
418
|
+
|
|
419
|
+
# Parse the functions
|
|
420
|
+
func_v1 = PyFunction.extract_first_function_from_text(llm_output_1)
|
|
421
|
+
func_v2 = PyFunction.extract_first_function_from_text(llm_output_2)
|
|
422
|
+
|
|
423
|
+
# --- Modification Logic ---
|
|
424
|
+
|
|
425
|
+
# 1. Standardize Naming: Rename to v1 and v2
|
|
426
|
+
func_v1.name = "crossover_v1"
|
|
427
|
+
func_v2.name = "crossover_v2"
|
|
428
|
+
|
|
429
|
+
# 2. Docstring Management:
|
|
430
|
+
# For v1: Enforce a specific docstring format for the prompt
|
|
431
|
+
func_v1.docstring = "Variant 1: Implementation of Single Point Crossover."
|
|
432
|
+
|
|
433
|
+
# For v2: Remove docstring entirely (e.g., to reduce context window usage)
|
|
434
|
+
func_v2.docstring = None
|
|
435
|
+
|
|
436
|
+
# --- Construct Prompt ---
|
|
437
|
+
|
|
438
|
+
prompt = "Here are the two crossover algorithms currently in the population:\n\n"
|
|
439
|
+
prompt += str(func_v1) + "\n"
|
|
440
|
+
prompt += str(func_v2) + "\n"
|
|
441
|
+
prompt += "Please generate a v3 that combines the best features of both."
|
|
442
|
+
|
|
443
|
+
print(prompt)
|
|
444
|
+
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
**Output:**
|
|
448
|
+
|
|
449
|
+
```text
|
|
450
|
+
Here are the two crossover algorithms currently in the population:
|
|
451
|
+
|
|
452
|
+
def crossover_v1(p1, p2):
|
|
453
|
+
"""Variant 1: Implementation of Single Point Crossover."""
|
|
454
|
+
point = len(p1) // 2
|
|
455
|
+
return p1[:point] + p2[point:], p2[:point] + p1[point:]
|
|
456
|
+
|
|
457
|
+
def crossover_v2(parent_a, parent_b):
|
|
458
|
+
# This is a uniform crossover
|
|
459
|
+
mask = [True, False] * (len(parent_a) // 2)
|
|
460
|
+
return [a if m else b for a, b, m in zip(parent_a, parent_b, mask)]
|
|
461
|
+
|
|
462
|
+
Please generate a v3 that combines the best features of both.
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### Secure Code Evaluation using Evaluators
|
|
466
|
+
|
|
467
|
+
When evaluating code generated by LLMs, safety and reliability are critical:
|
|
468
|
+
|
|
469
|
+
```python
|
|
470
|
+
import time
|
|
471
|
+
from adtools.evaluator import PyEvaluator
|
|
472
|
+
from typing import Dict, Callable, List
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
class AlgorithmValidator(PyEvaluator):
|
|
476
|
+
def evaluate_program(
|
|
477
|
+
self,
|
|
478
|
+
program_str: str,
|
|
479
|
+
callable_functions_dict: Dict[str, Callable] | None,
|
|
480
|
+
callable_functions_list: List[Callable] | None,
|
|
481
|
+
callable_classes_dict: Dict[str, Callable] | None,
|
|
482
|
+
callable_classes_list: List[Callable] | None,
|
|
483
|
+
**kwargs
|
|
484
|
+
) -> dict:
|
|
485
|
+
results = {"correct": 0, "total": 0, "time": 0}
|
|
486
|
+
|
|
487
|
+
try:
|
|
488
|
+
# Get the sorting function
|
|
489
|
+
sort_func = callable_functions_dict.get("sort_algorithm")
|
|
490
|
+
if not sort_func:
|
|
491
|
+
return {**results, "error": "Missing required function"}
|
|
492
|
+
|
|
493
|
+
# Test with multiple inputs
|
|
494
|
+
test_cases = [
|
|
495
|
+
[5, 3, 1, 4, 2],
|
|
496
|
+
[1, 2, 3, 4, 5],
|
|
497
|
+
[5, 4, 3, 2, 1],
|
|
498
|
+
list(range(100)), # Large test case
|
|
499
|
+
[],
|
|
500
|
+
]
|
|
501
|
+
|
|
502
|
+
for case in test_cases:
|
|
503
|
+
start = time.time()
|
|
504
|
+
result = sort_func(
|
|
505
|
+
case[:]
|
|
506
|
+
) # Pass a copy to avoid in-place modification
|
|
507
|
+
duration = time.time() - start
|
|
508
|
+
|
|
509
|
+
results["total"] += 1
|
|
510
|
+
if result == sorted(case):
|
|
511
|
+
results["correct"] += 1
|
|
512
|
+
results["time"] += duration
|
|
513
|
+
|
|
514
|
+
except Exception as e:
|
|
515
|
+
results["error"] = str(e)
|
|
516
|
+
|
|
517
|
+
return results
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
# Example usage with potentially problematic code
|
|
521
|
+
problematic_code = """
|
|
522
|
+
def sort_algorithm(arr):
|
|
523
|
+
# This implementation has a bug for empty arrays
|
|
524
|
+
if not arr:
|
|
525
|
+
return [] # Missing this case would cause failure
|
|
526
|
+
|
|
527
|
+
# Implementation with potential infinite loop
|
|
528
|
+
i = 0
|
|
529
|
+
while i < len(arr) - 1:
|
|
530
|
+
if arr[i] > arr[i+1]:
|
|
531
|
+
arr[i], arr[i+1] = arr[i+1], arr[i]
|
|
532
|
+
i = 0 # Reset to beginning after swap
|
|
533
|
+
else:
|
|
534
|
+
i += 1
|
|
535
|
+
return arr
|
|
536
|
+
"""
|
|
537
|
+
|
|
538
|
+
malicious_code = """
|
|
539
|
+
def sort_algorithm(arr):
|
|
540
|
+
import time
|
|
541
|
+
time.sleep(15) # Exceeds timeout
|
|
542
|
+
return sorted(arr)
|
|
543
|
+
"""
|
|
544
|
+
|
|
545
|
+
validator = AlgorithmValidator()
|
|
546
|
+
print(validator.secure_evaluate(problematic_code, timeout_seconds=5))
|
|
547
|
+
print(validator.secure_evaluate(malicious_code, timeout_seconds=5))
|
|
548
|
+
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
This demonstrates how `adtools` handles:
|
|
552
|
+
|
|
553
|
+
- **Timeout protection**: Malicious code with infinite loops is terminated
|
|
554
|
+
- **Error isolation**: Exceptions in evaluated code don't crash your main process
|
|
555
|
+
- **Output redirection**: Prevents unwanted print statements from cluttering your console
|
|
556
|
+
- **Resource management**: Proper cleanup of processes and shared resources
|
|
557
|
+
|
|
558
|
+
The evaluation framework ensures that even if the code contains errors, infinite loops, or attempts to access system
|
|
559
|
+
resources, your main application remains safe and responsive.
|
|
560
|
+
|
|
561
|
+
## License
|
|
562
|
+
|
|
563
|
+
This project is licensed under the **MIT License**. See the [LICENSE](./LICENSE) file for details.
|
|
564
|
+
|
|
565
|
+
## Contact & Feedback
|
|
566
|
+
|
|
567
|
+
If you have any questions, encounter bugs, or have suggestions for improvement, please feel free to [open an issue](https://github.com/RayZhhh/py-adtools/issues) or contact us. Your contributions and feedback are highly appreciated!
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
adtools/__init__.py,sha256=vf6o3ORG-PrgJ0d7yOjOn9QXZvJWDz8nOEyC6r7tU2U,72
|
|
2
|
+
adtools/cli.py,sha256=DiKZ2f1sfrAHQuB7Mh01AgUqyeHmh3Nnka6umTcwlNE,2315
|
|
3
|
+
adtools/py_code.py,sha256=gIJ6mAIlF_aSCUv2VE4rOmZeS_40hzbI5mgWzEgpeJk,22582
|
|
4
|
+
adtools/evaluator/__init__.py,sha256=ZkgKlBPSeUeWPlZxtJgNQYlDc0RspnAuRDHiH_pLR-Y,117
|
|
5
|
+
adtools/evaluator/auto_server.py,sha256=DscbWaVMxzeuZeV6qDyWkLQpDhn-LOSjd1BieZj9enw,8215
|
|
6
|
+
adtools/evaluator/py_evaluator.py,sha256=JqbvdJOd8APHebHSybMegMB_3xiAGrN5AonCKiSClRM,6709
|
|
7
|
+
adtools/evaluator/py_evaluator_ray.py,sha256=unfqXqpWYsIpBbuiKKHqOQVMl9gxfA542OP_8UrGsnQ,4065
|
|
8
|
+
adtools/lm/__init__.py,sha256=ppzCTJk9Ny-RLPyxarrCwu1kmr4homHkwiDsGHI-P4o,185
|
|
9
|
+
adtools/lm/lm_base.py,sha256=qUHyR5jYeIllnYoR4IbSIKJZLaigW7329osVuwD0btk,1818
|
|
10
|
+
adtools/lm/openai_api.py,sha256=l-qpC55L-scJUWf7DLnjimh9l2jfdGGDrXs5nAYS7NM,3841
|
|
11
|
+
adtools/lm/sglang_server.py,sha256=ClhMRV2pNFbQf_mX8kaUKMEfWgyHay6DqI6RWCMN8K0,15665
|
|
12
|
+
adtools/lm/vllm_server.py,sha256=4i76FLxIDemaUtJbULBaO5efTvh8nB7YJOyetzpQ36Q,16365
|
|
13
|
+
adtools/sandbox/__init__.py,sha256=SW9xejOgC2o5KFauuRcRoAEoI5Q9M-1W7QL3_uvNvbQ,147
|
|
14
|
+
adtools/sandbox/sandbox_executor.py,sha256=H4UeqQeAKcwSCJ8T4G7K70rAJZlmZifTMuHezfrLvck,10065
|
|
15
|
+
adtools/sandbox/sandbox_executor_ray.py,sha256=1w3RPGAqHk3fD1ZzQFRsgmd1I1dsVgMql36hIr0tTHw,6853
|
|
16
|
+
adtools/sandbox/utils.py,sha256=kxIy37T1XIvAtdbPKLZO5jM4PFpZxknlKxPpAREyqMc,1111
|
|
17
|
+
py_adtools-0.3.2.dist-info/licenses/LICENSE,sha256=E5GGyecx3y5h2gcEGQloF-rDY9wbaef5IHjRsvtFbt8,1065
|
|
18
|
+
py_adtools-0.3.2.dist-info/METADATA,sha256=0eGGxvIypsAeCocdO_fILVCPB2_nNY80aJ0_-ETGiaA,18940
|
|
19
|
+
py_adtools-0.3.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
20
|
+
py_adtools-0.3.2.dist-info/entry_points.txt,sha256=s-kKqZwk0k95NFGLNx7zd5Rdn0jmZkuu1VkN59zZUMw,45
|
|
21
|
+
py_adtools-0.3.2.dist-info/top_level.txt,sha256=X2kKzmJFDAKR2FWCij5pfMG9pVVjVUomyl4e-1VLXIk,8
|
|
22
|
+
py_adtools-0.3.2.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Rui Zhang
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
adtools
|