genxai-framework 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.
- cli/__init__.py +3 -0
- cli/commands/__init__.py +6 -0
- cli/commands/approval.py +85 -0
- cli/commands/audit.py +127 -0
- cli/commands/metrics.py +25 -0
- cli/commands/tool.py +389 -0
- cli/main.py +32 -0
- genxai/__init__.py +81 -0
- genxai/api/__init__.py +5 -0
- genxai/api/app.py +21 -0
- genxai/config/__init__.py +5 -0
- genxai/config/settings.py +37 -0
- genxai/connectors/__init__.py +19 -0
- genxai/connectors/base.py +122 -0
- genxai/connectors/kafka.py +92 -0
- genxai/connectors/postgres_cdc.py +95 -0
- genxai/connectors/registry.py +44 -0
- genxai/connectors/sqs.py +94 -0
- genxai/connectors/webhook.py +73 -0
- genxai/core/__init__.py +37 -0
- genxai/core/agent/__init__.py +32 -0
- genxai/core/agent/base.py +206 -0
- genxai/core/agent/config_io.py +59 -0
- genxai/core/agent/registry.py +98 -0
- genxai/core/agent/runtime.py +970 -0
- genxai/core/communication/__init__.py +6 -0
- genxai/core/communication/collaboration.py +44 -0
- genxai/core/communication/message_bus.py +192 -0
- genxai/core/communication/protocols.py +35 -0
- genxai/core/execution/__init__.py +22 -0
- genxai/core/execution/metadata.py +181 -0
- genxai/core/execution/queue.py +201 -0
- genxai/core/graph/__init__.py +30 -0
- genxai/core/graph/checkpoints.py +77 -0
- genxai/core/graph/edges.py +131 -0
- genxai/core/graph/engine.py +813 -0
- genxai/core/graph/executor.py +516 -0
- genxai/core/graph/nodes.py +161 -0
- genxai/core/graph/trigger_runner.py +40 -0
- genxai/core/memory/__init__.py +19 -0
- genxai/core/memory/base.py +72 -0
- genxai/core/memory/embedding.py +327 -0
- genxai/core/memory/episodic.py +448 -0
- genxai/core/memory/long_term.py +467 -0
- genxai/core/memory/manager.py +543 -0
- genxai/core/memory/persistence.py +297 -0
- genxai/core/memory/procedural.py +461 -0
- genxai/core/memory/semantic.py +526 -0
- genxai/core/memory/shared.py +62 -0
- genxai/core/memory/short_term.py +303 -0
- genxai/core/memory/vector_store.py +508 -0
- genxai/core/memory/working.py +211 -0
- genxai/core/state/__init__.py +6 -0
- genxai/core/state/manager.py +293 -0
- genxai/core/state/schema.py +115 -0
- genxai/llm/__init__.py +14 -0
- genxai/llm/base.py +150 -0
- genxai/llm/factory.py +329 -0
- genxai/llm/providers/__init__.py +1 -0
- genxai/llm/providers/anthropic.py +249 -0
- genxai/llm/providers/cohere.py +274 -0
- genxai/llm/providers/google.py +334 -0
- genxai/llm/providers/ollama.py +147 -0
- genxai/llm/providers/openai.py +257 -0
- genxai/llm/routing.py +83 -0
- genxai/observability/__init__.py +6 -0
- genxai/observability/logging.py +327 -0
- genxai/observability/metrics.py +494 -0
- genxai/observability/tracing.py +372 -0
- genxai/performance/__init__.py +39 -0
- genxai/performance/cache.py +256 -0
- genxai/performance/pooling.py +289 -0
- genxai/security/audit.py +304 -0
- genxai/security/auth.py +315 -0
- genxai/security/cost_control.py +528 -0
- genxai/security/default_policies.py +44 -0
- genxai/security/jwt.py +142 -0
- genxai/security/oauth.py +226 -0
- genxai/security/pii.py +366 -0
- genxai/security/policy_engine.py +82 -0
- genxai/security/rate_limit.py +341 -0
- genxai/security/rbac.py +247 -0
- genxai/security/validation.py +218 -0
- genxai/tools/__init__.py +21 -0
- genxai/tools/base.py +383 -0
- genxai/tools/builtin/__init__.py +131 -0
- genxai/tools/builtin/communication/__init__.py +15 -0
- genxai/tools/builtin/communication/email_sender.py +159 -0
- genxai/tools/builtin/communication/notification_manager.py +167 -0
- genxai/tools/builtin/communication/slack_notifier.py +118 -0
- genxai/tools/builtin/communication/sms_sender.py +118 -0
- genxai/tools/builtin/communication/webhook_caller.py +136 -0
- genxai/tools/builtin/computation/__init__.py +15 -0
- genxai/tools/builtin/computation/calculator.py +101 -0
- genxai/tools/builtin/computation/code_executor.py +183 -0
- genxai/tools/builtin/computation/data_validator.py +259 -0
- genxai/tools/builtin/computation/hash_generator.py +129 -0
- genxai/tools/builtin/computation/regex_matcher.py +201 -0
- genxai/tools/builtin/data/__init__.py +15 -0
- genxai/tools/builtin/data/csv_processor.py +213 -0
- genxai/tools/builtin/data/data_transformer.py +299 -0
- genxai/tools/builtin/data/json_processor.py +233 -0
- genxai/tools/builtin/data/text_analyzer.py +288 -0
- genxai/tools/builtin/data/xml_processor.py +175 -0
- genxai/tools/builtin/database/__init__.py +15 -0
- genxai/tools/builtin/database/database_inspector.py +157 -0
- genxai/tools/builtin/database/mongodb_query.py +196 -0
- genxai/tools/builtin/database/redis_cache.py +167 -0
- genxai/tools/builtin/database/sql_query.py +145 -0
- genxai/tools/builtin/database/vector_search.py +163 -0
- genxai/tools/builtin/file/__init__.py +17 -0
- genxai/tools/builtin/file/directory_scanner.py +214 -0
- genxai/tools/builtin/file/file_compressor.py +237 -0
- genxai/tools/builtin/file/file_reader.py +102 -0
- genxai/tools/builtin/file/file_writer.py +122 -0
- genxai/tools/builtin/file/image_processor.py +186 -0
- genxai/tools/builtin/file/pdf_parser.py +144 -0
- genxai/tools/builtin/test/__init__.py +15 -0
- genxai/tools/builtin/test/async_simulator.py +62 -0
- genxai/tools/builtin/test/data_transformer.py +99 -0
- genxai/tools/builtin/test/error_generator.py +82 -0
- genxai/tools/builtin/test/simple_math.py +94 -0
- genxai/tools/builtin/test/string_processor.py +72 -0
- genxai/tools/builtin/web/__init__.py +15 -0
- genxai/tools/builtin/web/api_caller.py +161 -0
- genxai/tools/builtin/web/html_parser.py +330 -0
- genxai/tools/builtin/web/http_client.py +187 -0
- genxai/tools/builtin/web/url_validator.py +162 -0
- genxai/tools/builtin/web/web_scraper.py +170 -0
- genxai/tools/custom/my_test_tool_2.py +9 -0
- genxai/tools/dynamic.py +105 -0
- genxai/tools/mcp_server.py +167 -0
- genxai/tools/persistence/__init__.py +6 -0
- genxai/tools/persistence/models.py +55 -0
- genxai/tools/persistence/service.py +322 -0
- genxai/tools/registry.py +227 -0
- genxai/tools/security/__init__.py +11 -0
- genxai/tools/security/limits.py +214 -0
- genxai/tools/security/policy.py +20 -0
- genxai/tools/security/sandbox.py +248 -0
- genxai/tools/templates.py +435 -0
- genxai/triggers/__init__.py +19 -0
- genxai/triggers/base.py +104 -0
- genxai/triggers/file_watcher.py +75 -0
- genxai/triggers/queue.py +68 -0
- genxai/triggers/registry.py +82 -0
- genxai/triggers/schedule.py +66 -0
- genxai/triggers/webhook.py +68 -0
- genxai/utils/__init__.py +1 -0
- genxai/utils/tokens.py +295 -0
- genxai_framework-0.1.0.dist-info/METADATA +495 -0
- genxai_framework-0.1.0.dist-info/RECORD +156 -0
- genxai_framework-0.1.0.dist-info/WHEEL +5 -0
- genxai_framework-0.1.0.dist-info/entry_points.txt +2 -0
- genxai_framework-0.1.0.dist-info/licenses/LICENSE +21 -0
- genxai_framework-0.1.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""Data transformation tool for testing JSON/data operations."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
import json
|
|
5
|
+
from genxai.tools.base import Tool, ToolMetadata, ToolParameter, ToolCategory
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class DataTransformerTool(Tool):
|
|
9
|
+
"""Tool for JSON and data transformation operations."""
|
|
10
|
+
|
|
11
|
+
def __init__(self):
|
|
12
|
+
"""Initialize data transformer tool."""
|
|
13
|
+
metadata = ToolMetadata(
|
|
14
|
+
name="data_transformer",
|
|
15
|
+
description="Transform and manipulate JSON data (keys, values, filter, sort)",
|
|
16
|
+
category=ToolCategory.DATA_PROCESSING,
|
|
17
|
+
tags=["json", "data", "transform", "test"],
|
|
18
|
+
version="1.0.0",
|
|
19
|
+
author="GenXAI",
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
parameters = [
|
|
23
|
+
ToolParameter(
|
|
24
|
+
name="data",
|
|
25
|
+
type="string",
|
|
26
|
+
description="JSON string to transform",
|
|
27
|
+
required=True,
|
|
28
|
+
),
|
|
29
|
+
ToolParameter(
|
|
30
|
+
name="transformation",
|
|
31
|
+
type="string",
|
|
32
|
+
description="The transformation to apply",
|
|
33
|
+
required=True,
|
|
34
|
+
enum=["keys", "values", "count", "pretty", "minify", "reverse_keys"],
|
|
35
|
+
),
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
super().__init__(metadata, parameters)
|
|
39
|
+
|
|
40
|
+
async def _execute(self, **kwargs: Any) -> Any:
|
|
41
|
+
"""Execute the data transformation.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
**kwargs: Tool parameters (data, transformation)
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
Result of the transformation
|
|
48
|
+
"""
|
|
49
|
+
data_str = kwargs.get("data", "{}")
|
|
50
|
+
transformation = kwargs.get("transformation")
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
# Parse JSON
|
|
54
|
+
data = json.loads(data_str)
|
|
55
|
+
except json.JSONDecodeError as e:
|
|
56
|
+
raise ValueError(f"Invalid JSON: {str(e)}")
|
|
57
|
+
|
|
58
|
+
if transformation == "keys":
|
|
59
|
+
if isinstance(data, dict):
|
|
60
|
+
result = list(data.keys())
|
|
61
|
+
else:
|
|
62
|
+
raise ValueError("Data must be a JSON object for 'keys' transformation")
|
|
63
|
+
|
|
64
|
+
elif transformation == "values":
|
|
65
|
+
if isinstance(data, dict):
|
|
66
|
+
result = list(data.values())
|
|
67
|
+
else:
|
|
68
|
+
raise ValueError("Data must be a JSON object for 'values' transformation")
|
|
69
|
+
|
|
70
|
+
elif transformation == "count":
|
|
71
|
+
if isinstance(data, dict):
|
|
72
|
+
result = len(data)
|
|
73
|
+
elif isinstance(data, list):
|
|
74
|
+
result = len(data)
|
|
75
|
+
else:
|
|
76
|
+
result = 1
|
|
77
|
+
|
|
78
|
+
elif transformation == "pretty":
|
|
79
|
+
result = json.dumps(data, indent=2, sort_keys=True)
|
|
80
|
+
|
|
81
|
+
elif transformation == "minify":
|
|
82
|
+
result = json.dumps(data, separators=(',', ':'))
|
|
83
|
+
|
|
84
|
+
elif transformation == "reverse_keys":
|
|
85
|
+
if isinstance(data, dict):
|
|
86
|
+
result = {k: v for k, v in reversed(data.items())}
|
|
87
|
+
else:
|
|
88
|
+
raise ValueError("Data must be a JSON object for 'reverse_keys' transformation")
|
|
89
|
+
|
|
90
|
+
else:
|
|
91
|
+
raise ValueError(f"Unknown transformation: {transformation}")
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
"original": data,
|
|
95
|
+
"transformation": transformation,
|
|
96
|
+
"result": result,
|
|
97
|
+
"original_type": type(data).__name__,
|
|
98
|
+
"result_type": type(result).__name__,
|
|
99
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Error generator tool for testing error handling."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
from genxai.tools.base import Tool, ToolMetadata, ToolParameter, ToolCategory
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ErrorGeneratorTool(Tool):
|
|
8
|
+
"""Tool to intentionally generate errors for testing error handling."""
|
|
9
|
+
|
|
10
|
+
def __init__(self):
|
|
11
|
+
"""Initialize error generator tool."""
|
|
12
|
+
metadata = ToolMetadata(
|
|
13
|
+
name="error_generator",
|
|
14
|
+
description="Generate different types of errors to test error handling",
|
|
15
|
+
category=ToolCategory.SYSTEM,
|
|
16
|
+
tags=["error", "test", "validation"],
|
|
17
|
+
version="1.0.0",
|
|
18
|
+
author="GenXAI",
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
parameters = [
|
|
22
|
+
ToolParameter(
|
|
23
|
+
name="error_type",
|
|
24
|
+
type="string",
|
|
25
|
+
description="Type of error to generate",
|
|
26
|
+
required=True,
|
|
27
|
+
enum=["value_error", "type_error", "zero_division", "key_error", "index_error", "none"],
|
|
28
|
+
),
|
|
29
|
+
ToolParameter(
|
|
30
|
+
name="message",
|
|
31
|
+
type="string",
|
|
32
|
+
description="Custom error message",
|
|
33
|
+
required=False,
|
|
34
|
+
default="Test error",
|
|
35
|
+
),
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
super().__init__(metadata, parameters)
|
|
39
|
+
|
|
40
|
+
async def _execute(self, **kwargs: Any) -> Any:
|
|
41
|
+
"""Execute the error generation.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
**kwargs: Tool parameters (error_type, message)
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
Result or raises an error based on error_type
|
|
48
|
+
"""
|
|
49
|
+
error_type = kwargs.get("error_type")
|
|
50
|
+
message = kwargs.get("message", "Test error")
|
|
51
|
+
|
|
52
|
+
if error_type == "value_error":
|
|
53
|
+
raise ValueError(f"ValueError: {message}")
|
|
54
|
+
|
|
55
|
+
elif error_type == "type_error":
|
|
56
|
+
raise TypeError(f"TypeError: {message}")
|
|
57
|
+
|
|
58
|
+
elif error_type == "zero_division":
|
|
59
|
+
# Intentionally divide by zero
|
|
60
|
+
result = 1 / 0
|
|
61
|
+
return result
|
|
62
|
+
|
|
63
|
+
elif error_type == "key_error":
|
|
64
|
+
# Access non-existent key
|
|
65
|
+
data = {"key1": "value1"}
|
|
66
|
+
return data["non_existent_key"]
|
|
67
|
+
|
|
68
|
+
elif error_type == "index_error":
|
|
69
|
+
# Access out of bounds index
|
|
70
|
+
data = [1, 2, 3]
|
|
71
|
+
return data[10]
|
|
72
|
+
|
|
73
|
+
elif error_type == "none":
|
|
74
|
+
# No error, return success
|
|
75
|
+
return {
|
|
76
|
+
"status": "success",
|
|
77
|
+
"message": "No error generated",
|
|
78
|
+
"error_type": error_type,
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
else:
|
|
82
|
+
raise ValueError(f"Unknown error type: {error_type}")
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""Simple math tool for testing basic operations."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
from genxai.tools.base import Tool, ToolMetadata, ToolParameter, ToolCategory
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class SimpleMathTool(Tool):
|
|
8
|
+
"""Tool for basic arithmetic operations."""
|
|
9
|
+
|
|
10
|
+
def __init__(self):
|
|
11
|
+
"""Initialize simple math tool."""
|
|
12
|
+
metadata = ToolMetadata(
|
|
13
|
+
name="simple_math",
|
|
14
|
+
description="Perform basic arithmetic operations (add, subtract, multiply, divide)",
|
|
15
|
+
category=ToolCategory.COMPUTATION,
|
|
16
|
+
tags=["math", "arithmetic", "test"],
|
|
17
|
+
version="1.0.0",
|
|
18
|
+
author="GenXAI",
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
parameters = [
|
|
22
|
+
ToolParameter(
|
|
23
|
+
name="operation",
|
|
24
|
+
type="string",
|
|
25
|
+
description="The operation to perform",
|
|
26
|
+
required=True,
|
|
27
|
+
enum=["add", "subtract", "multiply", "divide"],
|
|
28
|
+
),
|
|
29
|
+
ToolParameter(
|
|
30
|
+
name="a",
|
|
31
|
+
type="number",
|
|
32
|
+
description="First number",
|
|
33
|
+
required=True,
|
|
34
|
+
),
|
|
35
|
+
ToolParameter(
|
|
36
|
+
name="b",
|
|
37
|
+
type="number",
|
|
38
|
+
description="Second number",
|
|
39
|
+
required=True,
|
|
40
|
+
),
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
super().__init__(metadata, parameters)
|
|
44
|
+
|
|
45
|
+
async def _execute(self, **kwargs: Any) -> Any:
|
|
46
|
+
"""Execute the math operation.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
**kwargs: Tool parameters (operation, a, b)
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
Result of the operation
|
|
53
|
+
"""
|
|
54
|
+
operation = kwargs.get("operation")
|
|
55
|
+
a = kwargs.get("a")
|
|
56
|
+
b = kwargs.get("b")
|
|
57
|
+
|
|
58
|
+
if operation == "add":
|
|
59
|
+
result = a + b
|
|
60
|
+
elif operation == "subtract":
|
|
61
|
+
result = a - b
|
|
62
|
+
elif operation == "multiply":
|
|
63
|
+
result = a * b
|
|
64
|
+
elif operation == "divide":
|
|
65
|
+
if b == 0:
|
|
66
|
+
raise ValueError("Cannot divide by zero")
|
|
67
|
+
result = a / b
|
|
68
|
+
else:
|
|
69
|
+
raise ValueError(f"Unknown operation: {operation}")
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
"operation": operation,
|
|
73
|
+
"a": a,
|
|
74
|
+
"b": b,
|
|
75
|
+
"result": result,
|
|
76
|
+
"formula": f"{a} {self._get_operator(operation)} {b} = {result}",
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
def _get_operator(self, operation: str) -> str:
|
|
80
|
+
"""Get the operator symbol for an operation.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
operation: Operation name
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
Operator symbol
|
|
87
|
+
"""
|
|
88
|
+
operators = {
|
|
89
|
+
"add": "+",
|
|
90
|
+
"subtract": "-",
|
|
91
|
+
"multiply": "×",
|
|
92
|
+
"divide": "÷",
|
|
93
|
+
}
|
|
94
|
+
return operators.get(operation, "?")
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""String processing tool for testing string operations."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
from genxai.tools.base import Tool, ToolMetadata, ToolParameter, ToolCategory
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class StringProcessorTool(Tool):
|
|
8
|
+
"""Tool for string manipulation operations."""
|
|
9
|
+
|
|
10
|
+
def __init__(self):
|
|
11
|
+
"""Initialize string processor tool."""
|
|
12
|
+
metadata = ToolMetadata(
|
|
13
|
+
name="string_processor",
|
|
14
|
+
description="Perform string manipulation operations (uppercase, lowercase, reverse, length, capitalize)",
|
|
15
|
+
category=ToolCategory.DATA_PROCESSING,
|
|
16
|
+
tags=["string", "text", "manipulation", "test"],
|
|
17
|
+
version="1.0.0",
|
|
18
|
+
author="GenXAI",
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
parameters = [
|
|
22
|
+
ToolParameter(
|
|
23
|
+
name="text",
|
|
24
|
+
type="string",
|
|
25
|
+
description="The text to process",
|
|
26
|
+
required=True,
|
|
27
|
+
),
|
|
28
|
+
ToolParameter(
|
|
29
|
+
name="operation",
|
|
30
|
+
type="string",
|
|
31
|
+
description="The operation to perform",
|
|
32
|
+
required=True,
|
|
33
|
+
enum=["uppercase", "lowercase", "reverse", "length", "capitalize", "title"],
|
|
34
|
+
),
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
super().__init__(metadata, parameters)
|
|
38
|
+
|
|
39
|
+
async def _execute(self, **kwargs: Any) -> Any:
|
|
40
|
+
"""Execute the string operation.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
**kwargs: Tool parameters (text, operation)
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
Result of the operation
|
|
47
|
+
"""
|
|
48
|
+
text = kwargs.get("text", "")
|
|
49
|
+
operation = kwargs.get("operation")
|
|
50
|
+
|
|
51
|
+
if operation == "uppercase":
|
|
52
|
+
result = text.upper()
|
|
53
|
+
elif operation == "lowercase":
|
|
54
|
+
result = text.lower()
|
|
55
|
+
elif operation == "reverse":
|
|
56
|
+
result = text[::-1]
|
|
57
|
+
elif operation == "length":
|
|
58
|
+
result = len(text)
|
|
59
|
+
elif operation == "capitalize":
|
|
60
|
+
result = text.capitalize()
|
|
61
|
+
elif operation == "title":
|
|
62
|
+
result = text.title()
|
|
63
|
+
else:
|
|
64
|
+
raise ValueError(f"Unknown operation: {operation}")
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
"original": text,
|
|
68
|
+
"operation": operation,
|
|
69
|
+
"result": result,
|
|
70
|
+
"original_length": len(text),
|
|
71
|
+
"result_length": len(str(result)),
|
|
72
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Web tools for GenXAI."""
|
|
2
|
+
|
|
3
|
+
from genxai.tools.builtin.web.web_scraper import WebScraperTool
|
|
4
|
+
from genxai.tools.builtin.web.api_caller import APICallerTool
|
|
5
|
+
from genxai.tools.builtin.web.url_validator import URLValidatorTool
|
|
6
|
+
from genxai.tools.builtin.web.http_client import HTTPClientTool
|
|
7
|
+
from genxai.tools.builtin.web.html_parser import HTMLParserTool
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"WebScraperTool",
|
|
11
|
+
"APICallerTool",
|
|
12
|
+
"URLValidatorTool",
|
|
13
|
+
"HTTPClientTool",
|
|
14
|
+
"HTMLParserTool",
|
|
15
|
+
]
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""API caller tool for making HTTP requests."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, Optional
|
|
4
|
+
import logging
|
|
5
|
+
import json
|
|
6
|
+
|
|
7
|
+
from genxai.tools.base import Tool, ToolMetadata, ToolParameter, ToolCategory
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class APICallerTool(Tool):
|
|
13
|
+
"""Make HTTP API calls with authentication support."""
|
|
14
|
+
|
|
15
|
+
def __init__(self) -> None:
|
|
16
|
+
"""Initialize API caller tool."""
|
|
17
|
+
metadata = ToolMetadata(
|
|
18
|
+
name="api_caller",
|
|
19
|
+
description="Call external REST APIs with various HTTP methods and authentication",
|
|
20
|
+
category=ToolCategory.WEB,
|
|
21
|
+
tags=["api", "http", "rest", "request", "client"],
|
|
22
|
+
version="1.0.0",
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
parameters = [
|
|
26
|
+
ToolParameter(
|
|
27
|
+
name="url",
|
|
28
|
+
type="string",
|
|
29
|
+
description="API endpoint URL",
|
|
30
|
+
required=True,
|
|
31
|
+
pattern=r"^https?://",
|
|
32
|
+
),
|
|
33
|
+
ToolParameter(
|
|
34
|
+
name="method",
|
|
35
|
+
type="string",
|
|
36
|
+
description="HTTP method",
|
|
37
|
+
required=True,
|
|
38
|
+
enum=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"],
|
|
39
|
+
),
|
|
40
|
+
ToolParameter(
|
|
41
|
+
name="headers",
|
|
42
|
+
type="object",
|
|
43
|
+
description="HTTP headers as key-value pairs",
|
|
44
|
+
required=False,
|
|
45
|
+
),
|
|
46
|
+
ToolParameter(
|
|
47
|
+
name="params",
|
|
48
|
+
type="object",
|
|
49
|
+
description="URL query parameters",
|
|
50
|
+
required=False,
|
|
51
|
+
),
|
|
52
|
+
ToolParameter(
|
|
53
|
+
name="body",
|
|
54
|
+
type="object",
|
|
55
|
+
description="Request body (for POST, PUT, PATCH)",
|
|
56
|
+
required=False,
|
|
57
|
+
),
|
|
58
|
+
ToolParameter(
|
|
59
|
+
name="timeout",
|
|
60
|
+
type="number",
|
|
61
|
+
description="Request timeout in seconds",
|
|
62
|
+
required=False,
|
|
63
|
+
default=30,
|
|
64
|
+
min_value=1,
|
|
65
|
+
max_value=300,
|
|
66
|
+
),
|
|
67
|
+
ToolParameter(
|
|
68
|
+
name="follow_redirects",
|
|
69
|
+
type="boolean",
|
|
70
|
+
description="Whether to follow redirects",
|
|
71
|
+
required=False,
|
|
72
|
+
default=True,
|
|
73
|
+
),
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
super().__init__(metadata, parameters)
|
|
77
|
+
|
|
78
|
+
async def _execute(
|
|
79
|
+
self,
|
|
80
|
+
url: str,
|
|
81
|
+
method: str,
|
|
82
|
+
headers: Optional[Dict[str, str]] = None,
|
|
83
|
+
params: Optional[Dict[str, Any]] = None,
|
|
84
|
+
body: Optional[Dict[str, Any]] = None,
|
|
85
|
+
timeout: int = 30,
|
|
86
|
+
follow_redirects: bool = True,
|
|
87
|
+
) -> Dict[str, Any]:
|
|
88
|
+
"""Execute API call.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
url: API endpoint URL
|
|
92
|
+
method: HTTP method
|
|
93
|
+
headers: Request headers
|
|
94
|
+
params: Query parameters
|
|
95
|
+
body: Request body
|
|
96
|
+
timeout: Request timeout
|
|
97
|
+
follow_redirects: Follow redirects flag
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
Dictionary containing response data
|
|
101
|
+
"""
|
|
102
|
+
try:
|
|
103
|
+
import httpx
|
|
104
|
+
except ImportError:
|
|
105
|
+
raise ImportError(
|
|
106
|
+
"httpx package not installed. Install with: pip install httpx"
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# Prepare headers
|
|
110
|
+
request_headers = headers or {}
|
|
111
|
+
if body and "Content-Type" not in request_headers:
|
|
112
|
+
request_headers["Content-Type"] = "application/json"
|
|
113
|
+
|
|
114
|
+
# Prepare request
|
|
115
|
+
method = method.upper()
|
|
116
|
+
request_kwargs: Dict[str, Any] = {
|
|
117
|
+
"method": method,
|
|
118
|
+
"url": url,
|
|
119
|
+
"headers": request_headers,
|
|
120
|
+
"params": params,
|
|
121
|
+
"timeout": timeout,
|
|
122
|
+
"follow_redirects": follow_redirects,
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
# Add body for methods that support it
|
|
126
|
+
if method in ["POST", "PUT", "PATCH"] and body:
|
|
127
|
+
if request_headers.get("Content-Type") == "application/json":
|
|
128
|
+
request_kwargs["json"] = body
|
|
129
|
+
else:
|
|
130
|
+
request_kwargs["data"] = body
|
|
131
|
+
|
|
132
|
+
# Make request
|
|
133
|
+
async with httpx.AsyncClient() as client:
|
|
134
|
+
response = await client.request(**request_kwargs)
|
|
135
|
+
|
|
136
|
+
# Parse response
|
|
137
|
+
result: Dict[str, Any] = {
|
|
138
|
+
"status_code": response.status_code,
|
|
139
|
+
"headers": dict(response.headers),
|
|
140
|
+
"url": str(response.url),
|
|
141
|
+
"method": method,
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
# Try to parse JSON response
|
|
145
|
+
content_type = response.headers.get("content-type", "")
|
|
146
|
+
if "application/json" in content_type:
|
|
147
|
+
try:
|
|
148
|
+
result["data"] = response.json()
|
|
149
|
+
except json.JSONDecodeError:
|
|
150
|
+
result["data"] = response.text
|
|
151
|
+
else:
|
|
152
|
+
result["data"] = response.text
|
|
153
|
+
|
|
154
|
+
# Add response metadata
|
|
155
|
+
result["elapsed_ms"] = response.elapsed.total_seconds() * 1000
|
|
156
|
+
result["success"] = 200 <= response.status_code < 300
|
|
157
|
+
|
|
158
|
+
logger.info(
|
|
159
|
+
f"API call to {url} completed with status {response.status_code}"
|
|
160
|
+
)
|
|
161
|
+
return result
|