quantalogic 0.61.2__py3-none-any.whl → 0.61.3__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.
Files changed (29) hide show
  1. quantalogic/codeact/agent.py +1 -1
  2. quantalogic/codeact/cli.py +5 -5
  3. quantalogic/codeact/tools_manager.py +1 -1
  4. quantalogic/tools/action_gen_safe.py +340 -0
  5. quantalogic/tools/write_file_tool.py +7 -8
  6. {quantalogic-0.61.2.dist-info → quantalogic-0.61.3.dist-info}/METADATA +2 -1
  7. {quantalogic-0.61.2.dist-info → quantalogic-0.61.3.dist-info}/RECORD +10 -28
  8. quantalogic/python_interpreter/__init__.py +0 -23
  9. quantalogic/python_interpreter/assignment_visitors.py +0 -63
  10. quantalogic/python_interpreter/base_visitors.py +0 -20
  11. quantalogic/python_interpreter/class_visitors.py +0 -22
  12. quantalogic/python_interpreter/comprehension_visitors.py +0 -172
  13. quantalogic/python_interpreter/context_visitors.py +0 -59
  14. quantalogic/python_interpreter/control_flow_visitors.py +0 -88
  15. quantalogic/python_interpreter/exception_visitors.py +0 -109
  16. quantalogic/python_interpreter/exceptions.py +0 -39
  17. quantalogic/python_interpreter/execution.py +0 -202
  18. quantalogic/python_interpreter/function_utils.py +0 -386
  19. quantalogic/python_interpreter/function_visitors.py +0 -209
  20. quantalogic/python_interpreter/import_visitors.py +0 -28
  21. quantalogic/python_interpreter/interpreter_core.py +0 -358
  22. quantalogic/python_interpreter/literal_visitors.py +0 -74
  23. quantalogic/python_interpreter/misc_visitors.py +0 -148
  24. quantalogic/python_interpreter/operator_visitors.py +0 -108
  25. quantalogic/python_interpreter/scope.py +0 -10
  26. quantalogic/python_interpreter/visit_handlers.py +0 -110
  27. {quantalogic-0.61.2.dist-info → quantalogic-0.61.3.dist-info}/LICENSE +0 -0
  28. {quantalogic-0.61.2.dist-info → quantalogic-0.61.3.dist-info}/WHEEL +0 -0
  29. {quantalogic-0.61.2.dist-info → quantalogic-0.61.3.dist-info}/entry_points.txt +0 -0
@@ -5,8 +5,8 @@ from typing import Callable, Dict, List, Optional, Tuple
5
5
  from jinja2 import Environment, FileSystemLoader
6
6
  from loguru import logger
7
7
  from lxml import etree
8
+ from quantalogic_pythonbox import execute_async
8
9
 
9
- from quantalogic.python_interpreter import execute_async
10
10
  from quantalogic.tools import Tool
11
11
 
12
12
  from .constants import MAX_GENERATE_PROGRAM_TOKENS, MAX_HISTORY_TOKENS, MAX_TOKENS, TEMPLATE_DIR
@@ -4,11 +4,9 @@ from typing import Callable, List, Optional, Union
4
4
  import typer
5
5
  from loguru import logger
6
6
 
