gofannon 0.25.10.1__py3-none-any.whl → 0.25.12__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
@@ -9,6 +9,8 @@ from ..config import ToolConfig
9
9
 
10
10
  from .smol_agents import SmolAgentsMixin
11
11
  from .langchain import LangchainMixin
12
+ from .bedrock import BedrockMixin
13
+
12
14
 
13
15
  @dataclass
14
16
  class ToolResult:
@@ -17,6 +19,7 @@ class ToolResult:
17
19
  error: str = None
18
20
  retryable: bool = False
19
21
 
22
+
20
23
  class WorkflowContext:
21
24
  def __init__(self, firebase_config=None):
22
25
  self.data = {}
@@ -33,34 +36,37 @@ class WorkflowContext:
33
36
 
34
37
  def _save_local(self, name):
35
38
  path = self.local_storage / f"{name}.json"
36
- with open(path, 'w') as f:
37
- json.dump({
38
- 'data': self.data,
39
- 'execution_log': self.execution_log
40
- }, f)
39
+ with open(path, "w") as f:
40
+ json.dump({"data": self.data, "execution_log": self.execution_log}, f)
41
41
 
42
42
  def _save_to_firebase(self, name):
43
43
  from firebase_admin import firestore
44
+
44
45
  db = firestore.client()
45
- doc_ref = db.collection('checkpoints').document(name)
46
- doc_ref.set({
47
- 'data': self.data,
48
- 'execution_log': self.execution_log,
49
- 'timestamp': firestore.SERVER_TIMESTAMP
50
- })
46
+ doc_ref = db.collection("checkpoints").document(name)
47
+ doc_ref.set(
48
+ {
49
+ "data": self.data,
50
+ "execution_log": self.execution_log,
51
+ "timestamp": firestore.SERVER_TIMESTAMP,
52
+ }
53
+ )
51
54
 
52
55
  def log_execution(self, tool_name, duration, input_data, output_data):
53
56
  entry = {
54
- 'tool': tool_name,
55
- 'duration': duration,
56
- 'input': input_data,
57
- 'output': output_data
57
+ "tool": tool_name,
58
+ "duration": duration,
59
+ "input": input_data,
60
+ "output": output_data,
58
61
  }
59
62
  self.execution_log.append(entry)
60
63
 
61
- class BaseTool(SmolAgentsMixin, LangchainMixin, ABC):
64
+
65
+ class BaseTool(SmolAgentsMixin, LangchainMixin, BedrockMixin, ABC):
62
66
  def __init__(self, **kwargs):
63
- self.logger = logging.getLogger(f"{self.__class__.__module__}.{self.__class__.__name__}")
67
+ self.logger = logging.getLogger(
68
+ f"{self.__class__.__module__}.{self.__class__.__name__}"
69
+ )
64
70
  self._load_config()
65
71
  self._configure(**kwargs)
66
72
  self.logger.debug("Initialized %s tool", self.__class__.__name__)
@@ -73,7 +79,7 @@ class BaseTool(SmolAgentsMixin, LangchainMixin, ABC):
73
79
 
74
80
  def _load_config(self):
75
81
  """Auto-load config based on tool type"""
76
- if hasattr(self, 'API_SERVICE'):
82
+ if hasattr(self, "API_SERVICE"):
77
83
  self.api_key = ToolConfig.get(f"{self.API_SERVICE}_api_key")
78
84
 
79
85
  @property
@@ -83,7 +89,7 @@ class BaseTool(SmolAgentsMixin, LangchainMixin, ABC):
83
89
 
84
90
  @property
85
91
  def output_schema(self):
86
- return self.definition.get('function', {}).get('parameters', {})
92
+ return self.definition.get("function", {}).get("parameters", {})
87
93
 
88
94
  @abstractmethod
89
95
  def fn(self, *args, **kwargs):
@@ -99,14 +105,9 @@ class BaseTool(SmolAgentsMixin, LangchainMixin, ABC):
99
105
  tool_name=self.__class__.__name__,
100
106
  duration=duration,
101
107
  input_data=kwargs,
102
- output_data=result
108
+ output_data=result,
103
109
  )
104
110
 
105
111
  return ToolResult(success=True, output=result)
106
112
  except Exception as e:
107
- return ToolResult(
108
- success=False,
109
- output=None,
110
- error=str(e),
111
- retryable=True
112
- )
113
+ return ToolResult(success=False, output=None, error=str(e), retryable=True)
@@ -0,0 +1,666 @@
1
+ from typing import Type, Callable
2
+ import subprocess
3
+ import shutil
4
+ import zipfile
5
+ import json
6
+ import time
7
+
8
+ try:
9
+ import boto3
10
+ from botocore.exceptions import ClientError
11
+ from botocore.client import BaseClient
12
+
13
+ _HAS_BOTO3 = True
14
+ except ImportError:
15
+ _HAS_BOTO3 = False
16
+
17
+ try:
18
+ from jsonschema import validate
19
+ import jsonschema.exceptions
20
+
21
+ _HAS_JSONSCHEMA = True
22
+ except ImportError:
23
+ _HAS_JSONSCHEMA = False
24
+
25
+
26
+ class BedrockMixin:
27
+
28
+ def export_to_bedrock(self, agent_app_config: dict = None) -> dict:
29
+ """
30
+ Export tool as Bedrock Agent tool configuration
31
+ """
32
+ if not _HAS_BOTO3:
33
+ error = "boto3 not installed. Install with `pip install boto3`"
34
+ self.logger.error(error)
35
+ raise RuntimeError(error)
36
+
37
+ if not _HAS_JSONSCHEMA:
38
+ error = "jasonschema not installed. Install with `pip install jsonschema`"
39
+ self.logger.error(error)
40
+ raise RuntimeError(error)
41
+
42
+ valid_agent_app_config_schema = {
43
+ "type": "object",
44
+ "properties": {
45
+ "app_id": {"type": "string"},
46
+ "agent_session_timeout": {"type": "integer"},
47
+ "instruction": {"type": "string"},
48
+ "agent_description": {"type": "string"},
49
+ "target_model": {"type": "string"},
50
+ "python_runtime_version": {"type": "string"},
51
+ "temp_build_root": {"type": "string"},
52
+ },
53
+ "required": [
54
+ "app_id",
55
+ "agent_session_timeout",
56
+ "instruction",
57
+ "agent_description",
58
+ "target_model",
59
+ "python_runtime_version",
60
+ "temp_build_root",
61
+ ],
62
+ }
63
+ try:
64
+ validate(agent_app_config, valid_agent_app_config_schema)
65
+ except jsonschema.exceptions.ValidationError as err:
66
+ error = f"JSON validation failure on input parameters: {err}"
67
+ self.logger.error(error)
68
+ raise RuntimeError(error)
69
+
70
+ self.bedrock_agent_client = boto3.client("bedrock-agent")
71
+ self.app_id = agent_app_config["app_id"]
72
+ self.agent_name = agent_app_config["agent_name"]
73
+ self.agent_session_timeout = agent_app_config["agent_session_timeout"]
74
+ self.agent_instruction = agent_app_config["instruction"]
75
+ self.agent_description = agent_app_config["agent_description"]
76
+ self.agent_target_model = agent_app_config["target_model"]
77
+ self.python_runtime_version = agent_app_config["python_runtime_version"]
78
+ self.temp_build_root = agent_app_config["temp_build_root"]
79
+ try:
80
+ self.aws_account_id = (
81
+ boto3.client("sts").get_caller_identity().get("Account")
82
+ )
83
+ except ClientError as e:
84
+ error = f"Error getting AWS account number. Client error: {e}"
85
+ self.logger.error(error)
86
+ raise RuntimeError(error)
87
+ except Exception as e:
88
+ error = f"Error getting AWS account number. Unexpected error: {e}"
89
+ self.logger.error(error)
90
+ raise RuntimeError(error)
91
+
92
+ self.openapi_schema_dict = self._generate_openapi_schema()
93
+
94
+ self.logger.info(
95
+ f"Starting build of app {self.app_id}...", self.__class__.__name__
96
+ )
97
+ # Creates:
98
+ # the lambda - a .zip archive that contains all the dependencies and the lambda source.
99
+ # This lambda will have a resource-based policy attached giving it
100
+ # permission to be invoked by the agent attached.
101
+ # an IAM role - the lambda execution role with managed attached policy
102
+ # AWSLambdaBasicExecutionRole allowing the lambda to use CloudWatch
103
+
104
+ self.logger.info("\tCreating lambda...", self.__class__.__name__)
105
+
106
+ self.lambda_arn = self._create_bedrock_lambda()
107
+
108
+ # Creates:
109
+ # the Bedrock agent - The bedrock agent itself
110
+ # an IAM role - The agent execution role
111
+ # An IAM policy - a policy to attach to the role allowing the agent to invoke the
112
+ # - foundational model
113
+ self.logger.info("\tCreating agent...", self.__class__.__name__)
114
+ self.agent_id = self._create_bedrock_agent()
115
+
116
+ # Creates:
117
+ # the Bedrock agent action group - Defined with an API schema
118
+ self.logger.info("\tCreating agent action group...", self.__class__.__name__)
119
+ action_group = self._create_agent_action_group()
120
+
121
+ # Finally, prepare the agent
122
+ self.logger.info("\tPreparing agent...", self.__class__.__name__)
123
+ try:
124
+ response = self.bedrock_agent_client.prepare_agent(agentId=self.agent_id)
125
+ except ClientError as e:
126
+ error = f"Error preparing agent. Client error: {e}"
127
+ self.logger.error(error)
128
+ raise RuntimeError(error)
129
+ except Exception as e:
130
+ error = f"Error preparing agent. Unexpected error: {e}"
131
+ self.logger.error(error)
132
+ raise RuntimeError(error)
133
+
134
+ output_manifest = {
135
+ "lambdaARN": self.lambda_arn,
136
+ "lambdaRoleName": self.lambda_role_name,
137
+ "agentId": self.agent_id,
138
+ "agentRoleName": self.agent_role_name,
139
+ "agentPolicyARN": self.agent_role_policy_arn,
140
+ "agentActionGroup": self.agent_action_group_id,
141
+ }
142
+ self.logger.info("Done!", self.__class__.__name__)
143
+ return output_manifest
144
+
145
+ def _generate_openapi_schema(self) -> dict:
146
+ """Convert Gofannon definition to OpenAPI schema"""
147
+ params = self.definition["function"]["parameters"]
148
+ if not "properties" in params:
149
+ params["properties"] = {}
150
+ openapi_schema = {
151
+ "openapi": "3.0.0",
152
+ "info": {"title": self.name, "version": "1.0.0"},
153
+ "paths": {
154
+ f"/{self.name}": {
155
+ "get": {
156
+ "description": self.definition["function"]["description"],
157
+ "requestBody": {
158
+ "content": {
159
+ "application/json": {
160
+ "schema": {
161
+ "type": "object",
162
+ "properties": {
163
+ param: {
164
+ "type": props["type"],
165
+ "description": props["description"],
166
+ }
167
+ for param, props in params[
168
+ "properties"
169
+ ].items()
170
+ },
171
+ "required": params.get("required", []),
172
+ }
173
+ }
174
+ }
175
+ },
176
+ "responses": {
177
+ "200": {
178
+ "description": "Successful operation",
179
+ "content": {
180
+ "application/json": {
181
+ "schema": {
182
+ "type": "object",
183
+ "properties": {
184
+ "result": {"type": "string"}
185
+ },
186
+ }
187
+ }
188
+ },
189
+ }
190
+ },
191
+ }
192
+ }
193
+ },
194
+ }
195
+
196
+ return openapi_schema
197
+
198
+ def _create_bedrock_lambda(self) -> str:
199
+ """Create Lambda function for Bedrock integration"""
200
+
201
+ lambda_client = boto3.client("lambda")
202
+ self.lambda_role_arn = self._create_bedrock_lambda_role()
203
+
204
+ # Build the lambda .zip deployment package here. Package consits of:
205
+ # 1. All the Python dependencies, including Gofannon itself
206
+ # 2. The lambda source code.
207
+ # bash code as comments at the bottom of this file.
208
+ # See: https://docs.aws.amazon.com/lambda/latest/dg/python-package.html
209
+
210
+ # TODO: verify the path here
211
+ command = f"""../../scripts/build_lambda.sh {self.temp_build_root} {self.python_runtime_version.replace("python","")} {self.app_id}"""
212
+ try:
213
+ process = subprocess.Popen(
214
+ command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
215
+ )
216
+ stdout, stderr = process.communicate()
217
+
218
+ if process.returncode == 0:
219
+ self.built_archive = stdout.decode("utf-8").replace("\n", "")
220
+ else:
221
+ raise RuntimeError(stderr.decode("utf-8"))
222
+
223
+ except Exception as e:
224
+ error = f"Error creating .zip archive. {e}"
225
+ self.logger.error(error)
226
+ raise RuntimeError(error)
227
+
228
+ # TODO: Skeleton code in the function directory or here?
229
+ lambda_src_string = self._get_lambda_source(
230
+ module_name=self.__class__.__module__, class_name=self.__class__.__name__
231
+ )
232
+
233
+ try:
234
+ with open(f"{self.temp_build_root}/lambda_function.py", "w") as text_file:
235
+ text_file.write(lambda_src_string)
236
+ except Exception as e:
237
+ error = f"Error writing lambda Python source file: {e}"
238
+ self.logger.error(error)
239
+ raise RuntimeError(error)
240
+
241
+ try:
242
+ with zipfile.ZipFile(
243
+ self.built_archive,
244
+ "a",
245
+ compression=zipfile.ZIP_DEFLATED,
246
+ ) as zipf:
247
+ zipf.write(
248
+ f"{self.temp_build_root}/lambda_function.py", "lambda_function.py"
249
+ )
250
+ except Exception as e:
251
+ error = f"Error inserting lambda Python source file into zip: {e}"
252
+ self.logger.error(error)
253
+ raise RuntimeError(error)
254
+
255
+ with open(self.built_archive, "rb") as f:
256
+ zipped_code = f.read()
257
+
258
+ try:
259
+ response = lambda_client.create_function(
260
+ FunctionName=f"{self.app_id}_{self.name}",
261
+ Runtime=self.python_runtime_version,
262
+ Role=self.lambda_role_arn,
263
+ Handler="lambda_function.lambda_handler",
264
+ Code={"ZipFile": zipped_code},
265
+ Description=f"{self.app_id} tool: {self.name}",
266
+ Timeout=30,
267
+ MemorySize=256,
268
+ )
269
+ except ClientError as e:
270
+ error = f"Error creating lambda. Client error: {e}"
271
+ self.logger.error(error)
272
+ raise RuntimeError(error)
273
+
274
+ except Exception as e:
275
+ error = f"Error creating lambda. Unexpected error: {e}"
276
+ self.logger.error(error)
277
+ raise RuntimeError(error)
278
+
279
+ # Clean up the .zip archive build
280
+ try:
281
+ shutil.rmtree(self.temp_build_root)
282
+ except OSError as e:
283
+ error = (
284
+ f"Error deleting temporary build directory {self.temp_build_root}: {e}"
285
+ )
286
+ self.logger.error(error)
287
+ pass
288
+
289
+ # Add the resourced-base policy allowing the lambda to be invoked
290
+ # by the agent.
291
+ try:
292
+ add_permission_response = lambda_client.add_permission(
293
+ StatementId=f"{self.app_id}-bedrock-invoke",
294
+ FunctionName=response["FunctionArn"],
295
+ Action="lambda:InvokeFunction",
296
+ Principal="bedrock.amazonaws.com",
297
+ # TODO: fix this. Remove wildcard
298
+ SourceArn=f"arn:aws:bedrock:us-east-1:{self.aws_account_id}:agent/*",
299
+ )
300
+ except ClientError as e:
301
+ error = f"Error attaching resource policy to lambda execution role. Client error: {e}"
302
+ self.logger.error(error)
303
+ raise RuntimeError(error)
304
+ except Exception as e:
305
+ error = f"Error attaching resource policy to lambda execution role. Unexpected error: {e}"
306
+ self.logger.error(error)
307
+ raise RuntimeError(error)
308
+
309
+ return response["FunctionArn"]
310
+
311
+ def _create_bedrock_lambda_role(self) -> str:
312
+ """Get or create IAM role for Bedrock integration"""
313
+ iam = boto3.client("iam")
314
+
315
+ self.lambda_role_name = f"{self.app_id}_bedrock_lambda_execution_role"
316
+ self.assume_role = self._get_assumed_role(service="lambda.amazonaws.com")
317
+
318
+ try:
319
+ iam.create_role(
320
+ RoleName=self.lambda_role_name,
321
+ AssumeRolePolicyDocument=json.dumps(self.assume_role),
322
+ )
323
+ except ClientError as e:
324
+ error = f"Error creating lambda execution role. Client error: {e}"
325
+ self.logger.error(error)
326
+ raise RuntimeError(error)
327
+ except Exception as e:
328
+ error = f"Error creating lambda execution role. Unexpected error: {e}"
329
+ self.logger.error(error)
330
+ raise RuntimeError(error)
331
+
332
+ try:
333
+ iam.attach_role_policy(
334
+ RoleName=self.lambda_role_name,
335
+ PolicyArn="arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole",
336
+ )
337
+ except ClientError as e:
338
+ error = f"Error attaching managed policy to lambda execution role. Client error: {e}"
339
+ self.logger.error(error)
340
+ raise RuntimeError(error)
341
+
342
+ except Exception as e:
343
+ error = f"Error attaching managed policy to lambda execution role. Unexpected error: {e}"
344
+ self.logger.error(error)
345
+ raise RuntimeError(error)
346
+
347
+ return iam.get_role(RoleName=self.lambda_role_name)["Role"]["Arn"]
348
+
349
+ def _create_bedrock_agent(self) -> str:
350
+ """Create Bedrock agent"""
351
+
352
+ self.agent_role_arn = self._create_agent_role()
353
+
354
+ args = {
355
+ "agentName": f"{self.app_id}_{self.agent_name}Agent",
356
+ "agentResourceRoleArn": self.agent_role_arn,
357
+ "foundationModel": self.agent_target_model,
358
+ "idleSessionTTLInSeconds": self.agent_session_timeout,
359
+ "instruction": self.agent_instruction,
360
+ "description": self.agent_description,
361
+ }
362
+
363
+ try:
364
+ response = self.bedrock_agent_client.create_agent(**args)
365
+ except ClientError as e:
366
+ error = f"Error creating agent. Client error: {e}"
367
+ self.logger.error(error)
368
+ raise RuntimeError(error)
369
+
370
+ except Exception as e:
371
+ error = f"Error creating agent. Unexpected error: {e}"
372
+ self.logger.error(error)
373
+ raise RuntimeError(error)
374
+
375
+ return response["agent"]["agentId"]
376
+
377
+ def _create_agent_action_group(self):
378
+ """Create Bedrock action group and attach to Bedrock agent"""
379
+
380
+ args = {
381
+ "agentId": self.agent_id,
382
+ "actionGroupExecutor": {
383
+ "lambda": self.lambda_arn,
384
+ },
385
+ "actionGroupName": f"{self.app_id}_{self.agent_name}_ag",
386
+ "agentVersion": "DRAFT",
387
+ "apiSchema": {"payload": json.dumps(self.openapi_schema_dict)},
388
+ "description": self.agent_description,
389
+ }
390
+
391
+ # Wait until agent status transitions to CREATING before creating the action group.
392
+ counter = 0
393
+ agent_details = self.bedrock_agent_client.get_agent(agentId=self.agent_id)
394
+ agent_status = agent_details["agent"]["agentStatus"]
395
+ while (agent_status == "CREATING") and (counter <= 9):
396
+ counter = counter + 1
397
+ time.sleep(1)
398
+ agent_details = self.bedrock_agent_client.get_agent(agentId=self.agent_id)
399
+ agent_status = agent_details["agent"]["agentStatus"]
400
+
401
+ if counter == 10:
402
+ error = f"Error creating agent timeout. Agent created, but status never transitioned out of CREATING"
403
+ self.logger.error(error)
404
+ raise RuntimeError(error)
405
+
406
+ try:
407
+ agent_action_group = self.bedrock_agent_client.create_agent_action_group(
408
+ **args
409
+ )
410
+ self.agent_action_group_id = agent_action_group["agentActionGroup"][
411
+ "actionGroupId"
412
+ ]
413
+ except ClientError as e:
414
+ error = f"Error creating agent action group. Client error: {e}"
415
+ self.logger.error(error)
416
+ raise RuntimeError(error)
417
+ except Exception as e:
418
+ error = f"Error creating agent action group. Unexpected error: {e}"
419
+ self.logger.error(error)
420
+ raise RuntimeError(error)
421
+
422
+ return agent_action_group
423
+
424
+ def _create_agent_role(self) -> str:
425
+ """Create IAM agent role for Bedrock integration"""
426
+
427
+ iam = boto3.client("iam")
428
+
429
+ self.agent_role_name = f"{self.app_id}_bedrock_agent_execution_role"
430
+ self.agent_policy_name = f"{self.app_id}_bedrock_agent_allow_model"
431
+
432
+ assume_role = self._get_assumed_role(service="bedrock.amazonaws.com")
433
+
434
+ try:
435
+ iam.create_role(
436
+ RoleName=self.agent_role_name,
437
+ AssumeRolePolicyDocument=json.dumps(assume_role),
438
+ )
439
+ except ClientError as e:
440
+ error = f"Error creating agent role. Client error: {e}"
441
+ self.logger.error(error)
442
+ raise RuntimeError(error)
443
+ except Exception as e:
444
+ error = f"Error creating agent role. Unexpected error: {e}"
445
+ self.logger.error(error)
446
+ raise RuntimeError(error)
447
+
448
+ managed_policy_txt = self._get_managed_policy(
449
+ resource=f"foundation-model/{self.agent_target_model}"
450
+ )
451
+
452
+ try:
453
+ managed_policy = iam.create_policy(
454
+ PolicyName=self.agent_policy_name,
455
+ PolicyDocument=json.dumps(managed_policy_txt),
456
+ )
457
+ except ClientError as e:
458
+ error = f"Error creating agent policy. Client error: {e}"
459
+ self.logger.error(error)
460
+ raise RuntimeError(error)
461
+ except Exception as e:
462
+ error = f"Error creating agent policy. Unexpected error: {e}"
463
+ self.logger.error(error)
464
+ raise RuntimeError(error)
465
+
466
+ self.agent_role_policy_arn = (
467
+ f"arn:aws:iam::{self.aws_account_id}:policy/{self.agent_policy_name}"
468
+ )
469
+ try:
470
+ iam.attach_role_policy(
471
+ RoleName=self.agent_role_name,
472
+ PolicyArn=self.agent_role_policy_arn,
473
+ )
474
+ except ClientError as e:
475
+ error = f"Error creating attaching policy to agent role. Client error: {e}"
476
+ self.logger.error(error)
477
+ raise RuntimeError(error)
478
+ except Exception as e:
479
+ error = (
480
+ f"Error creating attaching policy to agent role. Unexpected error: {e}"
481
+ )
482
+ self.logger.error(error)
483
+ raise RuntimeError(error)
484
+
485
+ return iam.get_role(RoleName=self.agent_role_name)["Role"]["Arn"]
486
+
487
+ def _get_assumed_role(self, service: str = None) -> str:
488
+ """Skeleton code for creating a role"""
489
+ role = {
490
+ "Version": "2012-10-17",
491
+ "Statement": [
492
+ {
493
+ "Effect": "Allow",
494
+ "Principal": {"Service": f"{service}"},
495
+ "Action": "sts:AssumeRole",
496
+ }
497
+ ],
498
+ }
499
+ return role
500
+
501
+ def _get_managed_policy(self, resource: str = None) -> str:
502
+ """Skeleton code for creating a policy"""
503
+ managed_policy_txt = {
504
+ "Version": "2012-10-17",
505
+ "Statement": [
506
+ {
507
+ "Action": "bedrock:InvokeModel",
508
+ "Resource": f"arn:aws:bedrock:us-east-1::{resource}",
509
+ "Effect": "Allow",
510
+ }
511
+ ],
512
+ }
513
+
514
+ return managed_policy_txt
515
+
516
+ def _get_lambda_source(
517
+ self, module_name: str = None, class_name: str = None
518
+ ) -> str:
519
+ """Bedrock wrapped lambda used by the agent"""
520
+
521
+ # funky spacing below is required...
522
+ lambda_src = f"""
523
+ import json
524
+ from {module_name} import {class_name}
525
+
526
+
527
+ def lambda_handler(event, context):
528
+ tool = {class_name}()
529
+ result = tool.fn()
530
+ action_response = {{
531
+ "actionGroup": event["actionGroup"],
532
+ "apiPath": event["apiPath"],
533
+ "httpMethod": event["httpMethod"],
534
+ "httpStatusCode": 200,
535
+ "responseBody": {{"application/json": {{"body": json.dumps(result)}}}},
536
+ }}
537
+ api_response = {{"messageVersion": "1.0", "response": action_response}}
538
+
539
+ return api_response"""
540
+ return lambda_src
541
+
542
+ def delete_app(self, bedrock_config: dict = None) -> bool:
543
+ """Tear down the boto3 stack created by .export_to_bedrock()"""
544
+
545
+ valid_bedrock_config_schema = {
546
+ "type": "object",
547
+ "properties": {
548
+ "lambdaARN": {"type": "string"},
549
+ "lambdaRoleName": {"type": "string"},
550
+ "agentId": {"type": "string"},
551
+ "agentRoleName": {"type": "string"},
552
+ "agentPolicyARN": {"type": "string"},
553
+ "agentActionGroup": {"type": "string"},
554
+ },
555
+ "required": [
556
+ "lambdaARN",
557
+ "lambdaRoleName",
558
+ "agentId",
559
+ "agentRoleName",
560
+ "agentPolicyARN",
561
+ "agentActionGroup",
562
+ ],
563
+ }
564
+ try:
565
+ validate(bedrock_config, valid_bedrock_config_schema)
566
+ except jsonschema.exceptions.ValidationError as err:
567
+ error = f"JSON validation failure on input bedrock_config: {err}"
568
+ self.logger.error(error)
569
+ raise RuntimeError(error)
570
+
571
+ self.lambda_client = boto3.client("lambda")
572
+ self.iam_client = boto3.client("iam")
573
+ self.bedrock_agent_client = boto3.client("bedrock-agent")
574
+
575
+ # Order is important here...
576
+ try:
577
+ detach_agent_policy = self.iam_client.detach_role_policy(
578
+ RoleName=bedrock_config["agentRoleName"],
579
+ PolicyArn=bedrock_config["agentPolicyARN"],
580
+ )
581
+ except ClientError as e:
582
+ error = f"Error detaching agent policy. Client error: {e}"
583
+ self.logger.error(error)
584
+ pass
585
+ except Exception as e:
586
+ error = f"Error detaching agent policy. Unexpected error: {e}"
587
+ self.logger.error(error)
588
+ pass
589
+ try:
590
+ delete_agent_role = self.iam_client.delete_role(
591
+ RoleName=bedrock_config["agentRoleName"]
592
+ )
593
+ except ClientError as e:
594
+ error = f"Error deleting agent role. Client error: {e}"
595
+ self.logger.error(error)
596
+ pass
597
+ except Exception as e:
598
+ error = f"Error deleting agent role. Unexpected error: {e}"
599
+ self.logger.error(error)
600
+ pass
601
+
602
+ try:
603
+ delete_agent_policy = self.iam_client.delete_policy(
604
+ PolicyArn=bedrock_config["agentPolicyARN"]
605
+ )
606
+ except ClientError as e:
607
+ error = f"Error deleting agent policy. Client error: {e}"
608
+ self.logger.error(error)
609
+ pass
610
+ except Exception as e:
611
+ error = f"Error deleting agent policy. Unexpected error: {e}"
612
+ self.logger.error(error)
613
+ pass
614
+
615
+ try:
616
+ delete_agent = self.bedrock_agent_client.delete_agent(
617
+ agentId=bedrock_config["agentId"], skipResourceInUseCheck=True
618
+ )
619
+ except ClientError as e:
620
+ error = f"Error deleting agent. Client error: {e}"
621
+ self.logger.error(error)
622
+ pass
623
+ except Exception as e:
624
+ error = f"Error deleting agent. Unexpected error: {e}"
625
+ self.logger.error(error)
626
+ pass
627
+
628
+ try:
629
+ detach_lambda_policy = self.iam_client.detach_role_policy(
630
+ RoleName=bedrock_config["lambdaRoleName"],
631
+ PolicyArn="arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole",
632
+ )
633
+ except ClientError as e:
634
+ error = f"Error detaching lambda policy. Client error: {e}"
635
+ self.logger.error(error)
636
+ pass
637
+ except Exception as e:
638
+ error = f"Error detaching lambda policy. Unexpected error: {e}"
639
+ self.logger.error(error)
640
+ pass
641
+
642
+ try:
643
+ delete_lambda_role = self.iam_client.delete_role(
644
+ RoleName=bedrock_config["lambdaRoleName"]
645
+ )
646
+ except ClientError as e:
647
+ error = f"Error deleting lambda role. Client error: {e}"
648
+ self.logger.error(error)
649
+ pass
650
+ except Exception as e:
651
+ error = f"Error deleting lambda role. Unexpected error: {e}"
652
+ self.logger.error(error)
653
+ pass
654
+
655
+ try:
656
+ delete_lambda = self.lambda_client.delete_function(
657
+ FunctionName=bedrock_config["lambdaARN"]
658
+ )
659
+ except ClientError as e:
660
+ error = f"Error deleting lambda. Client error: {e}"
661
+ self.logger.error(error)
662
+ pass
663
+ except Exception as e:
664
+ error = f"Error deleting lambda. Unexpected error: {e}"
665
+ self.logger.error(error)
666
+ pass
@@ -3,20 +3,30 @@ from typing import Type, Callable
3
3
  try:
4
4
  from langchain.tools import BaseTool as LangchainBaseTool
5
5
  from langchain.pydantic_v1 import BaseModel, Field
6
+
6
7
  _HAS_LANGCHAIN = True
7
8
  except ImportError:
8
9
  _HAS_LANGCHAIN = False
9
10
 
11
+
10
12
  class LangchainMixin:
11
- def import_from_langchain(self, langchain_tool: LangchainBaseTool):
13
+ def import_from_langchain(self, langchain_tool):
12
14
  if not _HAS_LANGCHAIN:
13
- raise RuntimeError("langchain is not installed. Install with `pip install langchain-core`")
15
+ raise RuntimeError(
16
+ "langchain is not installed. Install with `pip install langchain-core`"
17
+ )
14
18
 
15
19
  self.name = getattr(langchain_tool, "name", "exported_langchain_tool")
16
- self.description = getattr(langchain_tool, "description", "No description provided.")
20
+ self.description = getattr(
21
+ langchain_tool, "description", "No description provided."
22
+ )
17
23
 
18
24
  maybe_args_schema = getattr(langchain_tool, "args_schema", None)
19
- if maybe_args_schema and hasattr(maybe_args_schema, "schema") and callable(maybe_args_schema.schema):
25
+ if (
26
+ maybe_args_schema
27
+ and hasattr(maybe_args_schema, "schema")
28
+ and callable(maybe_args_schema.schema)
29
+ ):
20
30
  args_schema = maybe_args_schema.schema()
