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.
Files changed (156) hide show
  1. cli/__init__.py +3 -0
  2. cli/commands/__init__.py +6 -0
  3. cli/commands/approval.py +85 -0
  4. cli/commands/audit.py +127 -0
  5. cli/commands/metrics.py +25 -0
  6. cli/commands/tool.py +389 -0
  7. cli/main.py +32 -0
  8. genxai/__init__.py +81 -0
  9. genxai/api/__init__.py +5 -0
  10. genxai/api/app.py +21 -0
  11. genxai/config/__init__.py +5 -0
  12. genxai/config/settings.py +37 -0
  13. genxai/connectors/__init__.py +19 -0
  14. genxai/connectors/base.py +122 -0
  15. genxai/connectors/kafka.py +92 -0
  16. genxai/connectors/postgres_cdc.py +95 -0
  17. genxai/connectors/registry.py +44 -0
  18. genxai/connectors/sqs.py +94 -0
  19. genxai/connectors/webhook.py +73 -0
  20. genxai/core/__init__.py +37 -0
  21. genxai/core/agent/__init__.py +32 -0
  22. genxai/core/agent/base.py +206 -0
  23. genxai/core/agent/config_io.py +59 -0
  24. genxai/core/agent/registry.py +98 -0
  25. genxai/core/agent/runtime.py +970 -0
  26. genxai/core/communication/__init__.py +6 -0
  27. genxai/core/communication/collaboration.py +44 -0
  28. genxai/core/communication/message_bus.py +192 -0
  29. genxai/core/communication/protocols.py +35 -0
  30. genxai/core/execution/__init__.py +22 -0
  31. genxai/core/execution/metadata.py +181 -0
  32. genxai/core/execution/queue.py +201 -0
  33. genxai/core/graph/__init__.py +30 -0
  34. genxai/core/graph/checkpoints.py +77 -0
  35. genxai/core/graph/edges.py +131 -0
  36. genxai/core/graph/engine.py +813 -0
  37. genxai/core/graph/executor.py +516 -0
  38. genxai/core/graph/nodes.py +161 -0
  39. genxai/core/graph/trigger_runner.py +40 -0
  40. genxai/core/memory/__init__.py +19 -0
  41. genxai/core/memory/base.py +72 -0
  42. genxai/core/memory/embedding.py +327 -0
  43. genxai/core/memory/episodic.py +448 -0
  44. genxai/core/memory/long_term.py +467 -0
  45. genxai/core/memory/manager.py +543 -0
  46. genxai/core/memory/persistence.py +297 -0
  47. genxai/core/memory/procedural.py +461 -0
  48. genxai/core/memory/semantic.py +526 -0
  49. genxai/core/memory/shared.py +62 -0
  50. genxai/core/memory/short_term.py +303 -0
  51. genxai/core/memory/vector_store.py +508 -0
  52. genxai/core/memory/working.py +211 -0
  53. genxai/core/state/__init__.py +6 -0
  54. genxai/core/state/manager.py +293 -0
  55. genxai/core/state/schema.py +115 -0
  56. genxai/llm/__init__.py +14 -0
  57. genxai/llm/base.py +150 -0
  58. genxai/llm/factory.py +329 -0
  59. genxai/llm/providers/__init__.py +1 -0
  60. genxai/llm/providers/anthropic.py +249 -0
  61. genxai/llm/providers/cohere.py +274 -0
  62. genxai/llm/providers/google.py +334 -0
  63. genxai/llm/providers/ollama.py +147 -0
  64. genxai/llm/providers/openai.py +257 -0
  65. genxai/llm/routing.py +83 -0
  66. genxai/observability/__init__.py +6 -0
  67. genxai/observability/logging.py +327 -0
  68. genxai/observability/metrics.py +494 -0
  69. genxai/observability/tracing.py +372 -0
  70. genxai/performance/__init__.py +39 -0
  71. genxai/performance/cache.py +256 -0
  72. genxai/performance/pooling.py +289 -0
  73. genxai/security/audit.py +304 -0
  74. genxai/security/auth.py +315 -0
  75. genxai/security/cost_control.py +528 -0
  76. genxai/security/default_policies.py +44 -0
  77. genxai/security/jwt.py +142 -0
  78. genxai/security/oauth.py +226 -0
  79. genxai/security/pii.py +366 -0
  80. genxai/security/policy_engine.py +82 -0
  81. genxai/security/rate_limit.py +341 -0
  82. genxai/security/rbac.py +247 -0
  83. genxai/security/validation.py +218 -0
  84. genxai/tools/__init__.py +21 -0
  85. genxai/tools/base.py +383 -0
  86. genxai/tools/builtin/__init__.py +131 -0
  87. genxai/tools/builtin/communication/__init__.py +15 -0
  88. genxai/tools/builtin/communication/email_sender.py +159 -0
  89. genxai/tools/builtin/communication/notification_manager.py +167 -0
  90. genxai/tools/builtin/communication/slack_notifier.py +118 -0
  91. genxai/tools/builtin/communication/sms_sender.py +118 -0
  92. genxai/tools/builtin/communication/webhook_caller.py +136 -0
  93. genxai/tools/builtin/computation/__init__.py +15 -0
  94. genxai/tools/builtin/computation/calculator.py +101 -0
  95. genxai/tools/builtin/computation/code_executor.py +183 -0
  96. genxai/tools/builtin/computation/data_validator.py +259 -0
  97. genxai/tools/builtin/computation/hash_generator.py +129 -0
  98. genxai/tools/builtin/computation/regex_matcher.py +201 -0
  99. genxai/tools/builtin/data/__init__.py +15 -0
  100. genxai/tools/builtin/data/csv_processor.py +213 -0
  101. genxai/tools/builtin/data/data_transformer.py +299 -0
  102. genxai/tools/builtin/data/json_processor.py +233 -0
  103. genxai/tools/builtin/data/text_analyzer.py +288 -0
  104. genxai/tools/builtin/data/xml_processor.py +175 -0
  105. genxai/tools/builtin/database/__init__.py +15 -0
  106. genxai/tools/builtin/database/database_inspector.py +157 -0
  107. genxai/tools/builtin/database/mongodb_query.py +196 -0
  108. genxai/tools/builtin/database/redis_cache.py +167 -0
  109. genxai/tools/builtin/database/sql_query.py +145 -0
  110. genxai/tools/builtin/database/vector_search.py +163 -0
  111. genxai/tools/builtin/file/__init__.py +17 -0
  112. genxai/tools/builtin/file/directory_scanner.py +214 -0
  113. genxai/tools/builtin/file/file_compressor.py +237 -0
  114. genxai/tools/builtin/file/file_reader.py +102 -0
  115. genxai/tools/builtin/file/file_writer.py +122 -0
  116. genxai/tools/builtin/file/image_processor.py +186 -0
  117. genxai/tools/builtin/file/pdf_parser.py +144 -0
  118. genxai/tools/builtin/test/__init__.py +15 -0
  119. genxai/tools/builtin/test/async_simulator.py +62 -0
  120. genxai/tools/builtin/test/data_transformer.py +99 -0
  121. genxai/tools/builtin/test/error_generator.py +82 -0
  122. genxai/tools/builtin/test/simple_math.py +94 -0
  123. genxai/tools/builtin/test/string_processor.py +72 -0
  124. genxai/tools/builtin/web/__init__.py +15 -0
  125. genxai/tools/builtin/web/api_caller.py +161 -0
  126. genxai/tools/builtin/web/html_parser.py +330 -0
  127. genxai/tools/builtin/web/http_client.py +187 -0
  128. genxai/tools/builtin/web/url_validator.py +162 -0
  129. genxai/tools/builtin/web/web_scraper.py +170 -0
  130. genxai/tools/custom/my_test_tool_2.py +9 -0
  131. genxai/tools/dynamic.py +105 -0
  132. genxai/tools/mcp_server.py +167 -0
  133. genxai/tools/persistence/__init__.py +6 -0
  134. genxai/tools/persistence/models.py +55 -0
  135. genxai/tools/persistence/service.py +322 -0
  136. genxai/tools/registry.py +227 -0
  137. genxai/tools/security/__init__.py +11 -0
  138. genxai/tools/security/limits.py +214 -0
  139. genxai/tools/security/policy.py +20 -0
  140. genxai/tools/security/sandbox.py +248 -0
  141. genxai/tools/templates.py +435 -0
  142. genxai/triggers/__init__.py +19 -0
  143. genxai/triggers/base.py +104 -0
  144. genxai/triggers/file_watcher.py +75 -0
  145. genxai/triggers/queue.py +68 -0
  146. genxai/triggers/registry.py +82 -0
  147. genxai/triggers/schedule.py +66 -0
  148. genxai/triggers/webhook.py +68 -0
  149. genxai/utils/__init__.py +1 -0
  150. genxai/utils/tokens.py +295 -0
  151. genxai_framework-0.1.0.dist-info/METADATA +495 -0
  152. genxai_framework-0.1.0.dist-info/RECORD +156 -0
  153. genxai_framework-0.1.0.dist-info/WHEEL +5 -0
  154. genxai_framework-0.1.0.dist-info/entry_points.txt +2 -0
  155. genxai_framework-0.1.0.dist-info/licenses/LICENSE +21 -0
  156. genxai_framework-0.1.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,322 @@