7
- from quantalogic.tools import create_tool
8
-
9
- from .agent import Agent # Import only Agent from agent.py
10
- from .constants import DEFAULT_MODEL, LOG_FILE
11
- from .events import ( # Import events from events.py
7
+ from quantalogic.codeact.agent import Agent # Import only Agent from agent.py
8
+ from quantalogic.codeact.constants import DEFAULT_MODEL, LOG_FILE
9
+ from quantalogic.codeact.events import ( # Import events from events.py
12
10
  ActionExecutedEvent,
13
11
  ActionGeneratedEvent,
14
12
  ErrorOccurredEvent,
@@ -22,6 +20,8 @@ from .events import ( # Import events from events.py
22
20
  ToolExecutionErrorEvent,
23
21
  ToolExecutionStartedEvent,
24
22
  )
23
+ from quantalogic.tools import create_tool
24
+
25
25
  from .tools_manager import Tool, get_default_tools
26
26
  from .utils import XMLResultHandler
27
27
 
@@ -130,6 +130,6 @@ def get_default_tools(model: str) -> List[Tool]:
130
130
  # RipgrepTool(),
131
131
  # SearchDefinitionNamesTool(),
132
132
  #TaskCompleteTool(),
133
- WriteFileTool(),
133
+ WriteFileTool(disable_ensure_tmp_path=True),
134
134
  AgentTool(model=model),
135
135
  ]
@@ -0,0 +1,340 @@
1
+ import ast
2
+ import asyncio
3
+ from functools import partial
4
+ from typing import Callable, Dict, List
5
+
6
+ import litellm
7
+ import typer
8
+ from loguru import logger
9
+
10
+ from quantalogic.tools.tool import Tool, ToolArgument
11
+ from quantalogic.utils.python_interpreter import ASTInterpreter
12
+
13
+ # Configure loguru to log to a file with rotation
14
+ logger.add("action_gen_safe.log", rotation="10 MB", level="DEBUG")
15
+
16
+ # Initialize Typer app
17
+ app = typer.Typer()
18
+
19
+ # Define tool classes with logging in async_execute
20
+ class AddTool(Tool):
21
+ def __init__(self):
22
+ super().__init__(
23
+ name="add_tool",
24
+ description="Adds two numbers and returns the sum.",
25
+ arguments=[
26
+ ToolArgument(name="a", arg_type="int", description="First number", required=True),
27
+ ToolArgument(name="b", arg_type="int", description="Second number", required=True)
28
+ ],
29
+ return_type="int"
30
+ )
31
+
32
+ async def async_execute(self, **kwargs) -> str:
33
+ logger.info(f"Adding {kwargs['a']} and {kwargs['b']}")
34
+ return str(int(kwargs["a"]) + int(kwargs["b"]))
35
+
36
+ class MultiplyTool(Tool):
37
+ def __init__(self):
38
+ super().__init__(
39
+ name="multiply_tool",
40
+ description="Multiplies two numbers and returns the product.",
41
+ arguments=[
42
+ ToolArgument(name="x", arg_type="int", description="First number", required=True),
43
+ ToolArgument(name="y", arg_type="int", description="Second number", required=True)
44
+ ],
45
+ return_type="int"
46
+ )
47
+
48
+ async def async_execute(self, **kwargs) -> str:
49
+ logger.info(f"Multiplying {kwargs['x']} and {kwargs['y']}")
50
+ return str(int(kwargs["x"]) * int(kwargs["y"]))
51
+
52
+ class ConcatTool(Tool):
53
+ def __init__(self):
54
+ super().__init__(
55
+ name="concat_tool",
56
+ description="Concatenates two strings.",
57
+ arguments=[
58
+ ToolArgument(name="s1", arg_type="string", description="First string", required=True),
59
+ ToolArgument(name="s2", arg_type="string", description="Second string", required=True)
60
+ ],
61
+ return_type="string"
62
+ )
63
+
64
+ async def async_execute(self, **kwargs) -> str:
65
+ logger.info(f"Concatenating '{kwargs['s1']}' and '{kwargs['s2']}'")
66
+ return kwargs["s1"] + kwargs["s2"]
67
+
68
+ class AgentTool(Tool):
69
+ def __init__(self, model: str = "gemini/gemini-2.0-flash"):
70
+ super().__init__(
71
+ name="agent_tool",
72
+ description="Generates text using a language model based on a system prompt and user prompt.",
73
+ arguments=[
74
+ ToolArgument(name="system_prompt", arg_type="string", description="System prompt to guide the model's behavior", required=True),
75
+ ToolArgument(name="prompt", arg_type="string", description="User prompt to generate a response for", required=True),
76
+ ToolArgument(name="temperature", arg_type="float", description="Temperature for generation (0 to 1)", required=True)
77
+ ],
78
+ return_type="string"
79
+ )
80
+ self.model = model
81
+
82
+ async def async_execute(self, **kwargs) -> str:
83
+ system_prompt = kwargs["system_prompt"]
84
+ prompt = kwargs["prompt"]
85
+ temperature = float(kwargs["temperature"])
86
+
87
+ # Validate temperature
88
+ if not 0 <= temperature <= 1:
89
+ logger.error(f"Temperature {temperature} is out of range (0-1)")
90
+ raise ValueError("Temperature must be between 0 and 1")
91
+
92
+ logger.info(f"Generating text with model {self.model}, temperature {temperature}")
93
+ try:
94
+ response = await litellm.acompletion(
95
+ model=self.model,
96
+ messages=[
97
+ {"role": "system", "content": system_prompt},
98
+ {"role": "user", "content": prompt}
99
+ ],
100
+ temperature=temperature,
101
+ max_tokens=1000 # Reasonable default for text generation
102
+ )
103
+ generated_text = response.choices[0].message.content.strip()
104
+ logger.debug(f"Generated text: {generated_text}")
105
+ return generated_text
106
+ except Exception as e:
107
+ logger.error(f"Failed to generate text with {self.model}: {str(e)}")
108
+ raise RuntimeError(f"Text generation failed: {str(e)}")
109
+
110
+ # Asynchronous function to generate the program
111
+ async def generate_program(task_description: str, tools: List[Tool], model: str, max_tokens: int) -> str:
112
+ """
113
+ Asynchronously generate a Python program that solves a given task using a list of tools.
114
+
115
+ Args:
116
+ task_description (str): A description of the task to be solved.
117
+ tools (List[Tool]): A list of Tool objects available for use.
118
+ model (str): The litellm model to use for code generation.
119
+ max_tokens (int): Maximum number of tokens for the generated response.
120
+
121
+ Returns:
122
+ str: A string containing a complete Python program.
123
+ """
124
+ logger.debug(f"Generating program for task: {task_description}")
125
+ # Collect tool docstrings
126
+ tool_docstrings = "\n\n".join([tool.to_docstring() for tool in tools])
127
+
128
+ # Construct the prompt for litellm
129
+ prompt = f"""
130
+ You are a Python code generator. Your task is to create a Python program that solves the following task:
131
+ "{task_description}"
132
+
133
+ You have access to the following pre-defined async tool functions, as defined with their signatures and descriptions:
134
+
135
+ {tool_docstrings}
136
+
137
+ Instructions:
138
+ 1. Generate a Python program as a single string.
139
+ 2. Include only the import for asyncio (import asyncio).
140
+ 3. Define an async function named main() that solves the task.
141
+ 4. Use the pre-defined tool functions (e.g., add_tool, multiply_tool, concat_tool) directly by calling them with await and the appropriate arguments as specified in their descriptions.
142
+ 5. Do not redefine the tool functions within the program; assume they are already available in the namespace.
143
+ 6. Return the program as a string enclosed in triple quotes (\"\"\"program\"\"\")).
144
+ 7. Do not include asyncio.run(main()) or any code outside the main() function definition.
145
+ 8. Do not include explanatory text outside the program string.
146
+ 9. Express all string variables as multiline strings (\"\"\"\nstring\n\"\"\"), always start a string at the beginning of a line.
147
+ 10. Always print the result at the end of the program.
148
+ 11. Never call the main() function.
149
+ 12. Never use not defined variables, modules, or functions.
150
+
151
+ Example task: "Add 5 and 7 and print the result"
152
+ Example output:
153
+ \"\"\"import asyncio
154
+ async def main():
155
+ result = await add_tool(a=5, b=7)
156
+ print(result)
157
+ \"\"\"
158
+ """
159
+
160
+ logger.debug(f"Prompt sent to litellm:\n{prompt}")
161
+
162
+ try:
163
+ # Call litellm asynchronously to generate the program
164
+ logger.debug(f"Calling litellm with model {model}")
165
+ response = await litellm.acompletion(
166
+ model=model,
167
+ messages=[
168
+ {"role": "system", "content": "You are a Python code generator."},
169
+ {"role": "user", "content": prompt}
170
+ ],
171
+ max_tokens=max_tokens,
172
+ temperature=0.3
173
+ )
174
+ generated_code = response.choices[0].message.content.strip()
175
+ logger.debug("Code generation successful")
176
+ except Exception as e:
177
+ logger.error(f"Failed to generate code: {str(e)}")
178
+ raise typer.BadParameter(f"Failed to generate code with model '{model}': {str(e)}")
179
+
180
+ # Clean up the output
181
+ if generated_code.startswith('"""') and generated_code.endswith('"""'):
182
+ generated_code = generated_code[3:-3]
183
+ elif generated_code.startswith("```python") and generated_code.endswith("```"):
184
+ generated_code = generated_code[9:-3].strip()
185
+
186
+ return generated_code
187
+
188
+ # Async core logic for generate
189
+ async def generate_core(task: str, model: str, max_tokens: int):
190
+ logger.info(f"Starting generate command for task: {task}")
191
+ # Input validation
192
+ if not task.strip():
193
+ logger.error("Task description is empty")
194
+ raise typer.BadParameter("Task description cannot be empty")
195
+ if max_tokens <= 0:
196
+ logger.error("max-tokens must be positive")
197
+ raise typer.BadParameter("max-tokens must be a positive integer")
198
+
199
+ # Initialize tools
200
+ tools = [
201
+ AddTool(),
202
+ MultiplyTool(),
203
+ ConcatTool(),
204
+ AgentTool(model=model) # Include AgentTool with the specified model
205
+ ]
206
+
207
+ # Generate the program
208
+ try:
209
+ program = await generate_program(task, tools, model, max_tokens)
210
+ except Exception as e:
211
+ logger.error(f"Failed to generate program: {str(e)}")
212
+ typer.echo(typer.style(f"Error: {str(e)}", fg=typer.colors.RED))
213
+ raise typer.Exit(code=1)
214
+
215
+ logger.debug(f"Generated program:\n{program}")
216
+ # Output the generated program
217
+ typer.echo(typer.style("Generated Python Program:", fg=typer.colors.GREEN, bold=True))
218
+ typer.echo(program)
219
+
220
+ # Attempt to execute the program using the safe interpreter
221
+ typer.echo("\n" + typer.style("Executing the program safely:", fg=typer.colors.GREEN, bold=True))
222
+ try:
223
+ # Create instances of tools
224
+ add_tool_instance = AddTool()
225
+ multiply_tool_instance = MultiplyTool()
226
+ concat_tool_instance = ConcatTool()
227
+ agent_tool_instance = AgentTool(model=model)
228
+
229
+ # Define the allowed modules
230
+ allowed_modules = ["asyncio"]
231
+
232
+ # Create a namespace with our tools
233
+ namespace = {
234
+ "add_tool": add_tool_instance.async_execute,
235
+ "multiply_tool": multiply_tool_instance.async_execute,
236
+ "concat_tool": concat_tool_instance.async_execute,
237
+ "agent_tool": agent_tool_instance.async_execute,
238
+ }
239
+
240
+ # Parse the program to AST
241
+ program_ast = ast.parse(program)
242
+
243
+ # Extract imports first to ensure they're allowed
244
+ for node in program_ast.body:
245
+ if isinstance(node, ast.Import):
246
+ for name in node.names:
247
+ if name.name not in allowed_modules:
248
+ raise ValueError(f"Import not allowed: {name.name}")
249
+ elif isinstance(node, ast.ImportFrom):
250
+ if node.module not in allowed_modules:
251
+ raise ValueError(f"Import from not allowed: {node.module}")
252
+
253
+ # Find the main function in the AST
254
+ main_func_node = None
255
+ for node in program_ast.body:
256
+ if isinstance(node, ast.AsyncFunctionDef) and node.name == "main":
257
+ main_func_node = node
258
+ break
259
+
260
+ if not main_func_node:
261
+ logger.warning("No async main() function found in generated code")
262
+ typer.echo(typer.style("Warning: No async main() function found", fg=typer.colors.YELLOW))
263
+ return
264
+
265
+ # Execute the code safely to define functions in the namespace
266
+ # Use a custom ASTInterpreter directly instead of calling interpret_code
267
+ interpreter = ASTInterpreter(allowed_modules=allowed_modules, source=program)
268
+
269
+ # Add tools to the interpreter's environment
270
+ interpreter.env_stack[0].update({
271
+ "add_tool": add_tool_instance.async_execute,
272
+ "multiply_tool": multiply_tool_instance.async_execute,
273
+ "concat_tool": concat_tool_instance.async_execute,
274
+ "agent_tool": agent_tool_instance.async_execute,
275
+ })
276
+
277
+ # First, execute all non-main code (imports and other top-level code)
278
+ for node in program_ast.body:
279
+ if not isinstance(node, ast.AsyncFunctionDef) or node.name != "main":
280
+ await interpreter.visit(node, wrap_exceptions=True)
281
+
282
+ # Then execute the main function definition
283
+ for node in program_ast.body:
284
+ if isinstance(node, ast.AsyncFunctionDef) and node.name == "main":
285
+ await interpreter.visit(node, wrap_exceptions=True)
286
+
287
+ # Get the main function from the environment
288
+ main_func = interpreter.get_variable("main")
289
+
290
+ # Execute the main function in the current event loop
291
+ logger.debug("Executing main() function")
292
+ await main_func()
293
+
294
+ logger.info("Program executed successfully")
295
+ typer.echo(typer.style("Execution completed successfully", fg=typer.colors.GREEN))
296
+
297
+ except SyntaxError as e:
298
+ logger.error(f"Syntax error in generated code: {e}")
299
+ typer.echo(typer.style(f"Syntax error: {e}", fg=typer.colors.RED))
300
+ except Exception as e:
301
+ logger.error(f"Execution error: {e}")
302
+ typer.echo(typer.style(f"Execution failed: {e}", fg=typer.colors.RED))
303
+
304
+ # Synchronous callback to invoke async generate_core
305
+ @app.callback(invoke_without_command=True)
306
+ def generate(
307
+ task: str = typer.Argument(
308
+ ...,
309
+ help="The task description to generate a program for (e.g., 'Add 5 and 7 and print the result')"
310
+ ),
311
+ model: str = typer.Option(
312
+ "gemini/gemini-2.0-flash",
313
+ "--model",
314
+ "-m",
315
+ help="The litellm model to use for generation (e.g., 'gpt-3.5-turbo', 'gpt-4')"
316
+ ),
317
+ max_tokens: int = typer.Option(
318
+ 4000,
319
+ "--max-tokens",
320
+ "-t",
321
+ help="Maximum number of tokens for the generated response (default: 4000)"
322
+ )
323
+ ):
324
+ """
325
+ Asynchronously generate a Python program based on a task description using specified tools and model.
326
+ Executes the program in a controlled, safe environment.
327
+
328
+ Examples:
329
+ $ python action_gen_safe.py "Add 5 and 7 and print the result"
330
+ $ python action_gen_safe.py "Concatenate 'Hello' and 'World' and print it" --model gpt-4 --max-tokens 5000
331
+ """
332
+ asyncio.run(generate_core(task, model, max_tokens))
333
+
334
+ # Entry point to start the app
335
+ def main():
336
+ logger.debug("Starting script execution")
337
+ app()
338
+
339
+ if __name__ == "__main__":
340
+ main()
@@ -10,10 +10,10 @@ from quantalogic.tools.tool import Tool, ToolArgument
10
10
 
11
11
 
12
12
  class WriteFileTool(Tool):
13
- """Tool for writing a text file in /tmp directory."""
13
+ """Tool for writing a text file to a specified path."""
14
14
 
15
15
  name: str = "write_file_tool"
16
- description: str = "Writes a file with the given content in /tmp directory. The tool will fail if the file already exists when not used in append mode."
16
+ description: str = "Writes a file to the specified path. The tool will fail if the file already exists when not used in append mode."
17
17
  need_validation: bool = True
18
18
 
19
19
  disable_ensure_tmp_path: bool = Field(default=False)
@@ -25,7 +25,7 @@ class WriteFileTool(Tool):
25
25
  arg_type="string",
26
26
  description="The path to the file to write. By default, paths will be forced to /tmp directory unless disable_ensure_tmp_path is enabled. Can include subdirectories.",
27
27
  required=True,
28
- example="/tmp/myfile.txt or myfile.txt",
28
+ example="/tmp/myfile.txt or ./myfile.txt",
29
29
  ),
