gofannon 0.25.20__py3-none-any.whl → 0.25.21__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.
gofannon/base/__init__.py CHANGED
@@ -8,6 +8,7 @@ from pathlib import Path
8
8
 
9
9
  import anyio
10
10
 
11
+ from .adk_mixin import AdkMixin
11
12
  from ..config import ToolConfig
12
13
 
13
14
  from .smol_agents import SmolAgentsMixin
@@ -74,6 +75,7 @@ class BaseTool(SmolAgentsMixin,
74
75
  LangflowMixin,
75
76
  MCPMixin,
76
77
  LlamaStackMixin,
78
+ AdkMixin,
77
79
  ABC):
78
80
  def __init__(self, **kwargs):
79
81
  self.logger = logging.getLogger(
@@ -0,0 +1,291 @@
1
+ # gofannon/base/adk_mixin.py
2
+
3
+ import inspect
4
+ import json
5
+ from typing import Any, Callable, Dict, List, Optional, Type as TypingType
6
+
7
+ # Try to import ADK components
8
+ try:
9
+ from google.adk.tools import BaseTool as AdkBaseTool
10
+ from google.adk.tools import FunctionTool as AdkFunctionTool
11
+ from google.adk.tools.tool_context import ToolContext as AdkToolContext
12
+ # _automatic_function_calling_util is not typically a public export,
13
+ # but FunctionTool uses it. For export, we might need a different strategy
14
+ # or rely on AdkFunctionTool to build its declaration.
15
+ # from google.adk.tools._automatic_function_calling_util import (
16
+ # build_function_declaration as adk_build_function_declaration,
17
+ # )
18
+ from google.genai import types as adk_gemini_types
19
+ import anyio # For running sync Gofannon fn in async ADK tool
20
+
21
+ _HAS_ADK = True
22
+ except ImportError:
23
+ _HAS_ADK = False
24
+ # Define dummy types for type hinting if ADK is not present
25
+ class AdkBaseTool: pass
26
+ class AdkFunctionTool(AdkBaseTool): pass # type: ignore
27
+ class AdkToolContext: pass # type: ignore
28
+ class adk_gemini_types: # type: ignore
29
+ class Type:
30
+ STRING = "STRING"
31
+ INTEGER = "INTEGER"
32
+ NUMBER = "NUMBER"
33
+ BOOLEAN = "BOOLEAN"
34
+ ARRAY = "ARRAY"
35
+ OBJECT = "OBJECT"
36
+ TYPE_UNSPECIFIED = "TYPE_UNSPECIFIED"
37
+ class Schema: # type: ignore
38
+ def __init__(self, **kwargs):
39
+ self.type = kwargs.get('type', adk_gemini_types.Type.OBJECT)
40
+ self.description = kwargs.get('description')
41
+ self.properties = kwargs.get('properties', {})
42
+ self.items = kwargs.get('items')
43
+ self.required = kwargs.get('required', [])
44
+ self.enum = kwargs.get('enum')
45
+ self.nullable = kwargs.get('nullable')
46
+
47
+ class FunctionDeclaration: # type: ignore
48
+ def __init__(self, name, description, parameters):
49
+ self.name = name
50
+ self.description = description
51
+ self.parameters = parameters
52
+
53
+
54
+ # Helper for ADK Schema to Gofannon JSON Schema
55
+ ADK_GEMINI_TYPE_TO_JSON_TYPE = {
56
+ adk_gemini_types.Type.STRING: "string",
57
+ adk_gemini_types.Type.INTEGER: "integer",
58
+ adk_gemini_types.Type.NUMBER: "number",
59
+ adk_gemini_types.Type.BOOLEAN: "boolean",
60
+ adk_gemini_types.Type.ARRAY: "array",
61
+ adk_gemini_types.Type.OBJECT: "object",
62
+ adk_gemini_types.Type.TYPE_UNSPECIFIED: "object", # Default for unspecified
63
+ }
64
+
65
+ def _adk_schema_to_gofannon_json_schema(adk_schema: Optional[adk_gemini_types.Schema]) -> Dict[str, Any]:
66
+ if not adk_schema:
67
+ return {"type": "object", "properties": {}}
68
+
69
+ json_schema: Dict[str, Any] = {}
70
+
71
+ adk_type_enum = getattr(adk_schema, 'type', adk_gemini_types.Type.TYPE_UNSPECIFIED)
72
+ json_type_str = ADK_GEMINI_TYPE_TO_JSON_TYPE.get(adk_type_enum, "object")
73
+
74
+ if getattr(adk_schema, 'nullable', False):
75
+ # Represent nullable as a list of types including "null" if original type is singular
76
+ # or handle it based on how Gofannon expects nullable
77
+ json_schema["type"] = [json_type_str, "null"] if json_type_str != "object" else json_type_str # Pydantic v1 style for Optional[T]
78
+ if json_type_str == "object": # For objects, nullable flag is more common in JSON schema
79
+ json_schema["nullable"] = True
80
+ json_schema["type"] = "object" # Keep type as object if it was object
81
+ else:
82
+ json_schema["type"] = json_type_str
83
+
84
+ description = getattr(adk_schema, 'description', None)
85
+ if description:
86
+ json_schema["description"] = description
87
+
88
+ if adk_type_enum == adk_gemini_types.Type.OBJECT:
89
+ properties = getattr(adk_schema, 'properties', None)
90
+ if properties:
91
+ json_schema["properties"] = {
92
+ name: _adk_schema_to_gofannon_json_schema(prop_schema)
93
+ for name, prop_schema in properties.items()
94
+ }
95
+ else:
96
+ json_schema["properties"] = {} # Ensure properties exist for object type
97
+
98
+ items = getattr(adk_schema, 'items', None)
99
+ if adk_type_enum == adk_gemini_types.Type.ARRAY and items:
100
+ json_schema["items"] = _adk_schema_to_gofannon_json_schema(items)
101
+
102
+ required_list = getattr(adk_schema, 'required', None)
103
+ if required_list:
104
+ json_schema["required"] = list(required_list)
105
+
106
+ enum_list = getattr(adk_schema, 'enum', None)
107
+ if enum_list:
108
+ json_schema["enum"] = list(enum_list)
109
+
110
+ # Ensure "properties" field exists if type is "object"
111
+ if json_schema.get("type") == "object" and "properties" not in json_schema:
112
+ json_schema["properties"] = {}
113
+
114
+ return json_schema
115
+
116
+ # Helper for Gofannon JSON Schema to ADK Schema
117
+ JSON_TYPE_TO_ADK_GEMINI_TYPE = {
118
+ "string": adk_gemini_types.Type.STRING,
119
+ "integer": adk_gemini_types.Type.INTEGER,
120
+ "number": adk_gemini_types.Type.NUMBER,
121
+ "boolean": adk_gemini_types.Type.BOOLEAN,
122
+ "array": adk_gemini_types.Type.ARRAY,
123
+ "object": adk_gemini_types.Type.OBJECT,
124
+ "null": adk_gemini_types.Type.TYPE_UNSPECIFIED, # ADK has no 'null' type, unspecified is closest
125
+ }
126
+
127
+ def _gofannon_json_schema_to_adk_schema(json_schema: Dict[str, Any]) -> adk_gemini_types.Schema:
128
+ if not json_schema: # Handles empty dict {} case
129
+ return adk_gemini_types.Schema(type=adk_gemini_types.Type.OBJECT, properties={})
130
+
131
+ adk_schema_kwargs: Dict[str, Any] = {}
132
+
133
+ json_type_val = json_schema.get("type", "object")
134
+ is_nullable = json_schema.get("nullable", False) # Check for explicit "nullable"
135
+
136
+ actual_json_type_str = json_type_val
137
+ if isinstance(json_type_val, list): # Handles type: ["string", "null"]
138
+ if "null" in json_type_val:
139
+ is_nullable = True
140
+ actual_json_type_str = next((t for t in json_type_val if t != "null"), "object")
141
+
142
+ adk_type_enum = JSON_TYPE_TO_ADK_GEMINI_TYPE.get(str(actual_json_type_str).lower(), adk_gemini_types.Type.OBJECT)
143
+ adk_schema_kwargs["type"] = adk_type_enum
144
+ if is_nullable:
145
+ adk_schema_kwargs["nullable"] = True
146
+
147
+ if "description" in json_schema:
148
+ adk_schema_kwargs["description"] = json_schema["description"]
149
+
150
+ if adk_type_enum == adk_gemini_types.Type.OBJECT and "properties" in json_schema:
151
+ adk_schema_kwargs["properties"] = {
152
+ name: _gofannon_json_schema_to_adk_schema(prop_schema)
153
+ for name, prop_schema in json_schema["properties"].items()
154
+ }
155
+ elif adk_type_enum == adk_gemini_types.Type.OBJECT: # Ensure properties for object type
156
+ adk_schema_kwargs["properties"] = {}
157
+
158
+ if adk_type_enum == adk_gemini_types.Type.ARRAY and "items" in json_schema:
159
+ adk_schema_kwargs["items"] = _gofannon_json_schema_to_adk_schema(json_schema["items"])
160
+
161
+ if "required" in json_schema:
162
+ adk_schema_kwargs["required"] = list(json_schema["required"])
163
+
164
+ if "enum" in json_schema:
165
+ adk_schema_kwargs["enum"] = list(json_schema["enum"])
166
+
167
+ return adk_gemini_types.Schema(**adk_schema_kwargs)
168
+
169
+
170
+ class AdkMixin:
171
+ def import_from_adk(self, adk_tool: AdkBaseTool):
172
+ """
173
+ Adapts a google-adk-python tool to the Gofannon BaseTool structure.
174
+
175
+ Args:
176
+ adk_tool: An instance of a class derived from `google.adk.tools.BaseTool`.
177
+ """
178
+ if not _HAS_ADK:
179
+ raise RuntimeError(
180
+ "google-adk-python is not installed. Install with `pip install google-adk`"
181
+ )
182
+ if not isinstance(adk_tool, AdkBaseTool): # type: ignore
183
+ raise TypeError("Input must be an instance of ADK BaseTool.")
184
+
185
+ self.name = adk_tool.name # type: ignore
186
+ self.description = adk_tool.description # type: ignore
187
+
188
+ declaration = None
189
+ # Ensure _get_declaration is callable and attempt to call it
190
+ if hasattr(adk_tool, "_get_declaration") and callable(adk_tool._get_declaration): # type: ignore
191
+ try:
192
+ declaration = adk_tool._get_declaration() # type: ignore
193
+ except Exception as e:
194
+ self.logger.warning(f"Could not get declaration from ADK tool {self.name}: {e}. Assuming no parameters.") # type: ignore
195
+
196
+ gofannon_params_schema: Dict[str, Any] = {"type": "object", "properties": {}}
197
+ if declaration and hasattr(declaration, 'parameters') and declaration.parameters:
198
+ gofannon_params_schema = _adk_schema_to_gofannon_json_schema(declaration.parameters)
199
+
200
+ self.definition = { # type: ignore
201
+ "function": {
202
+ "name": self.name,
203
+ "description": self.description,
204
+ "parameters": gofannon_params_schema
205
+ }
206
+ }
207
+
208
+ # Adapt the execution logic
209
+ if isinstance(adk_tool, AdkFunctionTool) and hasattr(adk_tool, 'func'): # type: ignore
210
+ target_callable = adk_tool.func # type: ignore
211
+ self.fn = target_callable # type: ignore
212
+ elif hasattr(adk_tool, 'run_async') and callable(adk_tool.run_async): # type: ignore
213
+ self.logger.warning( # type: ignore
214
+ f"Importing ADK tool {self.name} that is not a FunctionTool. "
215
+ f"ADK ToolContext features will not be available or may require a dummy context. "
216
+ f"Ensure this tool can operate correctly with args only."
217
+ )
218
+ # This wrapper will become self.fn. If self.fn is async, Gofannon's
219
+ # execute_async can await it directly. Gofannon's sync execute
220
+ # would need to handle running this async fn (e.g., using anyio.run).
221
+ async def adk_run_async_wrapper(**kwargs):
222
+ # This simplified call assumes the tool can function with a None ToolContext
223
+ # or that its core logic doesn't strictly depend on it.
224
+ return await adk_tool.run_async(args=kwargs, tool_context=None) # type: ignore
225
+ self.fn = adk_run_async_wrapper # type: ignore
226
+ else:
227
+ self.logger.error( # type: ignore
228
+ f"ADK tool {self.name} does not have a suitable execution method ('func' or 'run_async')."
229
+ )
230
+ def placeholder_fn(**kwargs):
231
+ raise NotImplementedError(f"Execution for imported ADK tool {self.name} is not available.")
232
+ self.fn = placeholder_fn # type: ignore
233
+
234
+
235
+ def export_to_adk(self) -> AdkBaseTool:
236
+ """
237
+ Converts the Gofannon tool to a google-adk-python compatible tool.
238
+ This typically creates a custom AdkBaseTool derivative that uses the
239
+ Gofannon tool's definition and execution logic.
240
+
241
+ Returns:
242
+ An instance of a `google.adk.tools.BaseTool` derivative.
243
+ """
244
+ if not _HAS_ADK:
245
+ raise RuntimeError(
246
+ "google-adk-python is not installed. Install with `pip install google-adk`"
247
+ )
248
+
249
+ gofannon_def = self.definition.get("function", {}) # type: ignore
250
+ tool_name = gofannon_def.get("name", getattr(self, "name", "gofannon_exported_tool"))
251
+ tool_description = gofannon_def.get("description", getattr(self, "description", "No description provided."))
252
+
253
+ gofannon_params_schema = gofannon_def.get("parameters", {"type": "object", "properties": {}})
254
+
255
+ original_gofannon_fn = self.fn # type: ignore
256
+ is_gofannon_fn_async = inspect.iscoroutinefunction(original_gofannon_fn)
257
+
258
+ # Define a custom ADK Tool class
259
+ class GofannonAdkTool(AdkBaseTool): # type: ignore
260
+ def __init__(self, name, description, gofannon_json_schema, gofannon_exec_fn, is_fn_async):
261
+ super().__init__(name=name, description=description) # type: ignore
262
+ self._gofannon_json_schema = gofannon_json_schema
263
+ self._gofannon_exec_fn = gofannon_exec_fn
264
+ self._is_fn_async = is_fn_async
265
+
266
+ def _get_declaration(self) -> Optional[adk_gemini_types.FunctionDeclaration]: # type: ignore
267
+ adk_params_schema = _gofannon_json_schema_to_adk_schema(self._gofannon_json_schema)
268
+ return adk_gemini_types.FunctionDeclaration( # type: ignore
269
+ name=self.name,
270
+ description=self.description,
271
+ parameters=adk_params_schema
272
+ )
273
+
274
+ async def run_async(self, *, args: Dict[str, Any], tool_context: AdkToolContext) -> Any: # type: ignore
275
+ # The ADK tool_context is available here but the Gofannon fn doesn't expect it.
276
+ # We simply pass the args to the Gofannon function.
277
+ if self._is_fn_async:
278
+ return await self._gofannon_exec_fn(**args)
279
+ else:
280
+ # Gofannon's synchronous fn needs to be run in a thread
281
+ # as ADK's run_async is an async method.
282
+ return await anyio.to_thread.run_sync(self._gofannon_exec_fn, **args) # type: ignore
283
+
284
+ exported_adk_tool = GofannonAdkTool(
285
+ name=tool_name,
286
+ description=tool_description,
287
+ gofannon_json_schema=gofannon_params_schema,
288
+ gofannon_exec_fn=original_gofannon_fn,
289
+ is_fn_async=is_gofannon_fn_async
290
+ )
291
+ return exported_adk_tool
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: gofannon
3
- Version: 0.25.20
3
+ Version: 0.25.21
4
4
  Summary: A collection of tools for LLMs
5
5
  License: ASFv2
6
6
  Author: Trevor Grant
@@ -52,7 +52,7 @@ functionality for various tasks.
52
52
  ## 🌟🌟 Features 🌟🌟
53
53
 
54
54
  1. Cross-Framework Compatibility (Import From/Export To Multiple Frameworks)
55
- - **Current:** `smolagents`, LangChain, AWS Bedrock
55
+ - **Current:** `smolagents`, LangChain, AWS Bedrock, Google ADK
56
56
  - **Currently Being Developed:** [Up To Date List](https://github.com/The-AI-Alliance/gofannon/issues?q=is%3Aissue%20state%3Aopen%20label%3Aframework%20assignee:*)
57
57
  - **In The Roadmap:** [Up To Date List](https://github.com/The-AI-Alliance/gofannon/issues?q=is%3Aissue%20state%3Aopen%20label%3Aframework%20no%3Aassignee)
58
58
  2. A Robust Collection of Tools
@@ -2,7 +2,8 @@ gofannon/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  gofannon/arxiv/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  gofannon/arxiv/get_article.py,sha256=SRGTXFXdXdXTIOLZKWUTXxZEYEqZFWFJEV2nTsU5qqU,1167
4
4
  gofannon/arxiv/search.py,sha256=37Zx1y2vAX1xYIKaAxzBGXE3qPHUZdAD2XR0H1Acs-4,4225
5
- gofannon/base/__init__.py,sha256=wnTkASBszxkZiWugQOgvSdAghFFBUwxzRzz7wTMxg3c,3757
5
+ gofannon/base/__init__.py,sha256=n2tWf7udXSXfREsxXD0E8kOY--X-ffGTNsdMwk2V33A,3814
6
+ gofannon/base/adk_mixin.py,sha256=GTvbCb-LLGPF5LUkf011lKq_JI1CB0n9K5ch_W0pAlk,13322
6
7
  gofannon/base/bedrock.py,sha256=Z2c36R8jaIusgpmegbYVz2eR7lDBc0IhTtwiqUGOcT4,25646
7
8
  gofannon/base/langchain.py,sha256=25z9opy7E7qWP-DSn6oYAqKDg8i21az-kAKrsYLfyiQ,2704
8
9
  gofannon/base/langflow.py,sha256=0WfNJ9MnndyLJ-yUAStIuXZpCzOPItsGKgtxdNifmLM,3833
@@ -64,7 +65,7 @@ gofannon/simpler_grants_gov/search_base.py,sha256=ask8ecAOQ9jZulr8iDxdfQZgSC_Eyx
64
65
  gofannon/simpler_grants_gov/search_opportunities.py,sha256=jZD64VqFIW36dBdAshOOQmywzLqBZyB3wLtA_C95sa8,19931
65
66
  gofannon/wikipedia/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
66
67
  gofannon/wikipedia/wikipedia_lookup.py,sha256=J6wKPbSivCF7cccaiRaJW1o0VqNhQAGfrh5U1ULLesg,2869
67
- gofannon-0.25.20.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
68
- gofannon-0.25.20.dist-info/METADATA,sha256=n5vCujMhTpr9mRxBrV-VakPK3zAxcyvWIBr7bWn6g8o,5415
69
- gofannon-0.25.20.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
70
- gofannon-0.25.20.dist-info/RECORD,,
68
+ gofannon-0.25.21.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
69
+ gofannon-0.25.21.dist-info/METADATA,sha256=LMaFBOVKh41jLmEPujIquU9hJbPNv-PnYjf7Eo3qaiI,5427
70
+ gofannon-0.25.21.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
71
+ gofannon-0.25.21.dist-info/RECORD,,