21
31
  else:
22
32
  args_schema = {}
@@ -29,7 +39,7 @@ class LangchainMixin:
29
39
 
30
40
  self.fn = adapted_fn
31
41
 
32
- def export_to_langchain(self) -> LangchainBaseTool:
42
+ def export_to_langchain(self):
33
43
  if not _HAS_LANGCHAIN:
34
44
  raise RuntimeError(
35
45
  "langchain is not installed. Install with `pip install langchain-core`"
@@ -43,7 +53,7 @@ class LangchainMixin:
43
53
  "integer": int,
44
54
  "boolean": bool,
45
55
  "object": dict,
46
- "array": list
56
+ "array": list,
47
57
  }
48
58
 
49
59
  parameters = self.definition.get("function", {}).get("parameters", {})
@@ -55,14 +65,16 @@ class LangchainMixin:
55
65
  description = param_def.get("description", "")
56
66
  fields[param_name] = (
57
67
  type_map.get(param_type, str),
58
- Field(..., description=description)
68
+ Field(..., description=description),
59
69
  )
60
70
 
61
- ArgsSchema = create_model('ArgsSchema', **fields)
71
+ ArgsSchema = create_model("ArgsSchema", **fields)
62
72
 
63
73
  class ExportedTool(LangchainBaseTool):