30
30
  ToolArgument(
31
31
  name="content",
@@ -107,11 +107,8 @@ class WriteFileTool(Tool):
107
107
  # Ensure path is in /tmp and normalize it
108
108
  if not self.disable_ensure_tmp_path:
109
109
  file_path = self._ensure_tmp_path(file_path)
110
-
111
- # Ensure parent directory exists (only within /tmp)
112
- parent_dir = os.path.dirname(file_path)
113
- if parent_dir.startswith("/tmp/"):
114
- os.makedirs(parent_dir, exist_ok=True)
110
+ if not file_path.startswith('/tmp/'):
111
+ raise ValueError('File path must be under /tmp when disable_ensure_tmp_path is False')
115
112
 
116
113
  # Determine file write mode based on append_mode
117
114
  mode = "a" if append_mode_bool else "w"
@@ -122,6 +119,8 @@ class WriteFileTool(Tool):
122
119
  f"File {file_path} already exists. Set append_mode=True to append or overwrite=True to overwrite."
123
120
  )
124
121
 
122
+ os.makedirs(os.path.dirname(file_path), exist_ok=True)
123
+
125
124
  with open(file_path, mode, encoding="utf-8") as f:
126
125
  f.write(content)
127
126
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: quantalogic
3
- Version: 0.61.2
3
+ Version: 0.61.3
4
4
  Summary: QuantaLogic ReAct Agents
