tinyagent-py 0.0.12__py3-none-any.whl → 0.0.13__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.
- tinyagent/code_agent/providers/base.py +60 -5
- tinyagent/code_agent/providers/modal_provider.py +16 -5
- tinyagent/code_agent/tiny_code_agent.py +14 -0
- tinyagent/code_agent/utils.py +32 -8
- tinyagent/hooks/gradio_callback.py +3 -2
- {tinyagent_py-0.0.12.dist-info → tinyagent_py-0.0.13.dist-info}/METADATA +11 -1
- {tinyagent_py-0.0.12.dist-info → tinyagent_py-0.0.13.dist-info}/RECORD +10 -10
- {tinyagent_py-0.0.12.dist-info → tinyagent_py-0.0.13.dist-info}/WHEEL +0 -0
- {tinyagent_py-0.0.12.dist-info → tinyagent_py-0.0.13.dist-info}/licenses/LICENSE +0 -0
- {tinyagent_py-0.0.12.dist-info → tinyagent_py-0.0.13.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,7 @@
|
|
1
1
|
from abc import ABC, abstractmethod
|
2
2
|
from typing import Dict, List, Any, Optional
|
3
3
|
from tinyagent.hooks.logging_manager import LoggingManager
|
4
|
+
import cloudpickle
|
4
5
|
|
5
6
|
|
6
7
|
class CodeExecutionProvider(ABC):
|
@@ -69,8 +70,6 @@ class CodeExecutionProvider(ABC):
|
|
69
70
|
Args:
|
70
71
|
tools: List of tool objects to add
|
71
72
|
"""
|
72
|
-
import cloudpickle
|
73
|
-
|
74
73
|
tools_str_list = ["import cloudpickle"]
|
75
74
|
tools_str_list.append("###########<tools>###########\n")
|
76
75
|
for tool in tools:
|
@@ -82,6 +81,22 @@ class CodeExecutionProvider(ABC):
|
|
82
81
|
tools_str_list.append("\n\n")
|
83
82
|
self.code_tools_definitions.extend(tools_str_list)
|
84
83
|
|
84
|
+
def set_code_tools(self, tools: List[Any]) -> None:
|
85
|
+
"""
|
86
|
+
Set the code tools available in the execution environment.
|
87
|
+
Replaces any existing tools with the new list.
|
88
|
+
|
89
|
+
Args:
|
90
|
+
tools: List of tool objects to set
|
91
|
+
"""
|
92
|
+
# Clear existing tools
|
93
|
+
self.code_tools = tools.copy()
|
94
|
+
self.code_tools_definitions = []
|
95
|
+
|
96
|
+
# Add the new tools
|
97
|
+
if tools:
|
98
|
+
self.add_tools(tools)
|
99
|
+
|
85
100
|
def set_user_variables(self, variables: Dict[str, Any]) -> None:
|
86
101
|
"""
|
87
102
|
Set user variables that will be available in the Python environment.
|
@@ -89,8 +104,6 @@ class CodeExecutionProvider(ABC):
|
|
89
104
|
Args:
|
90
105
|
variables: Dictionary of variable name -> value pairs
|
91
106
|
"""
|
92
|
-
import cloudpickle
|
93
|
-
|
94
107
|
self._user_variables = variables.copy()
|
95
108
|
|
96
109
|
# Add variables to the execution environment by serializing them
|
@@ -149,4 +162,46 @@ class CodeExecutionProvider(ABC):
|
|
149
162
|
Returns:
|
150
163
|
Dictionary of current user variables
|
151
164
|
"""
|
152
|
-
return self._user_variables.copy()
|
165
|
+
return self._user_variables.copy()
|
166
|
+
|
167
|
+
def update_user_variables_from_globals(self, globals_dict: Dict[str, Any]) -> None:
|
168
|
+
"""
|
169
|
+
Extract and update user variables from the globals dictionary after code execution.
|
170
|
+
This ensures that any modifications to user variables during code execution are preserved.
|
171
|
+
|
172
|
+
Args:
|
173
|
+
globals_dict: The globals dictionary after code execution
|
174
|
+
"""
|
175
|
+
if not globals_dict or not self._user_variables:
|
176
|
+
return
|
177
|
+
|
178
|
+
# Update user variables with values from globals
|
179
|
+
for var_name in list(self._user_variables.keys()):
|
180
|
+
if var_name in globals_dict:
|
181
|
+
try:
|
182
|
+
# Try to serialize the value to ensure it's valid
|
183
|
+
cloudpickle.dumps(globals_dict[var_name])
|
184
|
+
# Update the user variable with the new value
|
185
|
+
self._user_variables[var_name] = globals_dict[var_name]
|
186
|
+
except Exception:
|
187
|
+
# If serialization fails, keep the old value
|
188
|
+
pass
|
189
|
+
|
190
|
+
# Check for new variables that might have been created
|
191
|
+
# This handles cases where LLM creates new variables that should be preserved
|
192
|
+
for var_name, var_value in globals_dict.items():
|
193
|
+
# Skip special variables, modules, and functions
|
194
|
+
if (var_name.startswith('__') or
|
195
|
+
var_name in ['builtins', 'cloudpickle'] or
|
196
|
+
callable(var_value) or
|
197
|
+
var_name in self._user_variables):
|
198
|
+
continue
|
199
|
+
|
200
|
+
try:
|
201
|
+
# Try to serialize the value to ensure it's valid
|
202
|
+
cloudpickle.dumps(var_value)
|
203
|
+
# Add the new variable to user variables
|
204
|
+
self._user_variables[var_name] = var_value
|
205
|
+
except Exception:
|
206
|
+
# If serialization fails, skip this variable
|
207
|
+
pass
|
@@ -143,17 +143,28 @@ class ModalProvider(CodeExecutionProvider):
|
|
143
143
|
print(full_code)
|
144
144
|
print("#" * 100)
|
145
145
|
|
146
|
-
|
147
146
|
|
148
147
|
# Use Modal's native execution methods
|
149
148
|
response = self._python_executor(full_code, self._globals_dict, self._locals_dict)
|
150
149
|
|
151
150
|
print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!<response>!!!!!!!!!!!!!!!!!!!!!!!!!")
|
152
151
|
|
153
|
-
#
|
154
|
-
|
155
|
-
|
156
|
-
|
152
|
+
# Always update globals and locals dictionaries, regardless of whether there was an error
|
153
|
+
# This ensures variables are preserved even when code execution fails
|
154
|
+
try:
|
155
|
+
# Update globals and locals from the response
|
156
|
+
if "updated_globals" in response:
|
157
|
+
self._globals_dict = cloudpickle.loads(make_session_blob(response["updated_globals"]))
|
158
|
+
|
159
|
+
if "updated_locals" in response:
|
160
|
+
self._locals_dict = cloudpickle.loads(make_session_blob(response["updated_locals"]))
|
161
|
+
|
162
|
+
# Update user variables from the updated globals and locals
|
163
|
+
# This preserves any changes made to variables by the LLM
|
164
|
+
self.update_user_variables_from_globals(self._globals_dict)
|
165
|
+
self.update_user_variables_from_globals(self._locals_dict)
|
166
|
+
except Exception as e:
|
167
|
+
print(f"Warning: Failed to update globals/locals after execution: {str(e)}")
|
157
168
|
|
158
169
|
self._log_response(response)
|
159
170
|
|
@@ -261,7 +261,16 @@ class TinyCodeAgent:
|
|
261
261
|
async def run_python(code_lines: List[str], timeout: int = 120) -> str:
|
262
262
|
"""Execute Python code using the configured provider."""
|
263
263
|
try:
|
264
|
+
# Before execution, ensure provider has the latest user variables
|
265
|
+
if self.user_variables:
|
266
|
+
self.code_provider.set_user_variables(self.user_variables)
|
267
|
+
|
264
268
|
result = await self.code_provider.execute_python(code_lines, timeout)
|
269
|
+
|
270
|
+
# After execution, update TinyCodeAgent's user_variables from the provider
|
271
|
+
# This ensures they stay in sync
|
272
|
+
self.user_variables = self.code_provider.get_user_variables()
|
273
|
+
|
265
274
|
return str(result)
|
266
275
|
except Exception as e:
|
267
276
|
print("!"*100)
|
@@ -272,6 +281,11 @@ class TinyCodeAgent:
|
|
272
281
|
print(f"{COLOR['RED']}{str(e)}{COLOR['ENDC']}")
|
273
282
|
print(f"{COLOR['RED']}{traceback.format_exc()}{COLOR['ENDC']}")
|
274
283
|
print("!"*100)
|
284
|
+
|
285
|
+
# Even after an exception, update user_variables from the provider
|
286
|
+
# This ensures any variables that were successfully created/modified are preserved
|
287
|
+
self.user_variables = self.code_provider.get_user_variables()
|
288
|
+
|
275
289
|
return f"Error executing code: {str(e)}"
|
276
290
|
|
277
291
|
self.agent.add_tool(run_python)
|
tinyagent/code_agent/utils.py
CHANGED
@@ -116,22 +116,34 @@ def _run_python(
|
|
116
116
|
#updated_globals['print'] = custom_print
|
117
117
|
|
118
118
|
# Parse the code
|
119
|
-
|
120
|
-
|
119
|
+
try:
|
120
|
+
tree = ast.parse(code, mode="exec")
|
121
|
+
compiled = compile(tree, filename="<ast>", mode="exec")
|
122
|
+
except SyntaxError as e:
|
123
|
+
# Return syntax error without executing
|
124
|
+
return {
|
125
|
+
"printed_output": "",
|
126
|
+
"return_value": None,
|
127
|
+
"stderr": "",
|
128
|
+
"error_traceback": f"Syntax error: {str(e)}",
|
129
|
+
"updated_globals": updated_globals,
|
130
|
+
"updated_locals": updated_locals
|
131
|
+
}
|
132
|
+
|
121
133
|
stdout_buf = io.StringIO()
|
122
134
|
stderr_buf = io.StringIO()
|
123
135
|
# Execute with exception handling
|
124
136
|
error_traceback = None
|
125
137
|
output = None
|
126
138
|
|
139
|
+
# Merge all variables into globals to avoid scoping issues with generator expressions
|
140
|
+
# When exec() is called with both globals and locals, generator expressions can't
|
141
|
+
# access local variables. By using only globals, everything runs in global scope.
|
142
|
+
merged_globals = updated_globals.copy()
|
143
|
+
merged_globals.update(updated_locals)
|
144
|
+
|
127
145
|
with contextlib.redirect_stdout(stdout_buf), contextlib.redirect_stderr(stderr_buf):
|
128
146
|
try:
|
129
|
-
# Merge all variables into globals to avoid scoping issues with generator expressions
|
130
|
-
# When exec() is called with both globals and locals, generator expressions can't
|
131
|
-
# access local variables. By using only globals, everything runs in global scope.
|
132
|
-
merged_globals = updated_globals.copy()
|
133
|
-
merged_globals.update(updated_locals)
|
134
|
-
|
135
147
|
# Add 'exec' to authorized_functions for internal use
|
136
148
|
internal_authorized_functions = ['exec','eval']
|
137
149
|
if authorized_functions is not None and not isinstance(authorized_functions, bool):
|
@@ -152,6 +164,18 @@ def _run_python(
|
|
152
164
|
except Exception:
|
153
165
|
# Capture the full traceback as a string
|
154
166
|
error_traceback = traceback.format_exc()
|
167
|
+
|
168
|
+
# CRITICAL FIX: Even when an exception occurs, we need to update the globals and locals
|
169
|
+
# with any variables that were successfully created/modified before the exception
|
170
|
+
for key, value in merged_globals.items():
|
171
|
+
# Skip special variables and modules
|
172
|
+
if key.startswith('__') or key in ['builtins', 'traceback', 'contextlib', 'io', 'ast', 'sys']:
|
173
|
+
continue
|
174
|
+
|
175
|
+
# Update both dictionaries with the current state
|
176
|
+
if key in updated_locals or key not in updated_globals:
|
177
|
+
updated_locals[key] = value
|
178
|
+
updated_globals[key] = value
|
155
179
|
|
156
180
|
# Join all captured output
|
157
181
|
#printed_output = ''.join(output_buffer)
|
@@ -845,8 +845,9 @@ class GradioCallback:
|
|
845
845
|
|
846
846
|
# Footer
|
847
847
|
gr.Markdown(
|
848
|
-
|
849
|
-
"
|
848
|
+
"<div style='text-align: center; margin-top: 20px;'>"
|
849
|
+
"Built with ❤️ by <a href='https://github.com/askbudi/tinyagent' target='_blank'>TinyAgent</a>"
|
850
|
+
"<br>Start building your own AI agents with TinyAgent"
|
850
851
|
"</div>"
|
851
852
|
)
|
852
853
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: tinyagent-py
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.13
|
4
4
|
Summary: TinyAgent with MCP Client, Code Agent (Thinking, Planning, and Executing in Python), and Extendable Hooks, Tiny but powerful
|
5
5
|
Author-email: Mahdi Golchin <golchin@askdev.ai>
|
6
6
|
Project-URL: Homepage, https://github.com/askbudi/tinyagent
|
@@ -60,6 +60,16 @@ Inspired by:
|
|
60
60
|
## Quick Links
|
61
61
|
- [Build your own Tiny Agent](https://askdev.ai/github/askbudi/tinyagent)
|
62
62
|
|
63
|
+
|
64
|
+
## Live Projects using TinyAgent (🔥)
|
65
|
+
- [AskDev.AI](https://askdev.ai) - Understand, chat, and summarize codebase of any project on GitHub.
|
66
|
+
- [HackBuddy AI](https://huggingface.co/spaces/ask-dev/HackBuddyAI) - A Hackathon Assistant Agent, built with TinyCodeAgent and Gradio. Match invdividuals to teams based on their skills, interests and organizer preferences.
|
67
|
+
|
68
|
+
- [TinyCodeAgent Demo](https://huggingface.co/spaces/ask-dev/TinyCodeAgent) - A playground for TinyCodeAgent, built with tinyagent, Gradio and Modal.com
|
69
|
+
|
70
|
+
** Building something with TinyAgent? Let us know and I'll add it here!**
|
71
|
+
|
72
|
+
|
63
73
|
## Overview
|
64
74
|
This is a tiny agent framework that uses MCP and LiteLLM to interact with language models. You have full control over the agent, you can add any tools you like from MCP and extend the agent using its event system.
|
65
75
|
|
@@ -7,15 +7,15 @@ tinyagent/code_agent/example.py,sha256=qC6i3auUT1YwXS9WK1Ovq-9oDOUgzRxDegYdlVcVc
|
|
7
7
|
tinyagent/code_agent/helper.py,sha256=oZnpo-_H3cB12LxNN7Ztd-31EiUcuI2UpWP69xuF8oE,7205
|
8
8
|
tinyagent/code_agent/modal_sandbox.py,sha256=RcQ5a-UFyqV7xSHnttpgAQQ-mNWk-9Z0tb836ua7C0E,16381
|
9
9
|
tinyagent/code_agent/safety.py,sha256=WHad2ypzfsdKnXG6FcgXcgGiMC-H4KTmOzhP9S9i3Zw,22209
|
10
|
-
tinyagent/code_agent/tiny_code_agent.py,sha256=
|
11
|
-
tinyagent/code_agent/utils.py,sha256=
|
10
|
+
tinyagent/code_agent/tiny_code_agent.py,sha256=UrNjmJSrDC493bXGARrt0tDd1Bn56FgNlxuQSu4J614,28194
|
11
|
+
tinyagent/code_agent/utils.py,sha256=FxHnpb06S2o2-xPRHgf9zAnzbXvGWs5QApNI4DEH__U,7870
|
12
12
|
tinyagent/code_agent/providers/__init__.py,sha256=myfy9qsBDjNOhcgXJ2E9jO1q5eo6jHp43I2k0k8esLY,136
|
13
|
-
tinyagent/code_agent/providers/base.py,sha256=
|
14
|
-
tinyagent/code_agent/providers/modal_provider.py,sha256=
|
13
|
+
tinyagent/code_agent/providers/base.py,sha256=LfmahpulNbnivn5m91GTAo6ityjidq05dC3qx9EtJ80,8203
|
14
|
+
tinyagent/code_agent/providers/modal_provider.py,sha256=R0qt8XlTMFMbznMHN32pQxupDE9KR18NpQ3l1wJJP0w,10799
|
15
15
|
tinyagent/code_agent/tools/__init__.py,sha256=0XtrgYBgBayOffW50KyrlmrXXs9iu6z1DHu7-D8WGqY,94
|
16
16
|
tinyagent/code_agent/tools/example_tools.py,sha256=YbXb7PKuvvxh-LV12Y4n_Ez3RyLA95gWOcZrKsa7UHg,1203
|
17
17
|
tinyagent/hooks/__init__.py,sha256=RZow2r0XHLJ3-tnmecScdc0_wrEdmOy5dtXqoiRME5Y,254
|
18
|
-
tinyagent/hooks/gradio_callback.py,sha256=
|
18
|
+
tinyagent/hooks/gradio_callback.py,sha256=78x2x9AbYoLV-qwCxn2sH4s39DLlhNCzL7qCkVR-vy4,56911
|
19
19
|
tinyagent/hooks/logging_manager.py,sha256=UpdmpQ7HRPyer-jrmQSXcBwi409tV9LnGvXSHjTcYTI,7935
|
20
20
|
tinyagent/hooks/rich_code_ui_callback.py,sha256=PLcu5MOSoP4oZR3BtvcV9DquxcIT_d0WzSlkvaDcGOk,19820
|
21
21
|
tinyagent/hooks/rich_ui_callback.py,sha256=5iCNOiJmhc1lOL7ZjaOt5Sk3rompko4zu_pAxfTVgJQ,22897
|
@@ -26,8 +26,8 @@ tinyagent/storage/json_file_storage.py,sha256=SYD8lvTHu2-FEHm1tZmsrcgEOirBrlUsUM
|
|
26
26
|
tinyagent/storage/postgres_storage.py,sha256=IGwan8UXHNnTZFK1F8x4kvMDex3GAAGWUg9ePx_5IF4,9018
|
27
27
|
tinyagent/storage/redis_storage.py,sha256=hu3y7wHi49HkpiR-AW7cWVQuTVOUk1WaB8TEPGUKVJ8,1742
|
28
28
|
tinyagent/storage/sqlite_storage.py,sha256=ZyOYe0d_oHO1wOIT8FxKIbc67tP_0e_8FnM2Zq8Pwj8,5915
|
29
|
-
tinyagent_py-0.0.
|
30
|
-
tinyagent_py-0.0.
|
31
|
-
tinyagent_py-0.0.
|
32
|
-
tinyagent_py-0.0.
|
33
|
-
tinyagent_py-0.0.
|
29
|
+
tinyagent_py-0.0.13.dist-info/licenses/LICENSE,sha256=YIogcVQnknaaE4K-oaQylFWo8JGRBWnwmGb3fWB_Pww,1064
|
30
|
+
tinyagent_py-0.0.13.dist-info/METADATA,sha256=qr6akWcOHEZNUtQ34H_XalRWtJD-mK6WLiwmlcTH_N0,13848
|
31
|
+
tinyagent_py-0.0.13.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
32
|
+
tinyagent_py-0.0.13.dist-info/top_level.txt,sha256=Ny8aJNchZpc2Vvhp3306L5vjceJakvFxBk-UjjVeA_I,10
|
33
|
+
tinyagent_py-0.0.13.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|