tinyagent-py 0.0.8__py3-none-any.whl → 0.0.11__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/__init__.py CHANGED
@@ -1,4 +1,5 @@
1
1
  from .tiny_agent import TinyAgent,tool
2
2
  from .mcp_client import MCPClient
3
+ from .code_agent import TinyCodeAgent
3
4
 
4
- __all__ = ["TinyAgent", "MCPClient","tool"]
5
+ __all__ = ["TinyAgent", "MCPClient","tool", "TinyCodeAgent"]
@@ -0,0 +1,12 @@
1
+ from .tiny_code_agent import TinyCodeAgent
2
+ from .providers.base import CodeExecutionProvider
3
+ from .providers.modal_provider import ModalProvider
4
+ from .tools.example_tools import get_weather, get_traffic
5
+
6
+ __all__ = [
7
+ "TinyCodeAgent",
8
+ "CodeExecutionProvider",
9
+ "ModalProvider",
10
+ "get_weather",
11
+ "get_traffic"
12
+ ]
@@ -0,0 +1,176 @@
1
+ """
2
+ Example usage of TinyCodeAgent with Gradio UI.
3
+
4
+ This example demonstrates how to initialize and use TinyCodeAgent
5
+ following the guidelines from gradio_agent.md and README.md.
6
+ """
7
+
8
+ import asyncio
9
+ import os
10
+ import sys
11
+ import tempfile
12
+ import shutil
13
+ import logging
14
+ from typing import Dict, Any, Union
15
+
16
+ from tinyagent.hooks.logging_manager import LoggingManager
17
+ from tinyagent.hooks.gradio_callback import GradioCallback
18
+ from .tiny_code_agent import TinyCodeAgent
19
+ from .tools.example_tools import get_weather, get_traffic
20
+
21
+
22
+ async def run_example():
23
+ """Example usage of TinyCodeAgent with GradioCallback."""
24
+
25
+ # --- Logging Setup ---
26
+ log_manager = LoggingManager(default_level=logging.INFO)
27
+ log_manager.set_levels({
28
+ 'tinyagent.hooks.gradio_callback': logging.DEBUG,
29
+ 'tinyagent.tiny_agent': logging.DEBUG,
30
+ 'tinyagent.mcp_client': logging.DEBUG,
31
+ 'tinyagent.code_agent': logging.DEBUG,
32
+ })
33
+
34
+ console_handler = logging.StreamHandler(sys.stdout)
35
+ log_manager.configure_handler(
36
+ console_handler,
37
+ format_string='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
38
+ level=logging.DEBUG
39
+ )
40
+
41
+ ui_logger = log_manager.get_logger('tinyagent.hooks.gradio_callback')
42
+ agent_logger = log_manager.get_logger('tinyagent.code_agent')
43
+ ui_logger.info("--- Starting TinyCodeAgent Example ---")
44
+
45
+ # --- Configuration ---
46
+ model = "gpt-4.1-mini"
47
+ api_key = os.environ.get("OPENAI_API_KEY")
48
+ if not api_key:
49
+ ui_logger.error("OPENAI_API_KEY environment variable not set.")
50
+ return
51
+
52
+ # Create a temporary folder for file uploads
53
+ upload_folder = tempfile.mkdtemp(prefix="gradio_uploads_")
54
+ ui_logger.info(f"Created temporary upload folder: {upload_folder}")
55
+
56
+ # --- Modal Configuration ---
57
+ modal_secrets: Dict[str, Union[str, None]] = {
58
+ "OPENAI_API_KEY": api_key,
59
+ # Add other secrets as needed
60
+ }
61
+
62
+ provider_config = {
63
+ "modal_secrets": modal_secrets,
64
+ "pip_packages": ["tinyagent-py[all]", "requests", "cloudpickle"],
65
+ "default_python_codes": [
66
+ "import random",
67
+ "import requests",
68
+ "import cloudpickle",
69
+ "import tempfile",
70
+ "import shutil",
71
+ "import asyncio",
72
+ "import logging",
73
+ "import time"
74
+ ]
75
+ }
76
+
77
+ try:
78
+ # --- Initialize TinyCodeAgent ---
79
+ agent = TinyCodeAgent(
80
+ model=model,
81
+ api_key=api_key,
82
+ log_manager=log_manager,
83
+ provider="modal",
84
+ tools=[get_weather, get_traffic],
85
+ provider_config=provider_config
86
+ )
87
+
88
+ # --- Create Gradio UI ---
89
+ gradio_ui = GradioCallback(
90
+ file_upload_folder=upload_folder,
91
+ show_thinking=True,
92
+ show_tool_calls=True,
93
+ logger=ui_logger
94
+ )
95
+ agent.add_callback(gradio_ui)
96
+
97
+ # --- Connect to MCP servers (as per contribution guide) ---
98
+ try:
99
+ ui_logger.info("Connecting to MCP servers...")
100
+ await agent.connect_to_server("npx", ["-y", "@modelcontextprotocol/server-sequential-thinking"])
101
+ ui_logger.info("Connected to MCP servers.")
102
+ except Exception as e:
103
+ ui_logger.warning(f"Failed to connect to MCP servers: {e}")
104
+ # Continue without servers - we still have the local tools
105
+
106
+ # --- Launch Gradio Interface ---
107
+ ui_logger.info("Launching Gradio interface...")
108
+ try:
109
+ gradio_ui.launch(
110
+ agent.agent, # Pass the underlying TinyAgent
111
+ title="TinyCodeAgent Chat Interface",
112
+ description="Chat with TinyCodeAgent. Try asking: 'I need to know the weather and traffic in Toronto, Montreal, New York, Paris and San Francisco.'",
113
+ share=False,
114
+ prevent_thread_lock=True,
115
+ show_error=True,
116
+ mcp_server=True,
117
+ )
118
+ ui_logger.info("Gradio interface launched (non-blocking).")
119
+
120
+ # Keep the event loop running
121
+ while True:
122
+ await asyncio.sleep(1)
123
+
124
+ except KeyboardInterrupt:
125
+ ui_logger.info("Received keyboard interrupt, shutting down...")
126
+ except Exception as e:
127
+ ui_logger.error(f"Failed to launch or run Gradio app: {e}", exc_info=True)
128
+
129
+ finally:
130
+ # Clean up
131
+ ui_logger.info("Cleaning up resources...")
132
+ if os.path.exists(upload_folder):
133
+ ui_logger.info(f"Removing temporary upload folder: {upload_folder}")
134
+ shutil.rmtree(upload_folder)
135
+
136
+ if 'agent' in locals():
137
+ await agent.close()
138
+
139
+ ui_logger.info("--- TinyCodeAgent Example Finished ---")
140
+
141
+
142
+ def simple_example():
143
+ """Simple example without Gradio UI."""
144
+ async def _simple_example():
145
+ # Basic setup
146
+ api_key = os.environ.get("OPENAI_API_KEY")
147
+ if not api_key:
148
+ print("OPENAI_API_KEY environment variable not set.")
149
+ return
150
+
151
+ # Initialize TinyCodeAgent
152
+ agent = TinyCodeAgent(
153
+ model="gpt-4.1-mini",
154
+ api_key=api_key,
155
+ tools=[get_weather, get_traffic]
156
+ )
157
+
158
+ try:
159
+ # Run a simple query
160
+ result = await agent.run(
161
+ "I need to check the weather and traffic in Toronto. Can you help me?"
162
+ )
163
+ print(f"\nResult: {result}")
164
+
165
+ finally:
166
+ await agent.close()
167
+
168
+ asyncio.run(_simple_example())
169
+
170
+
171
+ if __name__ == "__main__":
172
+ # Run the full example with Gradio
173
+ asyncio.run(run_example())
174
+
175
+ # Uncomment to run the simple example instead
176
+ # simple_example()
@@ -0,0 +1,173 @@
1
+
2
+ import yaml
3
+ from jinja2 import Template
4
+ from textwrap import dedent
5
+ import asyncio
6
+ from typing import Any, Dict
7
+ import inspect
8
+ from typing import get_type_hints
9
+
10
+
11
+ prompt_code_example = dedent("""
12
+ User: How the following repo has implemented Logging, and how can I make it compatible with Facebook Standard Logging? github url: https://github.com/askbudi/tinyagent
13
+
14
+ function_calling: run_python
15
+ run_python("task='How the following repo has implemented Logging, and how can I make it compatible with Facebook Standard Logging?'",
16
+ "repo_result = code_research(repo_url='https://github.com/askbudi/tinyagent',task='How the following repo has implemented Logging, and how can I make it compatible with Facebook Standard Logging?')",
17
+ "print(repo_result)",
18
+ "answer_for_user_review = problem_solver(task=task,context=repo_result)",
19
+ "print(answer_for_user_review)",
20
+
21
+
22
+ """)
23
+
24
+ prompt_qwen_helper = dedent("""
25
+
26
+ **Your learning from past mistakes**
27
+ - Always think step by step about the task, what is it, how can you solve it, what are the steps you need to take to solve it.
28
+ - When you write a code and receive an error from run_python , go through your code and error step by step, you need to debug your code.
29
+ - You are an Agent, You need to solve the task, not suggesting user about how to solve the task.
30
+ - User can't directly see the response of run_python tool, so you need to use final_answer or ask_question whenever you want to show a response to the user.
31
+ Other tools calls and their responses are not visible to the user.
32
+ - run_python is a capable tool, if you need to call a function with different arguments, you can do it in one take, just like you would do in a python code you developed to be executed in one cell of Jupyter Notebook Cell.
33
+ - When Task is not resolvable using functions available in your python enviroment, first think about creating a new function to solve the task, then ask the user about your approach and your code, if user allowed you to use it, then define and execute your custom made function. [It is your super power, you can create functions to solve the task.]
34
+ - When you are defining a new function, you need to add any needed imports inside the function.
35
+ Example: instead of:
36
+ import requests
37
+ def get_weather_data(city: str,api_key: str) -> str:
38
+ response = requests.get(f"https://api.openweathermap.org/data/2.5/weather?q={city}&appid={api_key}")
39
+ return response.json()
40
+ You should do it like this: Otherwise you will get an error.
41
+ def get_weather_data(city: str,api_key: str) -> str:
42
+ import requests
43
+ response = requests.get(f"https://api.openweathermap.org/data/2.5/weather?q={city}&appid={api_key}")
44
+ return response.json()
45
+ ---
46
+ You are an Agent, You need to solve the task, not suggesting user about how to solve the task. Facing an error? Think about the error and try another approach to solve the task.
47
+
48
+ """)
49
+
50
+ def load_template(path: str) -> str:
51
+ """
52
+ Load the YAML file and extract its 'system_prompt' field.
53
+ """
54
+ with open(path, "r") as f:
55
+ data = yaml.safe_load(f)
56
+ return data["system_prompt"]
57
+
58
+ def render_system_prompt(template_str: str,
59
+ tools: dict,
60
+ managed_agents: dict,
61
+ authorized_imports) -> str:
62
+ """
63
+ Render the Jinja2 template with the given context.
64
+ """
65
+ tmpl = Template(template_str)
66
+ return tmpl.render(
67
+ tools=tools,
68
+ managed_agents=managed_agents,
69
+ authorized_imports=authorized_imports
70
+ )
71
+
72
+
73
+
74
+ def translate_tool_for_code_agent(tool_func_or_class: Any) -> Dict[str, Any]:
75
+ """
76
+ Translate a tool decorated with @tool into a format compatible with code_agent.yaml.
77
+
78
+ Args:
79
+ tool_func_or_class: A function or class decorated with @tool
80
+
81
+ Returns:
82
+ A dictionary with the tool configuration in code_agent.yaml format
83
+ """
84
+ def _get_type_as_string(type_hint: Any) -> str:
85
+ """
86
+ Convert a type hint to its string representation.
87
+
88
+ Args:
89
+ type_hint: The type hint to convert
90
+
91
+ Returns:
92
+ String representation of the type
93
+ """
94
+ if type_hint is Any:
95
+ return "Any"
96
+
97
+ # Handle common types
98
+ type_map = {
99
+ str: "str",
100
+ int: "int",
101
+ float: "float",
102
+ bool: "bool",
103
+ list: "List",
104
+ dict: "Dict",
105
+ tuple: "Tuple",
106
+ None: "None"
107
+ }
108
+
109
+ if type_hint in type_map:
110
+ return type_map[type_hint]
111
+
112
+ # Try to get the name attribute
113
+ if hasattr(type_hint, "__name__"):
114
+ return type_hint.__name__
115
+
116
+ # For generic types like List[str], Dict[str, int], etc.
117
+ return str(type_hint).replace("typing.", "")
118
+ # Check if the tool has the required metadata
119
+ if not hasattr(tool_func_or_class, '_tool_metadata'):
120
+ raise ValueError("Tool must be decorated with @tool decorator")
121
+
122
+ metadata = tool_func_or_class._tool_metadata
123
+
124
+ # Check if it's an async function
125
+ is_async = asyncio.iscoroutinefunction(tool_func_or_class)
126
+ if metadata["is_class"] and hasattr(tool_func_or_class, "__call__"):
127
+ is_async = asyncio.iscoroutinefunction(tool_func_or_class.__call__)
128
+
129
+ # Get the function signature for parameter types
130
+ if metadata["is_class"]:
131
+ func_to_inspect = tool_func_or_class.__init__
132
+ else:
133
+ func_to_inspect = tool_func_or_class
134
+
135
+ sig = inspect.signature(func_to_inspect)
136
+ type_hints = get_type_hints(func_to_inspect)
137
+
138
+ # Build inputs dictionary
139
+ inputs = {}
140
+ for name, param in sig.parameters.items():
141
+ if name in ['self', 'cls']:
142
+ continue
143
+
144
+ param_type = type_hints.get(name, Any)
145
+ param_type_str = _get_type_as_string(param_type)
146
+
147
+ # Get parameter description from schema if available
148
+ param_desc = ""
149
+ if metadata["schema"] and "properties" in metadata["schema"]:
150
+ if name in metadata["schema"]["properties"]:
151
+ param_desc = metadata["schema"]["properties"][name].get("description", "")
152
+
153
+ inputs[name] = {
154
+ "type": param_type_str,
155
+ "description": param_desc
156
+ }
157
+
158
+ # Determine output type
159
+ output_type = "Any"
160
+ if "return" in type_hints:
161
+ output_type = _get_type_as_string(type_hints["return"])
162
+
163
+ # Create the tool config
164
+ tool_config = {
165
+ "name": metadata["name"],
166
+ "description": metadata["description"],
167
+ "inputs": inputs,
168
+ "output_type": output_type,
169
+ "is_async": is_async
170
+ }
171
+
172
+ return tool_config
173
+