5
5
  Author: Raphaël MANSUY
6
6
  Author-email: raphael.mansuy@gmail.com
@@ -34,6 +34,7 @@ Requires-Dist: psutil (>=7.0.0,<8.0.0)
34
34
  Requires-Dist: pydantic (>=2.10.4,<3.0.0)
35
35
  Requires-Dist: pytest-asyncio (>=0.25.3,<0.26.0)
36
36
  Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
37
+ Requires-Dist: quantalogic-pythonbox (>=0.8.3,<0.9.0)
37
38
  Requires-Dist: requests (>=2.32.3,<3.0.0)
38
39
  Requires-Dist: rich (>=13.9.4,<14.0.0)
39
40
  Requires-Dist: serpapi (>=0.1.5,<0.2.0)
@@ -3,8 +3,8 @@ quantalogic/agent.py,sha256=VChZXFLEsIIrBtXVQZ-FGZ72GCUXDL3g9br8Vo1t5V8,75072
3
3
  quantalogic/agent_config.py,sha256=HNXHmu3BiT4q9i-L2wJKUh6PuiZ_ryTNaB7ga24kaJo,8334
4
4
  quantalogic/agent_factory.py,sha256=soActorasOqs5g6NmlyeEjRYbJceIGGg7wuUS-vA898,6878