64
74
  name: str = self.definition.get("function", {}).get("name", "")
65
- description: str = self.definition.get("function", {}).get("description", "")
75
+ description: str = self.definition.get("function", {}).get(
76
+ "description", ""
77
+ )
66
78
  args_schema: Type[BaseModel] = ArgsSchema
67
79
  fn: Callable = self.fn
68
80
 
@@ -70,4 +82,4 @@ class LangchainMixin:
70
82
  return self.fn(*args, **kwargs)
71
83
 
72
84
  tool = ExportedTool()
73
- return tool
85
+ return tool
@@ -1,12 +1,14 @@
1
1
  try:
2
2
  from smolagents.tools import Tool as SmolTool
3
3
  from smolagents.tools import tool as smol_tool_decorator
4
+
4
5
  _HAS_SMOLAGENTS = True
5
6
  except ImportError:
6
7
  _HAS_SMOLAGENTS = False
7
8
 
9
+
8
10
  class SmolAgentsMixin:
9
- def import_from_smolagents(self, smol_tool: SmolTool):
11
+ def import_from_smolagents(self, smol_tool):
10
12
  if not _HAS_SMOLAGENTS:
11
13
  raise RuntimeError(
12
14
  "smolagents is not installed or could not be imported. "
@@ -20,7 +22,7 @@ class SmolAgentsMixin:
20
22
 
21
23
  self.fn = adapted_fn
22
24
 
23
- def export_to_smolagents(self) -> SmolTool:
25
+ def export_to_smolagents(self):
24
26
  if not _HAS_SMOLAGENTS:
25
27
  raise RuntimeError(
26
28
  "smolagents is not installed or could not be imported. "
@@ -33,7 +35,7 @@ class SmolAgentsMixin:
33
35
  inputs_definition = {
34
36
  "example_arg": {
35
37
  "type": "string",
36
- "description": "Example argument recognized by this tool"
38
+ "description": "Example argument recognized by this tool",
37
39
  }
38
40
  }
39
41
  output_type = "string"
@@ -46,4 +48,4 @@ class SmolAgentsMixin:
46
48
  exported_tool.forward = smol_forward
47
49
  exported_tool.is_initialized = True
48
50
 
49
- return exported_tool
51
+ return exported_tool
@@ -0,0 +1,104 @@
1
+ import os
2
+ import json
3
+ import logging
4
+ import importlib.util
5
+ from github import Github
6
+ from openai import OpenAI
7
+ from gofannon.config import FunctionRegistry
8
+ from gofannon.base import BaseTool
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+ def load_review_checks():
13
+ """
14
+ Dynamically load the review checks module from a configurable file.
15
+ The path is specified via the environment variable PR_REVIEW_CHECKS_PATH.
16
+ If not defined, it defaults to ".github/scripts/pr_review_checks.py".
17
+ Returns a list of check classes (having names ending with 'Check').
18
+ """
19
+ checks_path = os.getenv("PR_REVIEW_CHECKS_PATH", ".github/scripts/pr_review_checks.py")
20
+ spec = importlib.util.spec_from_file_location("pr_review_checks", checks_path)
21
+ module = importlib.util.module_from_spec(spec)
22
+ spec.loader.exec_module(module)
23
+ # Collect all classes in the module with names ending in 'Check'
24
+ checks = [cls for name, cls in module.__dict__.items() if name.endswith("Check") and isinstance(cls, type)]
25
+ return checks
26
+
27
+ @FunctionRegistry.register
28
+ class PRReviewTool(BaseTool):
29
+ def __init__(self, name="pr_review_tool"):
30
+ super().__init__()
31
+ self.name = name
32
+ self.api_key = os.getenv("OPENAI_API_KEY")
33
+ self.base_url = os.getenv("OPENAI_BASE_URL")
34
+ self.model_name = os.getenv("OPENAI_MODEL_NAME")
35
+ self.client = OpenAI(api_key=self.api_key, base_url=self.base_url)
36
+
37
+ @property
38
+ def definition(self):
39
+ return {
40
+ "type": "function",
41
+ "function": {
42
+ "name": self.name,
43
+ "description": "Perform an automated pull request review using gofannon tools. "
44
+ "It aggregates configurable checks (e.g. code quality and schema validation).",
45
+ "parameters": {
46
+ "type": "object",
47
+ "properties": {
48
+ "pr_number": {
49
+ "type": "integer",
50
+ "description": "The pull request number."
51
+ },
52
+ "repo_name": {
53
+ "type": "string",
54
+ "description": "The repository name in the format owner/repo."
55
+ }
56
+ },
57
+ "required": ["pr_number", "repo_name"]
58
+ }
59
+ }
60
+ }
61
+
62
+ def fn(self, pr_number, repo_name):
63
+ # Connect to GitHub and get pull request details.
64
+ g = Github(os.getenv("GITHUB_TOKEN"))
65
+ repo = g.get_repo(repo_name)
66
+ pr = repo.get_pull(pr_number)
67
+ all_comments = []
68
+ check_results = {}
69
+
70
+ # Load review check classes dynamically.
71
+ check_classes = load_review_checks()
72
+ checks = [check_class(self.client, self.model_name) for check_class in check_classes]
73
+
74
+ for check in checks:
75
+ check_name = check.__class__.__name__
76
+ check_results[check_name] = []
77
+ if hasattr(check, 'process_pr_file'):
78
+ for file in pr.get_files():
79
+ file_comments, analyzed = check.process_pr_file(file, repo, pr)
80
+ if analyzed:
81
+ for comment in file_comments:
82
+ comment['check_name'] = check_name
83
+ all_comments.append(comment)
84
+ check_results[check_name].append(comment)
85
+ if hasattr(check, 'process_pr'):
86
+ pr_comments, _ = check.process_pr(pr)
87
+ for comment in pr_comments:
88
+ comment['check_name'] = check_name
89
+ all_comments.append(comment)
90
+ check_results[check_name].append(comment)
91
+
92
+ summary = "## 🤖 Automated PR Review Summary 🤖\n\n"
93
+ for check_name, comments in check_results.items():
94
+ summary += f"### 🤖 {check_name} 🤖\n\n"
95
+ for comment in comments:
96
+ body = comment.get("body", "")
97
+ file_path = comment.get("path", "")
98
+ if file_path and file_path != "GENERAL":
99
+ body = f"**File:** `{file_path}`\n{body}"
100
+ summary += f"{body}\n\n"
101
+ if not all_comments:
102
+ summary += "\nNo issues found. Code looks good!"
103
+
104
+ return summary
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: gofannon
3
- Version: 0.25.10.1
3
+ Version: 0.25.12
4
4
  Summary: A collection of tools for LLMs
5
5
  License: ASFv2
6
6
  Author: Trevor Grant
@@ -12,17 +12,20 @@ Classifier: Programming Language :: Python :: 3.10
12
12
  Classifier: Programming Language :: Python :: 3.11
13
13
  Classifier: Programming Language :: Python :: 3.12
14
14
  Classifier: Programming Language :: Python :: 3.13
15
+ Provides-Extra: aws
15
16
  Provides-Extra: google
16
17
  Provides-Extra: headless-browser
17
18
  Provides-Extra: langchain
18
19
  Provides-Extra: smolagents
19
20
  Provides-Extra: testing
20
21
  Requires-Dist: GitPython (>=3.1.43,<4.0.0)
22
+ Requires-Dist: boto3 (>=1.34.97,<2.0.0) ; extra == "aws"
21
23
  Requires-Dist: google-api-python-client (>=2.161.0,<3.0.0) ; extra == "google"
22
24
  Requires-Dist: jsonschema (>=4.23.0,<5.0.0)
23
25
  Requires-Dist: langchain (>=0.3.16,<0.4.0) ; extra == "langchain"
24
26
  Requires-Dist: openai (>=1.60.2,<2.0.0)
25
27
  Requires-Dist: pydantic (>=2.10.6,<3.0.0) ; extra == "langchain"
28
+ Requires-Dist: pygithub (>=2.6.1,<3.0.0)
26
29
  Requires-Dist: pytest (>=8.3.4,<9.0.0) ; extra == "testing"
27
30
  Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
28
31
  Requires-Dist: requests (>=2.32.3,<3.0.0)
@@ -31,7 +34,12 @@ Requires-Dist: selenium (>=4.10.0,<5.0.0) ; extra == "headless-browser"
31
34
  Requires-Dist: smolagents (>=1.6.0,<2.0.0) ; extra == "smolagents"
32
35
  Description-Content-Type: text/markdown
33
36
 
34
- ![gofannon logo](./gofannon.jpg)
37
+ ![gofannon logo](https://github.com/The-AI-Alliance/gofannon/blob/main/gofannon.jpg)
38
+ <!-- ![CI](https://github.com/The-AI-Alliance/gofannon/actions/workflows/main.yml/badge.svg) -->
39
+ ![PyPI](https://img.shields.io/pypi/v/gofannon)
40
+ ![License](https://img.shields.io/github/license/The-AI-Alliance/gofannon)
41
+ ![Issues](https://img.shields.io/github/issues/The-AI-Alliance/gofannon)
42
+ ![GitHub stars](https://img.shields.io/github/stars/The-AI-Alliance/gofannon?style=social)
35
43
 
36
44
  # gofannon
37
45
 
@@ -39,7 +47,30 @@ Description-Content-Type: text/markdown
39
47
 
40
48
  ## Why the name `gofanon` ?
41
49
 
42
- See [`why_the_name_gofannon.md`](./why_the_name_gofannon.md) for the rich story on why we chose to honor this Celtic Diety
50
+ See [`why_the_name_gofannon.md`](https://the-ai-alliance.github.io/gofannon/about/the_name_gofannon/) for the rich story on why we chose to honor this Celtic Diety
51
+
52
+ ## Our Mission
53
+
54
+ We aim to achieve:
55
+
56
+ ### Cross-Framework Compatibility
57
+ We solve the "vendor lock-in" problem in AI tooling through:
58
+ - Standardized interface definitions
59
+ - Automatic schema translation
60
+ - Bidirectional conversion tools
61
+
62
+ ### Open Source Education
63
+ We make AI development accessible by:
64
+ - Curated contribution pathways
65
+ - Interactive documentation
66
+ - Pair programming sessions
67
+ - Weekly office hours
68
+
69
+ ### Encouraging First-Time Contributors
70
+ We actively support new contributors through:
71
+ - Beginner-friendly issues
72
+ - Clear documentation and guides
73
+ - Supportuve community engagement
43
74
 
44
75
  ## Features
45
76
 
@@ -51,20 +82,12 @@ See [`why_the_name_gofannon.md`](./why_the_name_gofannon.md) for the rich story
51
82
 
52
83
  ## Roadmap
53
84
 
54
- For a detailed overview of planned features and their current status, please refer to the [ROADMAP](./ROADMAP.md).
85
+ For a detailed overview of planned features and their current status, please refer to the [ROADMAP](https://github.com/The-AI-Alliance/gofannon/blob/main/ROADMAP.md).
55
86
 
56
87
  ## Documentation
57
88
 
58
- Documentation can be found [here](./docs).
89
+ Documentation can be found [here](https://github.com/The-AI-Alliance/gofannon/tree/main/docs).Each tool comes with its own documentation, which can be found in the docs/ directory. The documentation provides detailed information on how to use each tool, including required parameters and example usage.
59
90
 
60
- ## Contributing
61
-
62
- We welcome contributions from the community! If you'd like to add a new tool or improve an existing one, please check out our [CONTRIBUTING](./CONTRIBUTING.md) guide for detailed instructions on how to get started.
63
-
64
- ## Documentation
65
-
66
- Each tool comes with its own documentation, which can be found in the `docs/` directory. The documentation provides detailed information on how to use each tool, including required parameters and example usage.
67
-
68
91
  ## Installation
69
92
 
70
93
  To install gofannon, simply clone the repository and install the required dependencies:
@@ -79,11 +102,41 @@ or
79
102
 
80
103
  ```
81
104
  pip install git+https://github.com/The-AI-Alliance/gofannon.git
105
+ # OR
106
+ pip install gofannon
107
+ ```
108
+
109
+ ## Communication Channels
110
+ - **Discord** [Join our Discord server](https://discord.gg/2MMCVs76Sr)for eal-time collaboration and support
111
+ - **GitHub Discussions**: Explore our [GitHub organization](https://github.com/The-AI-Alliance/agents-wg/discussions/) for all related projects
112
+ - **Community Calls**: [Join our bi-weekly video meetings](https://calendar.app.google/c4eKW4zrNiXaue926)
113
+
114
+ ## Usage Example
115
+ ```bash
116
+ from gofannon.base import BaseTool
117
+
118
+ class NewTool(BaseTool):
119
+ def __init__(self):
120
+ super().__init__()
121
+
122
+ @property
123
+ def definition(self):
124
+ return {
125
+ # Define your tool metadata and configuration
126
+ }
127
+
128
+ def fn(self, *args, **kwargs):
129
+ # Define your tool functionality
130
+ pass
82
131
  ```
83
132
 
84
133
  ## License
85
134
 
86
- This project is licensed under the ASFv2 License. See the [LICENSE](./LICENSE) file for more details.
135
+ This project is licensed under the ASFv2 License. See the [LICENSE](https://github.com/The-AI-Alliance/gofannon/blob/main/LICENSE) file for more details.
136
+
137
+ ## Contributing
138
+
139
+ We welcome contributions from the community! If you'd like to add a new tool or improve an existing one, please check out our [CONTRIBUTING](https://github.com/The-AI-Alliance/gofannon/blob/main/CONTRIBUTING.md) guide for detailed instructions on how to get started.
87
140
 
88
141
  ## Support
89
142
 
@@ -2,9 +2,10 @@ 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=Bsjc0ZJkVRx_q7f6Pp6czgkiysyTRwmqCZIflX7xeqA,3396
6
- gofannon/base/langchain.py,sha256=JbktMQQuMr2LmmGDKU4aDPTnBJZ39_UPChXiB9m3eUM,2619
7
- gofannon/base/smol_agents.py,sha256=8jOK7ijpQ7JjXrk1No6baOmw7PD6FmHLgfXrNX5iJ2E,1672
5
+ gofannon/base/__init__.py,sha256=8-uJsYAbLD-2e4clX73QZ8eip9S2PJlQ9JMRNWFHxSI,3387
6
+ gofannon/base/bedrock.py,sha256=Z2c36R8jaIusgpmegbYVz2eR7lDBc0IhTtwiqUGOcT4,25646
7
+ gofannon/base/langchain.py,sha256=gAmDT_khB28-p8KMf4lT4Buy2w4zrdP1jG4hOituQ54,2714
8
+ gofannon/base/smol_agents.py,sha256=rrdsXiOF-LZ1eY08PCN3r8bg0YEaN1b4vPlSaxii4LI,1654
8
9
  gofannon/basic_math/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
9
10
  gofannon/basic_math/addition.py,sha256=zA-xtUBNnGnQVqTJkBvjZwX0gnGZ6mrUGhrlTbsH1ts,1148
10
11
  gofannon/basic_math/division.py,sha256=ZO8ZzWNL9zeFdXTEdPWDpnbDrWMXVeucSu105RbQmWU,1229
@@ -18,6 +19,7 @@ gofannon/github/commit_file.py,sha256=jdQGQHbrZx4521XgTbx5N0Ss8fDyl7hvp9sjDW15v9
18
19
  gofannon/github/commit_files.py,sha256=0WP4nx4RYpO_Vb45Sa65afuzSMf4de-Wt6awpSOusyM,4395
19
20
  gofannon/github/create_issue.py,sha256=WXq0klzPvGZnZyZRmriGOkHl7E_Tp08vO1v2ou7TR8w,2560
20
21
  gofannon/github/get_repo_contents.py,sha256=9k6M2BqGlNsSGVjyfW7nxZpk1TFuhyPoZvURkv1PEyo,3637
22
+ gofannon/github/pr_review_tool.py,sha256=srBbfgqBWy-J4wdAM0kLJfQr8LJ6uaA4vkDErqhyMxI,4336
21
23
  gofannon/github/read_issue.py,sha256=JrnBAlZxknhHm3aLC0uB9u0bSvoQNfK3OKmxYlr8jgQ,2308
22
24
  gofannon/github/search.py,sha256=yuX_dZ6f8ogUnskIRMUgF8wuN7JepqRtTDFrbmbwrrs,2183
23
25
  gofannon/google_search/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -35,7 +37,7 @@ gofannon/reasoning/base.py,sha256=D-4JHJqUlqgwMNOkKU0BHYA4GEWwNgPlLxKYHX0FVyg,14
35
37
  gofannon/reasoning/hierarchical_cot.py,sha256=e8ZgMbyJQ0wCBEmv7QJqFv7l3XxTMwDYupGxJ7EF6t8,11516
36
38
  gofannon/reasoning/sequential_cot.py,sha256=m9c8GnyTtmI-JntCuhkoFfULAabVOxsYgTRUd3MjzfY,3166
37
39
  gofannon/reasoning/tree_of_thought.py,sha256=TRhRJQNsFVauCLw4TOvQCDcX1nGmp_wSg9H67GJn1hs,10574
38
- gofannon-0.25.10.1.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
39
- gofannon-0.25.10.1.dist-info/METADATA,sha256=rP0KpQfrea01Rl4cTtYfyLCByLPjojDlleY_9lyU6QQ,3884
40
- gofannon-0.25.10.1.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
41
- gofannon-0.25.10.1.dist-info/RECORD,,
40
+ gofannon-0.25.12.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
41
+ gofannon-0.25.12.dist-info/METADATA,sha256=KqFu7dMGSTz5Xg9ESdM20VRNZS3jdWwfRWj9NkV2gvA,6041
42
+ gofannon-0.25.12.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
43
+ gofannon-0.25.12.dist-info/RECORD,,