gemini-agent-framework 0.1.1__py3-none-any.whl → 0.1.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.
gemini_agent/agent.py CHANGED
@@ -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
 
@@ -31,6 +32,7 @@ class Agent:
31
32
  Agent._tools_registry[func.__name__]['description'] = desc
32
33
  Agent._tools_registry[func.__name__]['signature'] = inspect.signature(func)
33
34
  Agent._tools_registry[func.__name__]['function_ref'] = func
35
+ Agent._tools_registry[func.__name__]['is_method'] = inspect.ismethod(func)
34
36
  @wraps(func)
35
37
  def wrapper(*args, **kwargs):
36
38
  return func(*args, **kwargs)
@@ -48,6 +50,7 @@ class Agent:
48
50
  Agent._tools_registry[func.__name__]['signature'] = inspect.signature(func)
49
51
  if 'function_ref' not in Agent._tools_registry[func.__name__]:
50
52
  Agent._tools_registry[func.__name__]['function_ref'] = func
53
+ Agent._tools_registry[func.__name__]['is_method'] = inspect.ismethod(func)
51
54
  @wraps(func)
52
55
  def wrapper(*args, **kwargs):
53
56
  return func(*args, **kwargs)
@@ -60,7 +63,7 @@ class Agent:
60
63
 
61
64
  Args:
62
65
  api_key: Your Google Generative AI API key.
63
- tools: A list of Python functions decorated as tools.
66
+ tools: A list of Python functions or class methods decorated as tools.
64
67
  model_name: The name of the Gemini model to use.
65
68
  """
66
69
  if not api_key:
@@ -72,7 +75,9 @@ class Agent:
72
75
 
73
76
  self._registered_tools_json: List[Dict[str, Any]] = [] # Store JSON representation
74
77
  self._tool_functions: Dict[str, Callable] = {} # Map name to actual function
78
+ self._tool_instances: Dict[str, Any] = {} # Store instances for class methods
75
79
  self._intermediate_results: Dict[str, Any] = {} # Store intermediate results
80
+ self._stored_variables: Dict[str, Dict[str, Any]] = {} # Store variables with metadata
76
81
 
77
82
  if tools:
78
83
  self._process_tools(tools)
@@ -90,6 +95,12 @@ class Agent:
90
95
  print(f"Warning: Function '{tool_name}' is missing @Agent.description. Skipping.")
91
96
  continue
92
97
 
98
+ # Store the bound method directly if it's a class method
99
+ if inspect.ismethod(func):
100
+ self._tool_functions[tool_name] = func
101
+ else:
102
+ self._tool_functions[tool_name] = metadata['function_ref']
103
+
93
104
  # Build the parameters schema JSON
94
105
  gemini_params_schema = {
95
106
  "type": "OBJECT",
@@ -102,6 +113,9 @@ class Agent:
102
113
  if not params_def and signature:
103
114
  params_def = {}
104
115
  for name, param in signature.parameters.items():
116
+ # Skip 'self' parameter for class methods
117
+ if name == 'self' and inspect.ismethod(func):
118
+ continue
105
119
  py_type = param.annotation if param.annotation != inspect.Parameter.empty else str
106
120
  params_def[name] = {'type': py_type, 'description': f'Parameter {name}'}
107
121
 
@@ -125,10 +139,53 @@ class Agent:
125
139
  del declaration_json["parameters"]
126
140
 
127
141
  self._registered_tools_json.append(declaration_json)
128
- self._tool_functions[tool_name] = metadata['function_ref']
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()}
129
181
 
130
182
  def _get_system_prompt(self) -> str:
131
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
+
132
189
  return """You are an AI assistant that can break down complex tasks into sequential steps using available tools.
133
190
  When faced with a complex request:
134
191
  1. Analyze the request to identify which tools can be used
@@ -141,13 +198,49 @@ class Agent:
141
198
  Available tools:
142
199
  {tools_list}
143
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
+
144
210
  Remember:
145
211
  - Always perform one operation at a time
146
212
  - Use intermediate results from previous steps
147
213
  - If a step requires multiple tools, execute them sequentially
148
214
  - If you're unsure about the next step, explain your reasoning