1
+ """Service for tool persistence and management."""
2
+
3
+ import os
4
+ import logging
5
+ from typing import List, Optional, Dict, Any
6
+ from sqlalchemy import create_engine
7
+ from sqlalchemy.orm import sessionmaker, Session
8
+ from pathlib import Path
9
+
10
+ from genxai.tools.persistence.models import Base, ToolModel
11
+ from genxai.tools.base import Tool, ToolMetadata, ToolParameter, ToolCategory
12
+ from genxai.tools.registry import ToolRegistry
13
+ from genxai.tools.dynamic import DynamicTool
14
+ from genxai.tools.templates import create_tool_from_template
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+ # Database setup - now in genxai/data/
19
+ DB_PATH = Path(__file__).parent.parent.parent / "data" / "tools.db"
20
+ DB_PATH.parent.mkdir(parents=True, exist_ok=True)
21
+ DATABASE_URL = f"sqlite:///{DB_PATH}"
22
+
23
+ engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
24
+ SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
25
+
26
+ # Create tables
27
+ Base.metadata.create_all(bind=engine)
28
+
29
+
30
+ class ToolService:
31
+ """Service for managing tool persistence."""
32
+
33
+ @staticmethod
34
+ def get_db() -> Session:
35
+ """Get database session."""
36
+ db = SessionLocal()
37
+ try:
38
+ return db
39
+ finally:
40
+ pass
41
+
42
+ @staticmethod
43
+ def save_tool(
44
+ name: str,
45
+ description: str,
46
+ category: str,
47
+ tags: List[str],
48
+ version: str,
49
+ author: str,
50
+ tool_type: str,
51
+ code: Optional[str] = None,
52
+ parameters: Optional[List[Dict[str, Any]]] = None,
53
+ template_name: Optional[str] = None,
54
+ template_config: Optional[Dict[str, Any]] = None,
55
+ ) -> ToolModel:
56
+ """Save tool to database.
57
+
58
+ Args:
59
+ name: Tool name
60
+ description: Tool description
61
+ category: Tool category
62
+ tags: Tool tags
63
+ version: Tool version
64
+ author: Tool author
65
+ tool_type: "code_based" or "template_based"
66
+ code: Python code (for code-based tools)
67
+ parameters: Tool parameters (for code-based tools)
68
+ template_name: Template name (for template-based tools)
69
+ template_config: Template configuration (for template-based tools)
70
+
71
+ Returns:
72
+ Created ToolModel instance
73
+ """
74
+ db = SessionLocal()
75
+ try:
76
+ tool_model = ToolModel(
77
+ name=name,
78
+ description=description,
79
+ category=category,
80
+ tags=tags,
81
+ version=version,
82
+ author=author,
83
+ tool_type=tool_type,
84
+ code=code,
85
+ parameters=parameters or [],
86
+ template_name=template_name,
87
+ template_config=template_config,
88
+ )
89
+
90
+ db.add(tool_model)
91
+ db.commit()
92
+ db.refresh(tool_model)
93
+
94
+ logger.info(f"Saved tool to database: {name}")
95
+
96
+ # Optionally export to file
97
+ ToolService._export_to_file(tool_model)
98
+
99
+ return tool_model
100
+ except Exception as e:
101
+ db.rollback()
102
+ logger.error(f"Failed to save tool: {e}")
103
+ raise
104
+ finally:
105
+ db.close()
106
+
107
+ @staticmethod
108
+ def get_tool(name: str) -> Optional[ToolModel]:
109
+ """Get tool from database by name.
110
+
111
+ Args:
112
+ name: Tool name
113
+
114
+ Returns:
115
+ ToolModel instance or None
116
+ """
117
+ db = SessionLocal()
118
+ try:
119
+ return db.query(ToolModel).filter(ToolModel.name == name).first()
120
+ finally:
121
+ db.close()
122
+
123
+ @staticmethod
124
+ def list_tools() -> List[ToolModel]:
125
+ """List all tools from database.
126
+
127
+ Returns:
128
+ List of ToolModel instances
129
+ """
130
+ db = SessionLocal()
131
+ try:
132
+ return db.query(ToolModel).all()
133
+ finally:
134
+ db.close()
135
+
136
+ @staticmethod
137
+ def update_tool_code(name: str, code: str) -> bool:
138
+ """Update tool code in database.
139
+
140
+ Args:
141
+ name: Tool name
142
+ code: New Python code
143
+
144
+ Returns:
145
+ True if updated, False if not found
146
+ """
147
+ db = SessionLocal()
148
+ try:
149
+ tool = db.query(ToolModel).filter(ToolModel.name == name).first()
150
+ if tool and tool.tool_type == "code_based":
151
+ tool.code = code
152
+ db.commit()
153
+ logger.info(f"Updated tool code in database: {name}")
154
+
155
+ # Update file if exists
156
+ ToolService._export_to_file(tool)
157
+
158
+ return True
159
+ return False
160
+ except Exception as e:
161
+ db.rollback()
162
+ logger.error(f"Failed to update tool code: {e}")
163
+ raise
164
+ finally:
165
+ db.close()
166
+
167
+ @staticmethod
168
+ def delete_tool(name: str) -> bool:
169
+ """Delete tool from database.
170
+
171
+ Args:
172
+ name: Tool name
173
+
174
+ Returns:
175
+ True if deleted, False if not found
176
+ """
177
+ db = SessionLocal()
178
+ try:
179
+ tool = db.query(ToolModel).filter(ToolModel.name == name).first()
180
+ if tool:
181
+ db.delete(tool)
182
+ db.commit()
183
+ logger.info(f"Deleted tool from database: {name}")
184
+
185
+ # Delete file if exists
186
+ ToolService._delete_file(name)
187
+
188
+ return True
189
+ return False
190
+ except Exception as e:
191
+ db.rollback()
192
+ logger.error(f"Failed to delete tool: {e}")
193
+ raise
194
+ finally:
195
+ db.close()
196
+
197
+ @staticmethod
198
+ def load_tool_to_registry(tool_model: ToolModel) -> Tool:
199
+ """Load tool from database model to registry.
200
+
201
+ Args:
202
+ tool_model: ToolModel instance
203
+
204
+ Returns:
205
+ Tool instance
206
+ """
207
+ try:
208
+ category = ToolCategory(tool_model.category)
209
+
210
+ if tool_model.tool_type == "code_based":
211
+ # Create code-based tool
212
+ metadata = ToolMetadata(
213
+ name=tool_model.name,
214
+ description=tool_model.description,
215
+ category=category,
216
+ tags=tool_model.tags,
217
+ version=tool_model.version,
218
+ author=tool_model.author,
219
+ )
220
+
221
+ parameters = [
222
+ ToolParameter(
223
+ name=p["name"],
224
+ type=p["type"],
225
+ description=p["description"],
226
+ required=p.get("required", True),
227
+ default=p.get("default"),
228
+ enum=p.get("enum"),
229
+ min_value=p.get("min_value"),
230
+ max_value=p.get("max_value"),
231
+ pattern=p.get("pattern"),
232
+ )
233
+ for p in tool_model.parameters
234
+ ]
235
+
236
+ tool = DynamicTool(metadata, parameters, tool_model.code)
237
+
238
+ elif tool_model.tool_type == "template_based":
239
+ # Create template-based tool
240
+ tool = create_tool_from_template(
241
+ name=tool_model.name,
242
+ description=tool_model.description,
243
+ category=category,
244
+ tags=tool_model.tags,
245
+ template=tool_model.template_name,
246
+ config=tool_model.template_config or {},
247
+ )
248
+ else:
249
+ raise ValueError(f"Unknown tool type: {tool_model.tool_type}")
250
+
251
+ # Register in registry
252
+ ToolRegistry.register(tool)
253
+ logger.info(f"Loaded tool to registry: {tool_model.name}")
254
+
255
+ return tool
256
+
257
+ except Exception as e:
258
+ logger.error(f"Failed to load tool {tool_model.name}: {e}")
259
+ raise
260
+
261
+ @staticmethod
262
+ def load_all_tools():
263
+ """Load all tools from database to registry."""
264
+ tools = ToolService.list_tools()
265
+ loaded_count = 0
266
+
267
+ for tool_model in tools:
268
+ try:
269
+ ToolService.load_tool_to_registry(tool_model)
270
+ loaded_count += 1
271
+ except Exception as e:
272
+ logger.error(f"Failed to load tool {tool_model.name}: {e}")
273
+
274
+ logger.info(f"Loaded {loaded_count}/{len(tools)} tools from database")
275
+
276
+ @staticmethod
277
+ def _export_to_file(tool_model: ToolModel):
278
+ """Export tool to file system (optional backup).
279
+
280
+ Args:
281
+ tool_model: ToolModel instance
282
+ """
283
+ try:
284
+ custom_dir = Path(__file__).parent.parent / "custom"
285
+ custom_dir.mkdir(parents=True, exist_ok=True)
286
+
287
+ file_path = custom_dir / f"{tool_model.name}.py"
288
+
289
+ if tool_model.tool_type == "code_based":
290
+ content = f'''"""
291
+ Auto-generated tool: {tool_model.name}
292
+ Description: {tool_model.description}
293
+ Category: {tool_model.category}
294
+ Created: {tool_model.created_at}
295
+ """
296
+
297
+ # Tool code
298
+ {tool_model.code}
299
+ '''
300
+ file_path.write_text(content)
301
+ logger.info(f"Exported tool to file: {file_path}")
302
+
303
+ except Exception as e:
304
+ logger.warning(f"Failed to export tool to file: {e}")
305
+
306
+ @staticmethod
307
+ def _delete_file(name: str):
308
+ """Delete tool file if exists.
309
+
310
+ Args:
311
+ name: Tool name
312
+ """
313
+ try:
314
+ custom_dir = Path(__file__).parent.parent / "custom"
315
+ file_path = custom_dir / f"{name}.py"
316
+
317
+ if file_path.exists():
318
+ file_path.unlink()
319
+ logger.info(f"Deleted tool file: {file_path}")
320
+
321
+ except Exception as e:
322
+ logger.warning(f"Failed to delete tool file: {e}")
@@ -0,0 +1,227 @@
1
+ """Tool registry for managing available tools."""
2
+
3
+ from typing import Any, Dict, List, Optional
4
+ import logging
5
+
6
+ from genxai.tools.base import Tool, ToolCategory
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ class ToolRegistry:
12
+ """Central registry for all tools."""
13
+
14
+ SCHEMA_VERSION = "1.0"
15
+ _instance: Optional["ToolRegistry"] = None
16
+ _tools: Dict[str, Tool] = {}
17
+
18
+ def __new__(cls) -> "ToolRegistry":
19
+ """Singleton pattern for tool registry."""
20
+ if cls._instance is None:
21
+ cls._instance = super().__new__(cls)
22
+ return cls._instance
23
+
24
+ @classmethod
25
+ def register(cls, tool: Tool) -> None:
26
+ """Register a new tool.
27
+
28
+ Args:
29
+ tool: Tool to register
30
+ """
31
+ if tool.metadata.name in cls._tools:
32
+ logger.warning(
33
+ f"Tool {tool.metadata.name} already registered, overwriting"
34
+ )
35
+ cls._tools[tool.metadata.name] = tool
36
+ logger.info(f"Registered tool: {tool.metadata.name}")
37
+
38
+ @classmethod
39
+ def unregister(cls, name: str) -> None:
40
+ """Unregister a tool.
41
+
42
+ Args:
43
+ name: Tool name to unregister
44
+ """
45
+ if name in cls._tools:
46
+ del cls._tools[name]
47
+ logger.info(f"Unregistered tool: {name}")
48
+ else:
49
+ logger.warning(f"Tool {name} not found in registry")
50
+
51
+ @classmethod
52
+ def get(cls, name: str) -> Optional[Tool]:
53
+ """Get tool by name.
54
+
55
+ Args:
56
+ name: Tool name
57
+
58
+ Returns:
59
+ Tool instance or None if not found
60
+ """
61
+ return cls._tools.get(name)
62
+
63
+ @classmethod
64
+ def list_all(cls) -> List[Tool]:
65
+ """List all registered tools.
66
+
67
+ Returns:
68
+ List of all tools
69
+ """
70
+ return list(cls._tools.values())
71
+
72
+ @classmethod
73
+ def search(
74
+ cls, query: str, category: Optional[ToolCategory] = None
75
+ ) -> List[Tool]:
76
+ """Search tools by query and category.
77
+
78
+ Args:
79
+ query: Search query (searches name, description, tags)
80
+ category: Optional category filter
81
+
82
+ Returns:
83
+ List of matching tools
84
+ """
85
+ results = []
86
+ query_lower = query.lower()
87
+
88
+ for tool in cls._tools.values():
89
+ # Filter by category
90
+ if category and tool.metadata.category != category:
91
+ continue
92
+
93
+ # Search in name, description, and tags
94
+ if (
95
+ query_lower in tool.metadata.name.lower()
96
+ or query_lower in tool.metadata.description.lower()
97
+ or any(query_lower in tag.lower() for tag in tool.metadata.tags)
98
+ ):
99
+ results.append(tool)
100
+
101
+ logger.debug(f"Found {len(results)} tools matching '{query}'")
102
+ return results
103
+
104
+ @classmethod
105
+ def list_categories(cls) -> List[ToolCategory]:
106
+ """List all tool categories in use.
107
+
108
+ Returns:
109
+ List of categories
110
+ """
111
+ return list(set(tool.metadata.category for tool in cls._tools.values()))
112
+
113
+ @classmethod
114
+ def get_by_category(cls, category: ToolCategory) -> List[Tool]:
115
+ """Get all tools in a category.
116
+
117
+ Args:
118
+ category: Tool category
119
+
120
+ Returns:
121
+ List of tools in category
122
+ """
123
+ return [
124
+ tool for tool in cls._tools.values() if tool.metadata.category == category
125
+ ]
126
+
127
+ @classmethod
128
+ def get_by_tag(cls, tag: str) -> List[Tool]:
129
+ """Get all tools with a specific tag.
130
+
131
+ Args:
132
+ tag: Tag to search for
133
+
134
+ Returns:
135
+ List of tools with tag
136
+ """
137
+ return [
138
+ tool
139
+ for tool in cls._tools.values()
140
+ if tag.lower() in [t.lower() for t in tool.metadata.tags]
141
+ ]
142
+
143
+ @classmethod
144
+ def clear(cls) -> None:
145
+ """Clear all registered tools."""
146
+ cls._tools.clear()
147
+ logger.info("Cleared all tools from registry")
148
+
149
+ @classmethod
150
+ def export_schema_bundle(cls, category: Optional[ToolCategory] = None) -> Dict[str, Any]:
151
+ """Export a consolidated schema bundle for all registered tools.
152
+
153
+ Returns:
154
+ Dictionary containing tool schemas and metadata.
155
+ """
156
+ tools = [
157
+ tool.get_schema()
158
+ for tool in cls._tools.values()
159
+ if not category or tool.metadata.category == category
160
+ ]
161
+ categories = {}
162
+ for tool in cls._tools.values():
163
+ if category and tool.metadata.category != category:
164
+ continue
165
+ category = tool.metadata.category.value
166
+ categories[category] = categories.get(category, 0) + 1
167
+
168
+ return {
169
+ "schema_version": cls.SCHEMA_VERSION,
170
+ "tool_count": len(tools),
171
+ "categories": categories,
172
+ "tools": tools,
173
+ }
174
+
175
+ @classmethod
176
+ def export_schema_bundle_to_file(
177
+ cls,
178
+ path: str,
179
+ category: Optional[ToolCategory] = None,
180
+ ) -> str:
181
+ """Export tool schemas to a JSON file.
182
+
183
+ Args:
184
+ path: Output file path
185
+
186
+ Returns:
187
+ Absolute path to the exported file
188
+ """
189
+ from pathlib import Path
190
+
191
+ output_path = Path(path)
192
+ bundle = cls.export_schema_bundle(category=category)
193
+ if output_path.suffix.lower() in {".yaml", ".yml"}:
194
+ try:
195
+ import yaml
196
+ except ImportError as exc:
197
+ raise ImportError(
198
+ "PyYAML is required for YAML output. Install with: pip install PyYAML"
199
+ ) from exc
200
+ output_path.write_text(yaml.safe_dump(bundle, sort_keys=False))
201
+ else:
202
+ import json
203
+
204
+ output_path.write_text(json.dumps(bundle, indent=2))
205
+ return str(output_path.resolve())
206
+
207
+ @classmethod
208
+ def get_stats(cls) -> Dict[str, any]:
209
+ """Get registry statistics.
210
+
211
+ Returns:
212
+ Statistics dictionary
213
+ """
214
+ category_counts = {}
215
+ for tool in cls._tools.values():
216
+ category = tool.metadata.category.value
217
+ category_counts[category] = category_counts.get(category, 0) + 1
218
+
219
+ return {
220
+ "total_tools": len(cls._tools),
221
+ "categories": category_counts,
222
+ "tool_names": list(cls._tools.keys()),
223
+ }
224
+
225
+ def __repr__(self) -> str:
226
+ """String representation."""
227
+ return f"ToolRegistry(tools={len(self._tools)})"
@@ -0,0 +1,11 @@
1
+ """Security utilities for safe tool execution."""
2
+
3
+ from genxai.tools.security.sandbox import SafeExecutor, ExecutionTimeout
4
+ from genxai.tools.security.limits import ResourceLimits, ExecutionLimiter
5
+
6
+ __all__ = [
7
+ "SafeExecutor",
8
+ "ExecutionTimeout",
9
+ "ResourceLimits",
10
+ "ExecutionLimiter",
11
+ ]