gemini-agent-framework 0.1.2__tar.gz → 0.1.3__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.
- {gemini_agent_framework-0.1.2 → gemini_agent_framework-0.1.3}/PKG-INFO +1 -1
- {gemini_agent_framework-0.1.2 → gemini_agent_framework-0.1.3}/pyproject.toml +1 -1
- gemini_agent_framework-0.1.3/src/gemini_agent/__init__.py +4 -0
- {gemini_agent_framework-0.1.2 → gemini_agent_framework-0.1.3}/src/gemini_agent/agent.py +87 -4
- gemini_agent_framework-0.1.3/tests/__init__.py +3 -0
- {gemini_agent_framework-0.1.2 → gemini_agent_framework-0.1.3}/tests/test_agent.py +1 -1
- gemini_agent_framework-0.1.3/tests/test_variables.py +133 -0
- gemini_agent_framework-0.1.2/src/gemini_agent/__init__.py +0 -4
- {gemini_agent_framework-0.1.2 → gemini_agent_framework-0.1.3}/.gitignore +0 -0
- {gemini_agent_framework-0.1.2 → gemini_agent_framework-0.1.3}/README.md +0 -0
- {gemini_agent_framework-0.1.2 → gemini_agent_framework-0.1.3}/requirements.txt +0 -0
- {gemini_agent_framework-0.1.2 → gemini_agent_framework-0.1.3}/tests/test_class_methods.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: gemini-agent-framework
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.3
|
4
4
|
Summary: A framework for building agents that use Gemini's function calling capabilities
|
5
5
|
Project-URL: Homepage, https://github.com/m7mdony/gemini-agent-framework
|
6
6
|
Project-URL: Documentation, https://github.com/m7mdony/gemini-agent-framework#readme
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "gemini-agent-framework"
|
7
|
-
version = "0.1.
|
7
|
+
version = "0.1.3"
|
8
8
|
description = "A framework for building agents that use Gemini's function calling capabilities"
|
9
9
|
readme = "README.md"
|
10
10
|
requires-python = ">=3.8"
|
@@ -4,6 +4,7 @@ import inspect
|
|
4
4
|
from functools import wraps
|
5
5
|
from typing import List, Callable, Dict, Any, Optional
|
6
6
|
from dotenv import load_dotenv
|
7
|
+
from datetime import datetime
|
7
8
|
|
8
9
|
load_dotenv()
|
9
10
|
|
@@ -76,6 +77,7 @@ class Agent:
|
|
76
77
|
self._tool_functions: Dict[str, Callable] = {} # Map name to actual function
|
77
78
|
self._tool_instances: Dict[str, Any] = {} # Store instances for class methods
|
78
79
|
self._intermediate_results: Dict[str, Any] = {} # Store intermediate results
|
80
|
+
self._stored_variables: Dict[str, Dict[str, Any]] = {} # Store variables with metadata
|
79
81
|
|
80
82
|
if tools:
|
81
83
|
self._process_tools(tools)
|
@@ -138,8 +140,52 @@ class Agent:
|
|
138
140
|
|
139
141
|
self._registered_tools_json.append(declaration_json)
|
140
142
|
|
143
|
+
def set_variable(self, name: str, value: Any, description: str = "", type_hint: type = None) -> None:
|
144
|
+
"""
|
145
|
+
Stores a variable in the agent's memory with metadata.
|
146
|
+
|
147
|
+
Args:
|
148
|
+
name: The name of the variable
|
149
|
+
value: The actual value to store
|
150
|
+
description: A description of what the variable represents
|
151
|
+
type_hint: Optional type hint for the variable
|
152
|
+
"""
|
153
|
+
self._stored_variables[name] = {
|
154
|
+
'value': value,
|
155
|
+
'description': description,
|
156
|
+
'type': type_hint or type(value).__name__,
|
157
|
+
'created_at': datetime.now().isoformat()
|
158
|
+
}
|
159
|
+
|
160
|
+
def get_variable(self, name: str) -> Any:
|
161
|
+
"""
|
162
|
+
Retrieves a stored variable's value.
|
163
|
+
|
164
|
+
Args:
|
165
|
+
name: The name of the variable to retrieve
|
166
|
+
|
167
|
+
Returns:
|
168
|
+
The stored value or None if not found
|
169
|
+
"""
|
170
|
+
return self._stored_variables.get(name, {}).get('value')
|
171
|
+
|
172
|
+
def list_variables(self) -> Dict[str, Dict[str, Any]]:
|
173
|
+
"""
|
174
|
+
Returns information about all stored variables.
|
175
|
+
|
176
|
+
Returns:
|
177
|
+
Dictionary of variable names to their metadata
|
178
|
+
"""
|
179
|
+
return {name: {k: v for k, v in data.items() if k != 'value'}
|
180
|
+
for name, data in self._stored_variables.items()}
|
181
|
+
|
141
182
|
def _get_system_prompt(self) -> str:
|
142
183
|
"""Returns a system prompt that guides the model in breaking down complex operations."""
|
184
|
+
variables_info = "\n".join([
|
185
|
+
f"- {name}: {data['description']} (Type: {data['type']})"
|
186
|
+
for name, data in self._stored_variables.items()
|
187
|
+
])
|
188
|
+
|
143
189
|
return """You are an AI assistant that can break down complex tasks into sequential steps using available tools.
|
144
190
|
When faced with a complex request:
|
145
191
|
1. Analyze the request to identify which tools can be used
|
@@ -152,13 +198,49 @@ class Agent:
|
|
152
198
|
Available tools:
|
153
199
|
{tools_list}
|
154
200
|
|
201
|
+
Available variables:
|
202
|
+
{variables_list}
|
203
|
+
|
204
|
+
IMPORTANT - Variable Usage:
|
205
|
+
When you need to use a stored variable in a function call, you MUST use the following syntax:
|
206
|
+
- For function arguments: {{"variable": "variable_name"}}
|
207
|
+
- For example, if you want to use the 'current_user' variable in a function call:
|
208
|
+
{{"user_id": {{"variable": "current_user"}}}}
|
209
|
+
|
155
210
|
Remember:
|
156
211
|
- Always perform one operation at a time
|
157
212
|
- Use intermediate results from previous steps
|
158
213
|
- If a step requires multiple tools, execute them sequentially
|
159
214
|
- If you're unsure about the next step, explain your reasoning
|
160
|
-
|
161
|
-
|
215
|
+
- You can use both stored variables and values from the prompt
|
216
|
+
- When using stored variables, ALWAYS use the {{"variable": "variable_name"}} syntax
|
217
|
+
""".format(
|
218
|
+
tools_list="\n".join([f"- {name}: {desc}" for name, desc in
|
219
|
+
[(tool['name'], tool['description']) for tool in self._registered_tools_json]]),
|
220
|
+
variables_list=variables_info
|
221
|
+
)
|
222
|
+
|
223
|
+
def _substitute_variables(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
224
|
+
"""
|
225
|
+
Substitutes stored variable values in function arguments.
|
226
|
+
|
227
|
+
Args:
|
228
|
+
args: Dictionary of argument names to values
|
229
|
+
|
230
|
+
Returns:
|
231
|
+
Dictionary with variable values substituted where applicable
|
232
|
+
"""
|
233
|
+
substituted_args = {}
|
234
|
+
for name, value in args.items():
|
235
|
+
if isinstance(value, dict) and "variable" in value:
|
236
|
+
var_name = value["variable"]
|
237
|
+
if var_name in self._stored_variables:
|
238
|
+
substituted_args[name] = self._stored_variables[var_name]['value']
|
239
|
+
else:
|
240
|
+
substituted_args[name] = value
|
241
|
+
else:
|
242
|
+
substituted_args[name] = value
|
243
|
+
return substituted_args
|
162
244
|
|
163
245
|
def _call_gemini_api(self, payload: Dict[str, Any]) -> Dict[str, Any]:
|
164
246
|
"""Makes a POST request to the Gemini generateContent endpoint."""
|
@@ -210,7 +292,7 @@ class Agent:
|
|
210
292
|
current_contents = conversation_history if conversation_history else []
|
211
293
|
if system_prompt and not current_contents:
|
212
294
|
current_contents.append({'role': 'user', 'parts': [{'text': system_prompt}]})
|
213
|
-
current_contents.append({'role': 'model', 'parts': [{'text': "I understand I should break down complex tasks into sequential steps using the available tools."}]})
|
295
|
+
current_contents.append({'role': 'model', 'parts': [{'text': "I understand I should break down complex tasks into sequential steps using the available tools and variables."}]})
|
214
296
|
|
215
297
|
current_contents.append({'role': 'user', 'parts': [{'text': user_prompt}]})
|
216
298
|
payload: Dict[str, Any] = {"contents": current_contents}
|
@@ -242,7 +324,6 @@ class Agent:
|
|
242
324
|
|
243
325
|
while True:
|
244
326
|
response_data = self._call_gemini_api(payload)
|
245
|
-
|
246
327
|
if "error" in response_data:
|
247
328
|
print(f"API call failed: {response_data['error'].get('message', 'Unknown API error')}")
|
248
329
|
return response_data
|
@@ -285,6 +366,8 @@ class Agent:
|
|
285
366
|
tool_function = self._tool_functions[tool_name]
|
286
367
|
print(f"--- Calling Function: {tool_name}({args}) ---")
|
287
368
|
|
369
|
+
# Substitute both stored variables and intermediate results
|
370
|
+
args = self._substitute_variables(args)
|
288
371
|
for key, value in args.items():
|
289
372
|
if isinstance(value, str) and value.startswith('$'):
|
290
373
|
result_key = value[1:]
|
@@ -0,0 +1,133 @@
|
|
1
|
+
from agent import Agent
|
2
|
+
import os
|
3
|
+
from dotenv import load_dotenv
|
4
|
+
import json
|
5
|
+
from bs4 import BeautifulSoup
|
6
|
+
|
7
|
+
load_dotenv()
|
8
|
+
|
9
|
+
class HTMLAnalyzer:
|
10
|
+
@Agent.description("Count the number of input tags in an HTML page")
|
11
|
+
@Agent.parameters({
|
12
|
+
'html_content': {'type': str, 'description': 'The HTML content to analyze'}
|
13
|
+
})
|
14
|
+
def count_inputs(self, html_content: str) -> dict:
|
15
|
+
soup = BeautifulSoup(html_content, 'html.parser')
|
16
|
+
input_tags = soup.find_all('input')
|
17
|
+
return {
|
18
|
+
"count": len(input_tags),
|
19
|
+
"input_types": [tag.get('type', 'unknown') for tag in input_tags]
|
20
|
+
}
|
21
|
+
|
22
|
+
# Create HTML pages
|
23
|
+
home_page = """
|
24
|
+
<!DOCTYPE html>
|
25
|
+
<html>
|
26
|
+
<head>
|
27
|
+
<title>Home Page</title>
|
28
|
+
</head>
|
29
|
+
<body>
|
30
|
+
<header>
|
31
|
+
<nav>
|
32
|
+
<a href="/">Home</a>
|
33
|
+
<a href="/about">About</a>
|
34
|
+
<a href="/contact">Contact</a>
|
35
|
+
</nav>
|
36
|
+
</header>
|
37
|
+
<main>
|
38
|
+
<h1>Welcome to Our Website</h1>
|
39
|
+
<p>This is a simple home page with no input fields.</p>
|
40
|
+
</main>
|
41
|
+
<footer>
|
42
|
+
<p>© 2024 Our Website</p>
|
43
|
+
</footer>
|
44
|
+
</body>
|
45
|
+
</html>
|
46
|
+
"""
|
47
|
+
|
48
|
+
login_page = """
|
49
|
+
<!DOCTYPE html>
|
50
|
+
<html>
|
51
|
+
<head>
|
52
|
+
<title>Login Page</title>
|
53
|
+
</head>
|
54
|
+
<body>
|
55
|
+
<header>
|
56
|
+
<nav>
|
57
|
+
<a href="/">Home</a>
|
58
|
+
<a href="/login">Login</a>
|
59
|
+
</nav>
|
60
|
+
</header>
|
61
|
+
<main>
|
62
|
+
<h1>Login</h1>
|
63
|
+
<form action="/login" method="POST">
|
64
|
+
<div>
|
65
|
+
<label for="username">Username:</label>
|
66
|
+
<input type="text" id="username" name="username" required>
|
67
|
+
</div>
|
68
|
+
<div>
|
69
|
+
<label for="password">Password:</label>
|
70
|
+
<input type="password" id="password" name="password" required>
|
71
|
+
</div>
|
72
|
+
<div>
|
73
|
+
<input type="checkbox" id="remember" name="remember">
|
74
|
+
<label for="remember">Remember me</label>
|
75
|
+
</div>
|
76
|
+
<button type="submit">Login</button>
|
77
|
+
</form>
|
78
|
+
</main>
|
79
|
+
<footer>
|
80
|
+
<p>© 2024 Our Website</p>
|
81
|
+
</footer>
|
82
|
+
</body>
|
83
|
+
</html>
|
84
|
+
"""
|
85
|
+
|
86
|
+
# Create the analyzer instance
|
87
|
+
html_analyzer = HTMLAnalyzer()
|
88
|
+
|
89
|
+
# Create the agent with our tool
|
90
|
+
agent = Agent(
|
91
|
+
api_key=os.getenv("GEMINI_API_KEY"),
|
92
|
+
tools=[html_analyzer.count_inputs]
|
93
|
+
)
|
94
|
+
|
95
|
+
# Store the HTML pages as variables
|
96
|
+
agent.set_variable(
|
97
|
+
name="home_page",
|
98
|
+
value=home_page,
|
99
|
+
description="The HTML content of the home page",
|
100
|
+
type_hint=str
|
101
|
+
)
|
102
|
+
|
103
|
+
|
104
|
+
|
105
|
+
# Example 1: Count inputs in home page
|
106
|
+
print("\nExample 1: Count inputs in home page")
|
107
|
+
response = agent.prompt(
|
108
|
+
"How many input tags are in the home page?",
|
109
|
+
response_structure={
|
110
|
+
"type": "object",
|
111
|
+
"properties": {
|
112
|
+
"count": {"type": "integer"},
|
113
|
+
"input_types": {"type": "array", "items": {"type": "string"}}
|
114
|
+
},
|
115
|
+
"required": ["count", "input_types"]
|
116
|
+
}
|
117
|
+
)
|
118
|
+
print(json.dumps(response, indent=2))
|
119
|
+
|
120
|
+
# Example 2: Count inputs in login page
|
121
|
+
print("\nExample 2: Count inputs in login page")
|
122
|
+
response = agent.prompt(
|
123
|
+
"How many input tags are in this page "+ login_page,
|
124
|
+
response_structure={
|
125
|
+
"type": "object",
|
126
|
+
"properties": {
|
127
|
+
"count": {"type": "integer"},
|
128
|
+
"input_types": {"type": "array", "items": {"type": "string"}}
|
129
|
+
},
|
130
|
+
"required": ["count", "input_types"]
|
131
|
+
}
|
132
|
+
)
|
133
|
+
print(json.dumps(response, indent=2))
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|