149
- """.format(tools_list="\n".join([f"- {name}: {desc}" for name, desc in
150
- [(tool['name'], tool['description']) for tool in self._registered_tools_json]]))
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
151
244
 
152
245
  def _call_gemini_api(self, payload: Dict[str, Any]) -> Dict[str, Any]:
153
246
  """Makes a POST request to the Gemini generateContent endpoint."""
@@ -192,14 +285,14 @@ class Agent:
192
285
  self._intermediate_results = {}
193
286
 
194
287
  if not system_prompt:
195
- system_prompt = self._get_system_prompt() + system_prompt
196
- else:
197
288
  system_prompt = self._get_system_prompt()
289
+ else:
290
+ system_prompt = self._get_system_prompt() + system_prompt
198
291
 
199
292
  current_contents = conversation_history if conversation_history else []
200
293
  if system_prompt and not current_contents:
201
294
  current_contents.append({'role': 'user', 'parts': [{'text': system_prompt}]})
202
- 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."}]})
203
296
 
204
297
  current_contents.append({'role': 'user', 'parts': [{'text': user_prompt}]})
205
298
  payload: Dict[str, Any] = {"contents": current_contents}
@@ -214,6 +307,14 @@ class Agent:
214
307
 
215
308
  if response_structure and not self._registered_tools_json:
216
309
  apply_structure_later = False
310
+ # If response_structure is a string type, make it more flexible
311
+ if response_structure.get("type") == "string":
312
+ response_structure = {
313
+ "type": ["string", "object"],
314
+ "properties": {
315
+ "value": {"type": "string"}
316
+ }
317
+ }
217
318
  payload["generationConfig"] = {
218
319
  "response_mime_type": "application/json",
219
320
  "response_schema": response_structure
@@ -223,7 +324,6 @@ class Agent:
223
324
 
224
325
  while True:
225
326
  response_data = self._call_gemini_api(payload)
226
-
227
327
  if "error" in response_data:
228
328
  print(f"API call failed: {response_data['error'].get('message', 'Unknown API error')}")
229
329
  return response_data
@@ -266,13 +366,17 @@ class Agent:
266
366
  tool_function = self._tool_functions[tool_name]
267
367
  print(f"--- Calling Function: {tool_name}({args}) ---")
268
368
 
369
+ # Substitute both stored variables and intermediate results
370
+ args = self._substitute_variables(args)
269
371
  for key, value in args.items():
270
372
  if isinstance(value, str) and value.startswith('$'):
271
373
  result_key = value[1:]
272
374
  if result_key in self._intermediate_results:
273
375
  args[key] = self._intermediate_results[result_key]
274
376
 
377
+ # Call the function directly - it's already bound if it's a method
275
378
  function_result = tool_function(**args)
379
+
276
380
  print(f"--- Function Result: {function_result} ---")
277
381
 
278
382
  result_key = f"result_{len(self._intermediate_results)}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gemini-agent-framework
3
- Version: 0.1.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
@@ -0,0 +1,5 @@
1
+ gemini_agent/__init__.py,sha256=2AgrBW0ePFgOvwoNNqRcZaMPdbOaKPoNHXUo_VlFX-M,71
2
+ gemini_agent/agent.py,sha256=_jHx3ELxP2XmHPDiPqZs_1nH2OPdK8p_8AvLwlUGrz8,24045
3
+ gemini_agent_framework-0.1.3.dist-info/METADATA,sha256=DPXkQScmKbTIT4QYqDh4U5iVFFiwy7R1IKzcwRI3R3c,2386
4
+ gemini_agent_framework-0.1.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
5
+ gemini_agent_framework-0.1.3.dist-info/RECORD,,
@@ -1,5 +0,0 @@
1
- gemini_agent/__init__.py,sha256=2AgrBW0ePFgOvwoNNqRcZaMPdbOaKPoNHXUo_VlFX-M,71
2
- gemini_agent/agent.py,sha256=DqbUBsiI3qzEVAeKAMvlTp0yC0HuK8RAgpmxIJWbPYM,19552
3
- gemini_agent_framework-0.1.1.dist-info/METADATA,sha256=glzOEjvwQP2wswyz_NPzPjdQYfaMOooDCMU3ewbGMBs,2386
4
- gemini_agent_framework-0.1.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
5
- gemini_agent_framework-0.1.1.dist-info/RECORD,,