5
5
  quantalogic/codeact/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- quantalogic/codeact/agent.py,sha256=oajib7OsfBp230sg_IjmknqYsFz_lSURC39fj5brsP4,22454
7
- quantalogic/codeact/cli.py,sha256=FBqnqPUobq6ySuTL9oTLyXSenHC6V7wsCWAbNDK_xec,10810
6
+ quantalogic/codeact/agent.py,sha256=lF5Cq9e_XrBrGwhEZoNNbV-FxAMM2S12_u2og5W4994,22445
7
+ quantalogic/codeact/cli.py,sha256=Nxo5Biv08yvA72c860ikKI6Ebp0vlygoVrIMCpYsSxA,10867
8
8
  quantalogic/codeact/constants.py,sha256=_xJ2QwP5wUE9vwSDm0JTSMC4GVjXAaxHTwcZBjDCoKk,244
9
9
  quantalogic/codeact/events.py,sha256=4itxGsz54Te9vim13JC8QhacnLQ8cqtOOIyOWUrKFi0,1530
10
10
  quantalogic/codeact/llm_util.py,sha256=9HqN0prWyAgdt6FYtdzjWoIoPpDavHQ7EIIfJr2Fi8w,2749
@@ -12,7 +12,7 @@ quantalogic/codeact/prompts/error_format.j2,sha256=_8fNWxDr3U1Fq8SdzwRIBNsNG1WvP
12
12
  quantalogic/codeact/prompts/generate_action.j2,sha256=v0LXFxz6L2EwIPesu_mYSVqyNctjHOCIfdawHCb2rtc,1289
13
13
  quantalogic/codeact/prompts/generate_program.j2,sha256=05_1bAwIJLLBKt4o9c7sRWLHIoiHPyGl9OquieMZT5g,2048
14
14
  quantalogic/codeact/prompts/response_format.j2,sha256=TwO43dEG-3justNigpX4yyzGR1TGS3YDlpJQq8Z5Vf4,355
15
- quantalogic/codeact/tools_manager.py,sha256=l2cpHLuQP-MW0uP8d2e6IdMToeveaIOdilwDGPjPciY,4464
15
+ quantalogic/codeact/tools_manager.py,sha256=Q_BGtqN3E8QGbobtaYMpQXqqTv_eb5HmIoZelleUMeI,4491
16
16
  quantalogic/codeact/utils.py,sha256=B7Xowk4id6Sgoyl0eBJsi6JiKa-BAwnj0a5z70vrV1M,5308
17
17
  quantalogic/coding_agent.py,sha256=WFfabRwwPZFV3Pw3seLKpSrFE9Li4pz8Z8mCdsUIDi8,5532
18
18
  quantalogic/config.py,sha256=bmPI2rvQ9M8Zdl1H7K588JgJdnkxSE0L5_i5aBqsagA,564
