gofannon 0.1.0__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/__init__.py +0 -0
- gofannon/arxiv/__init__.py +2 -0
- gofannon/arxiv/get_article.py +41 -0
- gofannon/arxiv/search.py +115 -0
- gofannon/base/__init__.py +251 -0
- gofannon/basic_math/__init__.py +5 -0
- gofannon/basic_math/addition.py +39 -0
- gofannon/basic_math/division.py +41 -0
- gofannon/basic_math/exponents.py +39 -0
- gofannon/basic_math/multiplication.py +39 -0
- gofannon/basic_math/subtraction.py +39 -0
- gofannon/cli.py +53 -0
- gofannon/config.py +58 -0
- gofannon/github/__init__.py +11 -0
- gofannon/github/commit_file.py +77 -0
- gofannon/github/commit_files.py +116 -0
- gofannon/github/create_issue.py +78 -0
- gofannon/github/get_repo_contents.py +98 -0
- gofannon/github/read_issue.py +70 -0
- gofannon/github/search.py +67 -0
- gofannon/nhsta/__init__.py +59 -0
- gofannon/orchestration/__init__.py +144 -0
- gofannon/orchestration/firebase_wrapper.py +42 -0
- gofannon/reasoning/__init__.py +3 -0
- gofannon/reasoning/base.py +47 -0
- gofannon/reasoning/hierarchical_cot.py +272 -0
- gofannon/reasoning/sequential_cot.py +77 -0
- gofannon/reasoning/tree_of_thought.py +256 -0
- gofannon-0.1.0.dist-info/LICENSE +201 -0
- gofannon-0.1.0.dist-info/METADATA +84 -0
- gofannon-0.1.0.dist-info/RECORD +32 -0
- gofannon-0.1.0.dist-info/WHEEL +4 -0
gofannon/__init__.py
ADDED
File without changes
|
@@ -0,0 +1,41 @@
|
|
1
|
+
from..base import BaseTool
|
2
|
+
import requests
|
3
|
+
from ..config import FunctionRegistry
|
4
|
+
import logging
|
5
|
+
|
6
|
+
logger = logging.getLogger(__name__)
|
7
|
+
|
8
|
+
@FunctionRegistry.register
|
9
|
+
class GetArticle(BaseTool):
|
10
|
+
def __init__(self, name="get_article"):
|
11
|
+
super().__init__()
|
12
|
+
self.name = name
|
13
|
+
|
14
|
+
@property
|
15
|
+
def definition(self):
|
16
|
+
return {
|
17
|
+
"type": "function",
|
18
|
+
"function": {
|
19
|
+
"name": self.name,
|
20
|
+
"description": "Get a specific article from arXiv",
|
21
|
+
"parameters": {
|
22
|
+
"type": "object",
|
23
|
+
"properties": {
|
24
|
+
"id": {
|
25
|
+
"type": "string",
|
26
|
+
"description": "The ID of the article"
|
27
|
+
}
|
28
|
+
},
|
29
|
+
"required": ["id"]
|
30
|
+
}
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
def fn(self, id):
|
35
|
+
logger.debug("Fetching Article '%s' from ArXiv", id)
|
36
|
+
base_url = "http://export.arxiv.org/api/query"
|
37
|
+
params = {
|
38
|
+
"id_list": id
|
39
|
+
}
|
40
|
+
response = requests.get(base_url, params=params)
|
41
|
+
return response.text
|
gofannon/arxiv/search.py
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
|
2
|
+
from..base import BaseTool
|
3
|
+
import requests
|
4
|
+
from ..config import FunctionRegistry
|
5
|
+
import logging
|
6
|
+
|
7
|
+
logger = logging.getLogger(__name__)
|
8
|
+
|
9
|
+
@FunctionRegistry.register
|
10
|
+
class Search(BaseTool):
|
11
|
+
def __init__(self, name="search"):
|
12
|
+
super().__init__()
|
13
|
+
self.name = name
|
14
|
+
|
15
|
+
@property
|
16
|
+
def definition(self):
|
17
|
+
return {
|
18
|
+
"type": "function",
|
19
|
+
"function": {
|
20
|
+
"name": self.name,
|
21
|
+
"description": "Search for articles on arXiv",
|
22
|
+
"parameters": {
|
23
|
+
"type": "object",
|
24
|
+
"properties": {
|
25
|
+
"query": {
|
26
|
+
"type": "string",
|
27
|
+
"description": "The search query"
|
28
|
+
},
|
29
|
+
"start": {
|
30
|
+
"type": "integer",
|
31
|
+
"description": "The start index of the search results"
|
32
|
+
},
|
33
|
+
"max_results": {
|
34
|
+
"type": "integer",
|
35
|
+
"description": "The maximum number of search results to return"
|
36
|
+
},
|
37
|
+
"submittedDateFrom": {
|
38
|
+
"type": "string",
|
39
|
+
"description": "The start submission date in the format YYYYMMDD"
|
40
|
+
},
|
41
|
+
"submittedDateTo": {
|
42
|
+
"type": "string",
|
43
|
+
"description": "The end submission date in the format YYYYMMDD"
|
44
|
+
},
|
45
|
+
"ti": {
|
46
|
+
"type": "string",
|
47
|
+
"description": "Search in title"
|
48
|
+
},
|
49
|
+
"au": {
|
50
|
+
"type": "string",
|
51
|
+
"description": "Search in author"
|
52
|
+
},
|
53
|
+
"abs": {
|
54
|
+
"type": "string",
|
55
|
+
"description": "Search in abstract"
|
56
|
+
},
|
57
|
+
"co": {
|
58
|
+
"type": "string",
|
59
|
+
"description": "Search in comment"
|
60
|
+
},
|
61
|
+
"jr": {
|
62
|
+
"type": "string",
|
63
|
+
"description": "Search in journal reference"
|
64
|
+
},
|
65
|
+
"cat": {
|
66
|
+
"type": "string",
|
67
|
+
"description": "Search in subject category"
|
68
|
+
}
|
69
|
+
},
|
70
|
+
"required": ["query"]
|
71
|
+
}
|
72
|
+
}
|
73
|
+
}
|
74
|
+
|
75
|
+
def _format_date(self, date):
|
76
|
+
if len(date) == 8:
|
77
|
+
return f"{date}0000"
|
78
|
+
return date
|
79
|
+
|
80
|
+
def fn(self, query, start=0, max_results=10, submittedDateFrom=None, submittedDateTo=None, ti=None, au=None, abs=None, co=None, jr=None, cat=None):
|
81
|
+
logger.debug("Querying ArXiv for '%s'", query)
|
82
|
+
base_url = "http://export.arxiv.org/api/query"
|
83
|
+
params = {
|
84
|
+
"search_query": query,
|
85
|
+
"start": start,
|
86
|
+
"max_results": max_results
|
87
|
+
}
|
88
|
+
|
89
|
+
if submittedDateFrom and submittedDateTo:
|
90
|
+
params["search_query"] += f" AND submittedDate:[{self._format_date(submittedDateFrom)}0000 TO {self._format_date(submittedDateTo)}0000]"
|
91
|
+
elif submittedDateFrom:
|
92
|
+
params["search_query"] += f" AND submittedDate:[{self._format_date(submittedDateFrom)}0000 TO *]"
|
93
|
+
elif submittedDateTo:
|
94
|
+
params["search_query"] += f" AND submittedDate:[* TO {self._format_date(submittedDateTo)}0000]"
|
95
|
+
|
96
|
+
if ti:
|
97
|
+
params["search_query"] += f" AND ti:{ti}"
|
98
|
+
|
99
|
+
if au:
|
100
|
+
params["search_query"] += f" AND au:{au}"
|
101
|
+
|
102
|
+
if abs:
|
103
|
+
params["search_query"] += f" AND abs:{abs}"
|
104
|
+
|
105
|
+
if co:
|
106
|
+
params["search_query"] += f" AND co:{co}"
|
107
|
+
|
108
|
+
if jr:
|
109
|
+
params["search_query"] += f" AND jr:{jr}"
|
110
|
+
|
111
|
+
if cat:
|
112
|
+
params["search_query"] += f" AND cat:{cat}"
|
113
|
+
|
114
|
+
response = requests.get(base_url, params=params)
|
115
|
+
return response.text
|
@@ -0,0 +1,251 @@
|
|
1
|
+
import time
|
2
|
+
from abc import ABC, abstractmethod
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from typing import Dict, Any, Callable
|
5
|
+
import json
|
6
|
+
import logging
|
7
|
+
from pathlib import Path
|
8
|
+
from ..config import ToolConfig
|
9
|
+
|
10
|
+
from typing import Any, Dict
|
11
|
+
|
12
|
+
|
13
|
+
try:
|
14
|
+
from smolagents.tools import Tool as SmolTool
|
15
|
+
from smolagents.tools import tool as smol_tool_decorator
|
16
|
+
_HAS_SMOLAGENTS = True
|
17
|
+
except ImportError:
|
18
|
+
_HAS_SMOLAGENTS = False
|
19
|
+
|
20
|
+
try:
|
21
|
+
from langchain.tools import BaseTool as LangchainBaseTool
|
22
|
+
from langchain.pydantic_v1 import BaseModel, Field
|
23
|
+
from typing import Type, Optional
|
24
|
+
_HAS_LANGCHAIN = True
|
25
|
+
except ImportError:
|
26
|
+
_HAS_LANGCHAIN = False
|
27
|
+
|
28
|
+
@dataclass
|
29
|
+
class ToolResult:
|
30
|
+
success: bool
|
31
|
+
output: Any
|
32
|
+
error: str = None
|
33
|
+
retryable: bool = False
|
34
|
+
|
35
|
+
class WorkflowContext:
|
36
|
+
def __init__(self, firebase_config=None):
|
37
|
+
self.data = {}
|
38
|
+
self.execution_log = []
|
39
|
+
self.firebase_config = firebase_config
|
40
|
+
self.local_storage = Path.home() / ".llama" / "checkpoints"
|
41
|
+
self.local_storage.mkdir(parents=True, exist_ok=True)
|
42
|
+
|
43
|
+
def save_checkpoint(self, name="checkpoint"):
|
44
|
+
if self.firebase_config:
|
45
|
+
self._save_to_firebase(name)
|
46
|
+
else:
|
47
|
+
self._save_local(name)
|
48
|
+
|
49
|
+
def _save_local(self, name):
|
50
|
+
path = self.local_storage / f"{name}.json"
|
51
|
+
with open(path, 'w') as f:
|
52
|
+
json.dump({
|
53
|
+
'data': self.data,
|
54
|
+
'execution_log': self.execution_log
|
55
|
+
}, f)
|
56
|
+
|
57
|
+
def _save_to_firebase(self, name):
|
58
|
+
from firebase_admin import firestore
|
59
|
+
db = firestore.client()
|
60
|
+
doc_ref = db.collection('checkpoints').document(name)
|
61
|
+
doc_ref.set({
|
62
|
+
'data': self.data,
|
63
|
+
'execution_log': self.execution_log,
|
64
|
+
'timestamp': firestore.SERVER_TIMESTAMP
|
65
|
+
})
|
66
|
+
|
67
|
+
def log_execution(self, tool_name, duration, input_data, output_data):
|
68
|
+
entry = {
|
69
|
+
'tool': tool_name,
|
70
|
+
'duration': duration,
|
71
|
+
'input': input_data,
|
72
|
+
'output': output_data
|
73
|
+
}
|
74
|
+
self.execution_log.append(entry)
|
75
|
+
|
76
|
+
class BaseTool(ABC):
|
77
|
+
def __init__(self, **kwargs):
|
78
|
+
self.logger = logging.getLogger(f"{self.__class__.__module__}.{self.__class__.__name__}")
|
79
|
+
self._load_config()
|
80
|
+
self._configure(**kwargs)
|
81
|
+
self.logger.debug("Initialized %s tool", self.__class__.__name__)
|
82
|
+
|
83
|
+
def _configure(self, **kwargs):
|
84
|
+
"""Set instance-specific configurations"""
|
85
|
+
for key, value in kwargs.items():
|
86
|
+
if hasattr(self, key):
|
87
|
+
setattr(self, key, value)
|
88
|
+
|
89
|
+
def _load_config(self):
|
90
|
+
"""Auto-load config based on tool type"""
|
91
|
+
if hasattr(self, 'API_SERVICE'):
|
92
|
+
self.api_key = ToolConfig.get(f"{self.API_SERVICE}_api_key")
|
93
|
+
|
94
|
+
@property
|
95
|
+
@abstractmethod
|
96
|
+
def definition(self):
|
97
|
+
pass
|
98
|
+
|
99
|
+
@property
|
100
|
+
def output_schema(self):
|
101
|
+
return self.definition.get('function', {}).get('parameters', {})
|
102
|
+
|
103
|
+
@abstractmethod
|
104
|
+
def fn(self, *args, **kwargs):
|
105
|
+
pass
|
106
|
+
|
107
|
+
def execute(self, context: WorkflowContext, **kwargs) -> ToolResult:
|
108
|
+
try:
|
109
|
+
start_time = time.time()
|
110
|
+
result = self.fn(**kwargs)
|
111
|
+
duration = time.time() - start_time
|
112
|
+
|
113
|
+
context.log_execution(
|
114
|
+
tool_name=self.__class__.__name__,
|
115
|
+
duration=duration,
|
116
|
+
input_data=kwargs,
|
117
|
+
output_data=result
|
118
|
+
)
|
119
|
+
|
120
|
+
return ToolResult(success=True, output=result)
|
121
|
+
except Exception as e:
|
122
|
+
return ToolResult(
|
123
|
+
success=False,
|
124
|
+
output=None,
|
125
|
+
error=str(e),
|
126
|
+
retryable=True
|
127
|
+
)
|
128
|
+
|
129
|
+
def import_from_smolagents(self, smol_tool: "SmolTool"):
|
130
|
+
"""
|
131
|
+
Takes a smolagents Tool instance and adapts it into this Tool.
|
132
|
+
"""
|
133
|
+
if not _HAS_SMOLAGENTS:
|
134
|
+
raise RuntimeError(
|
135
|
+
"smolagents is not installed or could not be imported. "
|
136
|
+
"Install it or check your environment."
|
137
|
+
)
|
138
|
+
self.name = smol_tool.name[0]
|
139
|
+
self.description = smol_tool.description #getattr(smol_tool, "description", "No description provided.")
|
140
|
+
|
141
|
+
|
142
|
+
def adapted_fn(*args, **kwargs):
|
143
|
+
return smol_tool.forward(*args, **kwargs)
|
144
|
+
|
145
|
+
|
146
|
+
self.fn = adapted_fn
|
147
|
+
|
148
|
+
def export_to_smolagents(self) -> "SmolTool":
|
149
|
+
"""
|
150
|
+
Export this Tool as a smolagents Tool instance.
|
151
|
+
This sets up a smolagents-style forward method that calls self.fn.
|
152
|
+
"""
|
153
|
+
if not _HAS_SMOLAGENTS:
|
154
|
+
raise RuntimeError(
|
155
|
+
"smolagents is not installed or could not be imported. "
|
156
|
+
"Install it or check your environment."
|
157
|
+
)
|
158
|
+
|
159
|
+
# Provide a standard forward function that calls self.fn
|
160
|
+
def smol_forward(*args, **kwargs):
|
161
|
+
return self.fn(*args, **kwargs)
|
162
|
+
|
163
|
+
|
164
|
+
inputs_definition = {
|
165
|
+
"example_arg": {
|
166
|
+
"type": "string",
|
167
|
+
"description": "Example argument recognized by this tool"
|
168
|
+
}
|
169
|
+
}
|
170
|
+
output_type = "string"
|
171
|
+
|
172
|
+
# Construct a new smolagents Tool with the minimal fields
|
173
|
+
exported_tool = SmolTool()
|
174
|
+
exported_tool.name = getattr(self, "name", "exported_base_tool")
|
175
|
+
exported_tool.description = getattr(self, "description", "Exported from Tool")
|
176
|
+
exported_tool.inputs = inputs_definition
|
177
|
+
exported_tool.output_type = output_type
|
178
|
+
exported_tool.forward = smol_forward
|
179
|
+
exported_tool.is_initialized = True
|
180
|
+
|
181
|
+
return exported_tool
|
182
|
+
|
183
|
+
def import_from_langchain(self, langchain_tool: "LangchainBaseTool"):
|
184
|
+
if not _HAS_LANGCHAIN:
|
185
|
+
raise RuntimeError("langchain is not installed. Install with `pip install langchain-core`")
|
186
|
+
|
187
|
+
self.name = getattr(langchain_tool, "name", "exported_langchain_tool")
|
188
|
+
self.description = getattr(langchain_tool, "description", "No description provided.")
|
189
|
+
|
190
|
+
maybe_args_schema = getattr(langchain_tool, "args_schema", None)
|
191
|
+
if maybe_args_schema and hasattr(maybe_args_schema, "schema") and callable(maybe_args_schema.schema):
|
192
|
+
args_schema = maybe_args_schema.schema()
|
193
|
+
else:
|
194
|
+
args_schema = {}
|
195
|
+
|
196
|
+
# Store parameters to avoid modifying the definition property directly
|
197
|
+
self._parameters = args_schema.get("properties", {})
|
198
|
+
self._required = args_schema.get("required", [])
|
199
|
+
|
200
|
+
# Adapt the LangChain tool's execution method
|
201
|
+
def adapted_fn(*args, **kwargs):
|
202
|
+
return langchain_tool._run(*args, **kwargs)
|
203
|
+
|
204
|
+
self.fn = adapted_fn
|
205
|
+
|
206
|
+
def export_to_langchain(self) -> "LangchainBaseTool":
|
207
|
+
if not _HAS_LANGCHAIN:
|
208
|
+
raise RuntimeError(
|
209
|
+
"langchain is not installed. Install with `pip install langchain-core`"
|
210
|
+
)
|
211
|
+
|
212
|
+
from pydantic import create_model
|
213
|
+
|
214
|
+
# Create type mapping from JSON schema types to Python types
|
215
|
+
type_map = {
|
216
|
+
"number": float,
|
217
|
+
"string": str,
|
218
|
+
"integer": int,
|
219
|
+
"boolean": bool,
|
220
|
+
"object": dict,
|
221
|
+
"array": list
|
222
|
+
}
|
223
|
+
|
224
|
+
parameters = self.definition.get("function", {}).get("parameters", {})
|
225
|
+
param_properties = parameters.get("properties", {})
|
226
|
+
|
227
|
+
# Dynamically create ArgsSchema using pydantic.create_model
|
228
|
+
fields = {}
|
229
|
+
for param_name, param_def in param_properties.items():
|
230
|
+
param_type = param_def.get("type", "string")
|
231
|
+
description = param_def.get("description", "")
|
232
|
+
fields[param_name] = (
|
233
|
+
type_map.get(param_type, str),
|
234
|
+
Field(..., description=description)
|
235
|
+
)
|
236
|
+
|
237
|
+
ArgsSchema = create_model('ArgsSchema', **fields)
|
238
|
+
|
239
|
+
# Create tool subclass with our functionality
|
240
|
+
class ExportedTool(LangchainBaseTool):
|
241
|
+
name: str = self.definition.get("function", {}).get("name", "")
|
242
|
+
description: str = self.definition.get("function", {}).get("description", "")
|
243
|
+
args_schema: Type[BaseModel] = ArgsSchema
|
244
|
+
fn: Callable = self.fn
|
245
|
+
|
246
|
+
def _run(self, *args, **kwargs):
|
247
|
+
return self.fn(*args, **kwargs)
|
248
|
+
|
249
|
+
# Instantiate and return the tool
|
250
|
+
tool = ExportedTool()
|
251
|
+
return tool
|
@@ -0,0 +1,39 @@
|
|
1
|
+
from..base import BaseTool
|
2
|
+
from ..config import FunctionRegistry
|
3
|
+
import logging
|
4
|
+
|
5
|
+
logger = logging.getLogger(__name__)
|
6
|
+
|
7
|
+
@FunctionRegistry.register
|
8
|
+
class Addition(BaseTool):
|
9
|
+
def __init__(self, name="addition"):
|
10
|
+
super().__init__()
|
11
|
+
self.name = name
|
12
|
+
|
13
|
+
@property
|
14
|
+
def definition(self):
|
15
|
+
return {
|
16
|
+
"type": "function",
|
17
|
+
"function": {
|
18
|
+
"name": self.name,
|
19
|
+
"description": "Calculate the sum of two numbers",
|
20
|
+
"parameters": {
|
21
|
+
"type": "object",
|
22
|
+
"properties": {
|
23
|
+
"num1": {
|
24
|
+
"type": "number",
|
25
|
+
"description": "The first number"
|
26
|
+
},
|
27
|
+
"num2": {
|
28
|
+
"type": "number",
|
29
|
+
"description": "The second number"
|
30
|
+
}
|
31
|
+
},
|
32
|
+
"required": ["num1", "num2"]
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
def fn(self, num1, num2):
|
38
|
+
logger.debug(f"Adding {num1} and {num2}")
|
39
|
+
return num1 + num2
|
@@ -0,0 +1,41 @@
|
|
1
|
+
from..base import BaseTool
|
2
|
+
from ..config import FunctionRegistry
|
3
|
+
import logging
|
4
|
+
|
5
|
+
logger = logging.getLogger(__name__)
|
6
|
+
|
7
|
+
@FunctionRegistry.register
|
8
|
+
class Division(BaseTool):
|
9
|
+
def __init__(self, name="division"):
|
10
|
+
super().__init__()
|
11
|
+
self.name = name
|
12
|
+
|
13
|
+
@property
|
14
|
+
def definition(self):
|
15
|
+
return {
|
16
|
+
"type": "function",
|
17
|
+
"function": {
|
18
|
+
"name": self.name,
|
19
|
+
"description": "Calculate the quotient of two numbers",
|
20
|
+
"parameters": {
|
21
|
+
"type": "object",
|
22
|
+
"properties": {
|
23
|
+
"num1": {
|
24
|
+
"type": "number",
|
25
|
+
"description": "The dividend"
|
26
|
+
},
|
27
|
+
"num2": {
|
28
|
+
"type": "number",
|
29
|
+
"description": "The divisor"
|
30
|
+
}
|
31
|
+
},
|
32
|
+
"required": ["num1", "num2"]
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
def fn(self, num1, num2):
|
38
|
+
logger.debug(f"Dividing {num1} by {num2}")
|
39
|
+
if num2 == 0:
|
40
|
+
raise ZeroDivisionError("Cannot divide by zero")
|
41
|
+
return num1 / num2
|
@@ -0,0 +1,39 @@
|
|
1
|
+
from..base import BaseTool
|
2
|
+
from ..config import FunctionRegistry
|
3
|
+
import logging
|
4
|
+
|
5
|
+
logger = logging.getLogger(__name__)
|
6
|
+
|
7
|
+
@FunctionRegistry.register
|
8
|
+
class Exponents(BaseTool):
|
9
|
+
def __init__(self, name="exponents"):
|
10
|
+
super().__init__()
|
11
|
+
self.name = name
|
12
|
+
|
13
|
+
@property
|
14
|
+
def definition(self):
|
15
|
+
return {
|
16
|
+
"type": "function",
|
17
|
+
"function": {
|
18
|
+
"name": self.name,
|
19
|
+
"description": "Calculate the result of a number raised to a power",
|
20
|
+
"parameters": {
|
21
|
+
"type": "object",
|
22
|
+
"properties": {
|
23
|
+
"base": {
|
24
|
+
"type": "number",
|
25
|
+
"description": "The base number"
|
26
|
+
},
|
27
|
+
"power": {
|
28
|
+
"type": "number",
|
29
|
+
"description": "The power to which the base is raised"
|
30
|
+
}
|
31
|
+
},
|
32
|
+
"required": ["base", "power"]
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
def fn(self, base, power):
|
38
|
+
logger.debug(f"Raising {base} to the {power}th power")
|
39
|
+
return base ** power
|
@@ -0,0 +1,39 @@
|
|
1
|
+
from..base import BaseTool
|
2
|
+
from ..config import FunctionRegistry
|
3
|
+
import logging
|
4
|
+
|
5
|
+
logger = logging.getLogger(__name__)
|
6
|
+
|
7
|
+
@FunctionRegistry.register
|
8
|
+
class Multiplication(BaseTool):
|
9
|
+
def __init__(self, name="multiplication"):
|
10
|
+
super().__init__()
|
11
|
+
self.name = name
|
12
|
+
|
13
|
+
@property
|
14
|
+
def definition(self):
|
15
|
+
return {
|
16
|
+
"type": "function",
|
17
|
+
"function": {
|
18
|
+
"name": self.name,
|
19
|
+
"description": "Calculate the product of two numbers",
|
20
|
+
"parameters": {
|
21
|
+
"type": "object",
|
22
|
+
"properties": {
|
23
|
+
"num1": {
|
24
|
+
"type": "number",
|
25
|
+
"description": "The first number"
|
26
|
+
},
|
27
|
+
"num2": {
|
28
|
+
"type": "number",
|
29
|
+
"description": "The second number"
|
30
|
+
}
|
31
|
+
},
|
32
|
+
"required": ["num1", "num2"]
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
def fn(self, num1, num2):
|
38
|
+
logger.debug(f"Multiplying {num1} by {num2}")
|
39
|
+
return num1 * num2
|
@@ -0,0 +1,39 @@
|
|
1
|
+
from..base import BaseTool
|
2
|
+
from ..config import FunctionRegistry
|
3
|
+
import logging
|
4
|
+
|
5
|
+
logger = logging.getLogger(__name__)
|
6
|
+
|
7
|
+
@FunctionRegistry.register
|
8
|
+
class Subtraction(BaseTool):
|
9
|
+
def __init__(self, name="subtraction"):
|
10
|
+
super().__init__()
|
11
|
+
self.name = name
|
12
|
+
|
13
|
+
@property
|
14
|
+
def definition(self):
|
15
|
+
return {
|
16
|
+
"type": "function",
|
17
|
+
"function": {
|
18
|
+
"name": self.name,
|
19
|
+
"description": "Calculate the difference between two numbers",
|
20
|
+
"parameters": {
|
21
|
+
"type": "object",
|
22
|
+
"properties": {
|
23
|
+
"num1": {
|
24
|
+
"type": "number",
|
25
|
+
"description": "The minuend"
|
26
|
+
},
|
27
|
+
"num2": {
|
28
|
+
"type": "number",
|
29
|
+
"description": "The subtrahend"
|
30
|
+
}
|
31
|
+
},
|
32
|
+
"required": ["num1", "num2"]
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
def fn(self, num1, num2):
|
38
|
+
logger.debug(f"Subtracting {num2} from {num1}")
|
39
|
+
return num1 - num2
|
gofannon/cli.py
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
import argparse
|
2
|
+
from .orchestration import ToolChain
|
3
|
+
from .base import WorkflowContext
|
4
|
+
from orchestration.firebase_wrapper import FirebaseWrapper
|
5
|
+
import json
|
6
|
+
|
7
|
+
def main():
|
8
|
+
parser = argparse.ArgumentParser(description="Gofannon CLI")
|
9
|
+
parser.add_argument('--local', action='store_true', help='Run locally')
|
10
|
+
parser.add_argument('--firebase', help='Firebase config path')
|
11
|
+
parser.add_argument('--workflow', required=True, help='Workflow config JSON')
|
12
|
+
parser.add_argument('--output', help='Output file path')
|
13
|
+
|
14
|
+
args = parser.parse_args()
|
15
|
+
|
16
|
+
# Initialize context
|
17
|
+
if args.firebase:
|
18
|
+
FirebaseWrapper.initialize(args.firebase)
|
19
|
+
context = FirebaseWrapper.get_context('current_workflow')
|
20
|
+
else:
|
21
|
+
context = WorkflowContext()
|
22
|
+
|
23
|
+
# Load workflow config
|
24
|
+
with open(args.workflow) as f:
|
25
|
+
workflow_config = json.load(f)
|
26
|
+
|
27
|
+
# Import and instantiate tools
|
28
|
+
tools = []
|
29
|
+
for tool_config in workflow_config['tools']:
|
30
|
+
module = __import__(f".{tool_config['module']}", fromlist=[tool_config['class']])
|
31
|
+
tool_class = getattr(module, tool_config['class'])
|
32
|
+
tool = tool_class(**tool_config.get('params', {}))
|
33
|
+
tools.append(tool)
|
34
|
+
|
35
|
+
# Execute workflow
|
36
|
+
chain = ToolChain(tools, context)
|
37
|
+
result = chain.execute(workflow_config.get('initial_input', {}))
|
38
|
+
|
39
|
+
# Handle output
|
40
|
+
if args.output:
|
41
|
+
with open(args.output, 'w') as f:
|
42
|
+
json.dump(result.output, f)
|
43
|
+
else:
|
44
|
+
print(json.dumps(result.output, indent=2))
|
45
|
+
|
46
|
+
# Save final state
|
47
|
+
if args.firebase:
|
48
|
+
FirebaseWrapper.save_context('current_workflow', context)
|
49
|
+
else:
|
50
|
+
context.save_checkpoint('final')
|
51
|
+
|
52
|
+
if __name__ == '__main__':
|
53
|
+
main()
|