@@ -57,25 +57,6 @@ quantalogic/prompts/task_summary_prompt.j2,sha256=fcjV96nqi6jsfR8l6uyep20rCaOi-z
57
57
  quantalogic/prompts/tools_prompt.j2,sha256=ZjvTAZtAk-FmzDb1QuyDJg1hzo-FqayQ7wN_ytHAi2s,385
58
58
  quantalogic/prompts/variables_prompt.j2,sha256=N3OsMbzy3PfEUqYZVF_34wVidhh2ghVToSgdRCv2qgM,231
59
59
  quantalogic/prompts.py,sha256=zqmyosq4hdYpzHI-FG0m5CKV2wQnjgyMJf6Vo1ZaLW4,1982
60
- quantalogic/python_interpreter/__init__.py,sha256=cNYxH9jy5QEAPZo44zmJzy3Kci14e0lL2h20Cbrs7RQ,675
61
- quantalogic/python_interpreter/assignment_visitors.py,sha256=t6ttdYuwjcz8VtqJS5vMaaUUG6GbEhoNmnNDUj8dZiI,2771
62
- quantalogic/python_interpreter/base_visitors.py,sha256=aD1pc3DbdlPdjQ3yJTYhBqNpN6tant7NOHXShts8coo,765
63
- quantalogic/python_interpreter/class_visitors.py,sha256=FUgBVg_TmpAdLdLlYlPnruSm8iViijW3gTBQveJjkpI,862
64
- quantalogic/python_interpreter/comprehension_visitors.py,sha256=AdPn047QUeoG5D1VD4wU2JuXS2KBTwLOIGbApmx9s7A,8468
65
- quantalogic/python_interpreter/context_visitors.py,sha256=_4-Xfk0wzsla2rSIJZYEyYK4yIK7yRZWX6-HNBFSWTs,2137
66
- quantalogic/python_interpreter/control_flow_visitors.py,sha256=trJ0Led1pPWKyseQF7BbIF5LFopkOiRDSVHz9Hi7-hI,3662
67
- quantalogic/python_interpreter/exception_visitors.py,sha256=dbRuk2CaEZN-tYrzcRxZQh_UdLfpcW7wwrG8iQoGeE8,4571
68
- quantalogic/python_interpreter/exceptions.py,sha256=mL4G9a0PZrvW_hAjkweMohvN8ryZsZOiIOolZUDSq9Y,1296
69
- quantalogic/python_interpreter/execution.py,sha256=bggzv-GMnFe0Pkt00T4IlPUnDMsuqcHO1Qo0Wq6rEbs,8420
70
- quantalogic/python_interpreter/function_utils.py,sha256=1LCEqCDt0p0uBTeC6rcre84r_j06jc6HuWLZ5Fc8m8M,18133
71
- quantalogic/python_interpreter/function_visitors.py,sha256=WcGBWXZ_A7vekkrejL2XTuVPyqsKymK4svJr6-kR-OI,11317
72
- quantalogic/python_interpreter/import_visitors.py,sha256=AgqFM8SO1wuJluDRsDGBwm-FQ5K374pQrXgbXXEBgPI,1331
73
- quantalogic/python_interpreter/interpreter_core.py,sha256=PV_XFx0xhUG9ACvhz3FGat2FSaXK3C031HjRizHbox0,16646
74
- quantalogic/python_interpreter/literal_visitors.py,sha256=TX9-02rn093OFTDvKkhp0PbTsXqIU1voBfbxs-TXfu0,3715
75
- quantalogic/python_interpreter/misc_visitors.py,sha256=_olOaGD04icqKDqeEFUPIY5Nq0cH5pdGkq-8T6xbilM,6758
76
- quantalogic/python_interpreter/operator_visitors.py,sha256=WTf4EfwE88lMoTKjuL8UBNpbwJ8Z6DTdrwvVffVw-Ic,4048
77
- quantalogic/python_interpreter/scope.py,sha256=HSSbtDAMeDbE5LjSckTHvFlhcOUGv8UzxpI4NgUbp5U,257
78
- quantalogic/python_interpreter/visit_handlers.py,sha256=dGh7BxwGArnBnmwxVzpV6l_uXQ5J6esUm2xKtXne30M,2610
79
60
  quantalogic/quantlitellm.py,sha256=nf-awyOxP0ANoAPGHNvHfdLu8gNn65L39gl7x4saIQc,5550
80
61
  quantalogic/search_agent.py,sha256=tr0cwscJ4wu_G1aumjFyvGHQ0eQv5OL5sxj17s6Ocls,2470
81
62
  quantalogic/server/__init__.py,sha256=8sz_PYAUCrkM6JM5EAUeIzNM4NPW6j6UT72JVkc21WQ,91
@@ -91,6 +72,7 @@ quantalogic/task_runner.py,sha256=NB7TqNuwCstCAsTkjGcJSQRapNk8-iXe7d_2qf-fs1s,15
91
72
  quantalogic/tool_manager.py,sha256=vNA7aBKgdU3wpw_goom6i9rg_64pNZapNxvg4cUhhCI,6983
92
73
  quantalogic/tools/__init__.py,sha256=NU_M6VYYaAbSUtb2Qdu1lsYaDh0G3f_8jnrZTsBD0eY,2390
93
74
  quantalogic/tools/action_gen.py,sha256=M16voPq7tMR1qiCKTaZAusmVchFbBIluuBbLQH9kEG4,15580
75
+ quantalogic/tools/action_gen_safe.py,sha256=7doUtPnV9oxBc02N_N-xcMxmfT9Nd6gHfkNU7MtwVUk,13996
94
76
  quantalogic/tools/agent_tool.py,sha256=MXCXxWHRch7VK4UWhtRP1jeI8Np9Ne2CUGo8vm1oZiM,3064
95
77
  quantalogic/tools/composio/__init__.py,sha256=Yo9ygNx0qQILVhIfRgqpO8fgnCgp5WoZMd3Hm5D20GY,429
96
78
  quantalogic/tools/composio/composio.py,sha256=icVHA_Scr1pViBhahGGBGBRBl9JSB3hGSqpgQzAIUH8,17627
@@ -188,7 +170,7 @@ quantalogic/tools/utils/generate_database_report.py,sha256=IU_XGTDNXfJXxzpHR1F4c
188
170
  quantalogic/tools/web_navigation/__init__.py,sha256=O7SkVqbGwN4zt8Sm3H8AHF9451FSgI5h0J3fDj1rFS4,142
189
171
  quantalogic/tools/web_navigation/web_tool.py,sha256=AxAxQLUNwCElqxP2ceOOjHVY80ck-Md-uNsjHdR9ErA,4721
190
172
  quantalogic/tools/wikipedia_search_tool.py,sha256=LXQSPH8961Efw2QNxKe-cD5ZiIYD3ufEgrxH4y5uB74,5180
191
- quantalogic/tools/write_file_tool.py,sha256=aevO9koB1Gz96PJCu-hQuW014qTzrnoSpcMc6eke5mI,5583
173
+ quantalogic/tools/write_file_tool.py,sha256=cOheTr2ZTWNbPHlB1PRhc1btJ2mW8TvE5PLQFIdbUb8,5578
192
174
  quantalogic/utils/__init__.py,sha256=E442CJQuTohKzgI0Wrd4NZEpKascFjz6F4Vy8Y1c_0Y,634
193
175
  quantalogic/utils/ask_user_validation.py,sha256=kSr7TXPTpsLR9zgwpGWgvffx8-cKAC_rdFRdLqwC22A,1176
194
176
  quantalogic/utils/async_utils.py,sha256=FOizWRbHdsZwoD36dNErzunfwPlE7zDprS6RXcWuWSo,963
@@ -209,8 +191,8 @@ quantalogic/version_check.py,sha256=JyQFTNMDWtpHCLnN-BiakzB2cyXf6kUFsTjvmSruZi4,
209
191
  quantalogic/welcome_message.py,sha256=o4tHdgabNuIV9kbIDPgS3_2yzJhayK30oKad2UouYDc,3020
210
192
  quantalogic/xml_parser.py,sha256=bLLwIwO-VEHWF3heNS7nuPC8wgdYw9F_fVZZNW1figY,11728
211
193
  quantalogic/xml_tool_parser.py,sha256=hGHA1q20JUoTNTbZYmi4FTdA5I25-AGEIP8DwZgQCNA,3897
212
- quantalogic-0.61.2.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
213
- quantalogic-0.61.2.dist-info/METADATA,sha256=38TzrZH-h5sMLtIgRGcMlOdTBXtGh-tSndq7AeFmJxA,32975
214
- quantalogic-0.61.2.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
215
- quantalogic-0.61.2.dist-info/entry_points.txt,sha256=h74O_Q3qBRCrDR99qvwB4BpBGzASPUIjCfxHq6Qnups,183
216
- quantalogic-0.61.2.dist-info/RECORD,,
194
+ quantalogic-0.61.3.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
195
+ quantalogic-0.61.3.dist-info/METADATA,sha256=6VPi8J-lWUhZsEKVuCKFuJaWChJ2bHRO6qCQN47I_Cw,33029
196
+ quantalogic-0.61.3.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
197
+ quantalogic-0.61.3.dist-info/entry_points.txt,sha256=h74O_Q3qBRCrDR99qvwB4BpBGzASPUIjCfxHq6Qnups,183
198
+ quantalogic-0.61.3.dist-info/RECORD,,
@@ -1,23 +0,0 @@
1
- # quantalogic/utils/__init__.py
2
- from .exceptions import BreakException, ContinueException, ReturnException, WrappedException, has_await
3
- from .execution import AsyncExecutionResult, execute_async, interpret_ast, interpret_code
4
- from .function_utils import AsyncFunction, Function, LambdaFunction
5
- from .interpreter_core import ASTInterpreter
6
- from .scope import Scope
7
-
8
- __all__ = [
9
- 'ASTInterpreter',
10
- 'execute_async',
11
- 'interpret_ast',
12
- 'interpret_code',
13
- 'AsyncExecutionResult',
14
- 'ReturnException',
15
- 'BreakException',
16
- 'ContinueException',
17
- 'WrappedException',
18
- 'has_await',
19
- 'Function',
20
- 'AsyncFunction',
21
- 'LambdaFunction',
22
- 'Scope',
23
- ]
@@ -1,63 +0,0 @@
1
- import ast
2
- from typing import Any
3
-
4
- from .interpreter_core import ASTInterpreter
5
-
6
- async def visit_Assign(self: ASTInterpreter, node: ast.Assign, wrap_exceptions: bool = True) -> None:
7
- value: Any = await self.visit(node.value, wrap_exceptions=wrap_exceptions)
8
- for target in node.targets:
9
- if isinstance(target, ast.Subscript):
10
- obj = await self.visit(target.value, wrap_exceptions=wrap_exceptions)
11
- key = await self.visit(target.slice, wrap_exceptions=wrap_exceptions)
12
- obj[key] = value
13
- else:
14
- await self.assign(target, value)
15
-
16
- async def visit_AugAssign(self: ASTInterpreter, node: ast.AugAssign, wrap_exceptions: bool = True) -> Any:
17
- if isinstance(node.target, ast.Name):
18
- current_val: Any = self.get_variable(node.target.id)
19
- else:
20
- current_val: Any = await self.visit(node.target, wrap_exceptions=wrap_exceptions)
21
- right_val: Any = await self.visit(node.value, wrap_exceptions=wrap_exceptions)
22
- op = node.op
23
- if isinstance(op, ast.Add):
24
- result: Any = current_val + right_val
25
- elif isinstance(op, ast.Sub):
26
- result = current_val - right_val
27
- elif isinstance(op, ast.Mult):
28
- result = current_val * right_val
29
- elif isinstance(op, ast.Div):
30
- result = current_val / right_val
31
- elif isinstance(op, ast.FloorDiv):
32
- result = current_val // right_val
33
- elif isinstance(op, ast.Mod):
34
- result = current_val % right_val
35
- elif isinstance(op, ast.Pow):
36
- result = current_val**right_val
37
- elif isinstance(op, ast.BitAnd):
38
- result = current_val & right_val
39
- elif isinstance(op, ast.BitOr):
40
- result = current_val | right_val
41
- elif isinstance(op, ast.BitXor):
42
- result = current_val ^ right_val
43
- elif isinstance(op, ast.LShift):
44
- result = current_val << right_val
45
- elif isinstance(op, ast.RShift):
46
- result = current_val >> right_val
47
- else:
48
- raise Exception("Unsupported augmented operator: " + str(op))
49
- await self.assign(node.target, result)
50
- return result
51
-
52
- async def visit_AnnAssign(self: ASTInterpreter, node: ast.AnnAssign, wrap_exceptions: bool = True) -> None:
53
- value = await self.visit(node.value, wrap_exceptions=wrap_exceptions) if node.value else None
54
- annotation = await self.visit(node.annotation, wrap_exceptions=True)
55
- if isinstance(node.target, ast.Name):
56
- self.type_hints[node.target.id] = annotation
57
- if value is not None or node.simple:
58
- await self.assign(node.target, value)
59
-
60
- async def visit_NamedExpr(self: ASTInterpreter, node: ast.NamedExpr, wrap_exceptions: bool = True) -> Any:
61
- value = await self.visit(node.value, wrap_exceptions=wrap_exceptions)
62
- await self.assign(node.target, value)
63
- return value
@@ -1,20 +0,0 @@
1
- import ast
2
- from typing import Any
3
-
4
- from .exceptions import WrappedException
5
- from .interpreter_core import ASTInterpreter
6
-
7
- async def visit_Module(self: ASTInterpreter, node: ast.Module, wrap_exceptions: bool = True) -> Any:
8
- last_value = None
9
- for stmt in node.body:
10
- last_value = await self.visit(stmt, wrap_exceptions=True)
11
- return last_value
12
-
13
- async def visit_Expr(self: ASTInterpreter, node: ast.Expr, wrap_exceptions: bool = True) -> Any:
14
- return await self.visit(node.value, wrap_exceptions=wrap_exceptions)
15
-
16
- async def visit_Pass(self: ASTInterpreter, node: ast.Pass, wrap_exceptions: bool = True) -> None:
17
- return None
18
-
19
- async def visit_TypeIgnore(self: ASTInterpreter, node: ast.TypeIgnore, wrap_exceptions: bool = True) -> None:
20
- pass
@@ -1,22 +0,0 @@
1
- import ast
2
- from typing import Any, Dict, List
3
-
4
- from .function_utils import Function
5
- from .interpreter_core import ASTInterpreter
6
-
7
- async def visit_ClassDef(self: ASTInterpreter, node: ast.ClassDef, wrap_exceptions: bool = True) -> Any:
8
- base_frame = {}
9
- self.env_stack.append(base_frame)
10
- bases = [await self.visit(base, wrap_exceptions=True) for base in node.bases]
11
- try:
12
- for stmt in node.body:
13
- await self.visit(stmt, wrap_exceptions=True)
14
- class_dict = {k: v for k, v in self.env_stack[-1].items() if k not in ["__builtins__"]}
15
- cls = type(node.name, tuple(bases), class_dict)
16
- for name, value in class_dict.items():
17
- if isinstance(value, Function):
18
- value.defining_class = cls
19
- self.env_stack[-2][node.name] = cls
20
- return cls
21
- finally:
22
- self.env_stack.pop()