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,233 @@
1
+ """JSON processor tool for parsing and manipulating JSON data."""
2
+
3
+ from typing import Any, Dict, List, Optional, Union
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 JSONProcessorTool(Tool):
13
+ """Process, parse, and manipulate JSON data."""
14
+
15
+ def __init__(self) -> None:
16
+ """Initialize JSON processor tool."""
17
+ metadata = ToolMetadata(
18
+ name="json_processor",
19
+ description="Parse, validate, query, and transform JSON data",
20
+ category=ToolCategory.DATA,
21
+ tags=["json", "data", "parsing", "transformation", "query"],
22
+ version="1.0.0",
23
+ )
24
+
25
+ parameters = [
26
+ ToolParameter(
27
+ name="data",
28
+ type="object",
29
+ description="JSON input. Can be a JSON string (for parse/validate/query/etc.) or a Python object (for stringify)",
30
+ required=True,
31
+ ),
32
+ ToolParameter(
33
+ name="operation",
34
+ type="string",
35
+ description="Operation to perform",
36
+ required=True,
37
+ enum=[
38
+ "parse",
39
+ "validate",
40
+ "query",
41
+ "transform",
42
+ "minify",
43
+ "prettify",
44
+ "stringify",
45
+ ],
46
+ ),
47
+ ToolParameter(
48
+ name="query_path",
49
+ type="string",
50
+ description="JSONPath query (for query operation)",
51
+ required=False,
52
+ ),
53
+ ToolParameter(
54
+ name="transform_rules",
55
+ type="object",
56
+ description="Transformation rules (for transform operation)",
57
+ required=False,
58
+ ),
59
+ ]
60
+
61
+ super().__init__(metadata, parameters)
62
+
63
+ async def _execute(
64
+ self,
65
+ data: Any,
66
+ operation: str,
67
+ query_path: Optional[str] = None,
68
+ transform_rules: Optional[Dict[str, Any]] = None,
69
+ ) -> Dict[str, Any]:
70
+ """Execute JSON processing.
71
+
72
+ Args:
73
+ data: JSON string
74
+ operation: Operation to perform
75
+ query_path: JSONPath query
76
+ transform_rules: Transformation rules
77
+
78
+ Returns:
79
+ Dictionary containing processed data
80
+ """
81
+ result: Dict[str, Any] = {
82
+ "operation": operation,
83
+ "success": False,
84
+ }
85
+
86
+ try:
87
+ # Support passing either a JSON string (common) or a Python object
88
+ # for operations like "stringify".
89
+ if operation == "stringify":
90
+ # Serialize python object to JSON.
91
+ result["data"] = json.dumps(data)
92
+ result["success"] = True
93
+ logger.info(f"JSON {operation} operation completed: success={result['success']}")
94
+ return result
95
+
96
+ if not isinstance(data, str):
97
+ raise ValueError("data must be a JSON string for this operation")
98
+
99
+ # Parse JSON
100
+ parsed_data = json.loads(data)
101
+ result["parsed"] = True
102
+
103
+ if operation == "parse":
104
+ result["data"] = parsed_data
105
+ result["type"] = type(parsed_data).__name__
106
+ if isinstance(parsed_data, dict):
107
+ result["keys"] = list(parsed_data.keys())
108
+ result["key_count"] = len(parsed_data)
109
+ elif isinstance(parsed_data, list):
110
+ result["length"] = len(parsed_data)
111
+ result["success"] = True
112
+
113
+ elif operation == "validate":
114
+ result["valid"] = True
115
+ result["data"] = parsed_data
116
+ result["type"] = type(parsed_data).__name__
117
+ result["success"] = True
118
+
119
+ elif operation == "query":
120
+ if not query_path:
121
+ raise ValueError("query_path is required for query operation")
122
+
123
+ # Simple JSONPath-like query implementation
124
+ query_result = self._query_json(parsed_data, query_path)
125
+ result["data"] = query_result
126
+ result["matches"] = len(query_result) if isinstance(query_result, list) else 1
127
+ result["success"] = True
128
+
129
+ elif operation == "transform":
130
+ if not transform_rules:
131
+ raise ValueError("transform_rules is required for transform operation")
132
+
133
+ transformed = self._transform_json(parsed_data, transform_rules)
134
+ result["data"] = transformed
135
+ result["success"] = True
136
+
137
+ elif operation == "minify":
138
+ minified = json.dumps(parsed_data, separators=(",", ":"))
139
+ result["data"] = minified
140
+ result["original_size"] = len(data)
141
+ result["minified_size"] = len(minified)
142
+ result["reduction_percent"] = (
143
+ (len(data) - len(minified)) / len(data) * 100
144
+ )
145
+ result["success"] = True
146
+
147
+ elif operation == "prettify":
148
+ prettified = json.dumps(parsed_data, indent=2, sort_keys=True)
149
+ result["data"] = prettified
150
+ result["success"] = True
151
+
152
+ except json.JSONDecodeError as e:
153
+ result["error"] = f"Invalid JSON: {str(e)}"
154
+ result["error_line"] = e.lineno
155
+ result["error_column"] = e.colno
156
+ except Exception as e:
157
+ result["error"] = str(e)
158
+
159
+ logger.info(f"JSON {operation} operation completed: success={result['success']}")
160
+ return result
161
+
162
+ def _query_json(self, data: Any, path: str) -> Any:
163
+ """Simple JSONPath-like query implementation.
164
+
165
+ Args:
166
+ data: JSON data
167
+ path: Query path (e.g., "$.users[0].name")
168
+
169
+ Returns:
170
+ Query result
171
+ """
172
+ # Remove leading $. if present
173
+ path = path.lstrip("$.")
174
+
175
+ parts = path.split(".")
176
+ current = data
177
+
178
+ for part in parts:
179
+ # Handle array indexing
180
+ if "[" in part and "]" in part:
181
+ key = part[:part.index("[")]
182
+ index_str = part[part.index("[") + 1:part.index("]")]
183
+
184
+ if key:
185
+ current = current[key]
186
+
187
+ if index_str == "*":
188
+ # Return all elements
189
+ return current if isinstance(current, list) else [current]
190
+ else:
191
+ index = int(index_str)
192
+ current = current[index]
193
+ else:
194
+ current = current[part]
195
+
196
+ return current
197
+
198
+ def _transform_json(self, data: Any, rules: Dict[str, Any]) -> Any:
199
+ """Transform JSON data based on rules.
200
+
201
+ Args:
202
+ data: JSON data
203
+ rules: Transformation rules
204
+
205
+ Returns:
206
+ Transformed data
207
+ """
208
+ if not isinstance(data, dict):
209
+ return data
210
+
211
+ result = {}
212
+
213
+ for new_key, rule in rules.items():
214
+ if isinstance(rule, str):
215
+ # Simple key mapping
216
+ if rule in data:
217
+ result[new_key] = data[rule]
218
+ elif isinstance(rule, dict):
219
+ # Complex transformation
220
+ if "source" in rule:
221
+ value = data.get(rule["source"])
222
+
223
+ # Apply transformations
224
+ if "uppercase" in rule and rule["uppercase"]:
225
+ value = value.upper() if isinstance(value, str) else value
226
+ if "lowercase" in rule and rule["lowercase"]:
227
+ value = value.lower() if isinstance(value, str) else value
228
+ if "default" in rule and value is None:
229
+ value = rule["default"]
230
+
231
+ result[new_key] = value
232
+
233
+ return result
@@ -0,0 +1,288 @@
1
+ """Text analyzer tool for analyzing and extracting insights from text."""
2
+
3
+ from typing import Any, Dict, List
4
+ import logging
5
+ import re
6
+ from collections import Counter
7
+
8
+ from genxai.tools.base import Tool, ToolMetadata, ToolParameter, ToolCategory
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ class TextAnalyzerTool(Tool):
14
+ """Analyze text and extract statistics, patterns, and insights."""
15
+
16
+ def __init__(self) -> None:
17
+ """Initialize text analyzer tool."""
18
+ metadata = ToolMetadata(
19
+ name="text_analyzer",
20
+ description="Analyze text for statistics, patterns, word frequency, and readability",
21
+ category=ToolCategory.DATA,
22
+ tags=["text", "analysis", "nlp", "statistics", "patterns"],
23
+ version="1.0.0",
24
+ )
25
+
26
+ parameters = [
27
+ ToolParameter(
28
+ name="text",
29
+ type="string",
30
+ description="Text to analyze",
31
+ required=True,
32
+ ),
33
+ ToolParameter(
34
+ name="operation",
35
+ type="string",
36
+ description="High-level analysis operation (backwards compatible with tests)",
37
+ required=False,
38
+ default="statistics",
39
+ enum=["word_count", "char_count", "sentiment", "statistics", "word_frequency", "patterns", "readability", "all"],
40
+ ),
41
+ ToolParameter(
42
+ name="top_n",
43
+ type="number",
44
+ description="Number of top items to return (for word frequency)",
45
+ required=False,
46
+ default=10,
47
+ min_value=1,
48
+ max_value=100,
49
+ ),
50
+ ]
51
+
52
+ super().__init__(metadata, parameters)
53
+
54
+ async def _execute(
55
+ self,
56
+ text: str,
57
+ operation: str = "statistics",
58
+ top_n: int = 10,
59
+ ) -> Dict[str, Any]:
60
+ """Execute text analysis.
61
+
62
+ Args:
63
+ text: Text to analyze
64
+ analysis_type: Type of analysis
65
+ top_n: Number of top items
66
+
67
+ Returns:
68
+ Dictionary containing analysis results
69
+ """
70
+ result: Dict[str, Any] = {
71
+ "operation": operation,
72
+ "success": False,
73
+ }
74
+
75
+ try:
76
+ # Simple operations expected by tests
77
+ if operation == "word_count":
78
+ result["word_count"] = len(text.split()) if text else 0
79
+ result["success"] = True
80
+ return result
81
+
82
+ if operation == "char_count":
83
+ result["char_count"] = len(text)
84
+ result["success"] = True
85
+ return result
86
+
87
+ if operation == "sentiment":
88
+ # Minimal / heuristic sentiment (test accepts True/False either way)
89
+ # This is intentionally simple to avoid heavy deps.
90
+ lowered = text.lower()
91
+ score = 0
92
+ for w in ["great", "good", "excellent", "love", "happy"]:
93
+ if w in lowered:
94
+ score += 1
95
+ for w in ["bad", "terrible", "hate", "sad"]:
96
+ if w in lowered:
97
+ score -= 1
98
+ result["sentiment"] = "positive" if score > 0 else "negative" if score < 0 else "neutral"
99
+ result["success"] = True
100
+ return result
101
+
102
+ # Legacy/internal richer analysis types
103
+ analysis_type = operation
104
+
105
+ if analysis_type == "statistics" or analysis_type == "all":
106
+ result["statistics"] = self._get_statistics(text)
107
+
108
+ if analysis_type == "word_frequency" or analysis_type == "all":
109
+ result["word_frequency"] = self._get_word_frequency(text, top_n)
110
+
111
+ if analysis_type == "patterns" or analysis_type == "all":
112
+ result["patterns"] = self._get_patterns(text)
113
+
114
+ if analysis_type == "readability" or analysis_type == "all":
115
+ result["readability"] = self._get_readability(text)
116
+
117
+ result["success"] = True
118
+
119
+ except Exception as e:
120
+ result["error"] = str(e)
121
+
122
+ logger.info(f"Text analysis ({operation}) completed: success={result['success']}")
123
+ return result
124
+
125
+ def _get_statistics(self, text: str) -> Dict[str, Any]:
126
+ """Get basic text statistics.
127
+
128
+ Args:
129
+ text: Input text
130
+
131
+ Returns:
132
+ Statistics dictionary
133
+ """
134
+ # Character counts
135
+ char_count = len(text)
136
+ char_count_no_spaces = len(text.replace(" ", ""))
137
+
138
+ # Word counts
139
+ words = text.split()
140
+ word_count = len(words)
141
+
142
+ # Sentence counts
143
+ sentences = re.split(r'[.!?]+', text)
144
+ sentences = [s.strip() for s in sentences if s.strip()]
145
+ sentence_count = len(sentences)
146
+
147
+ # Paragraph counts
148
+ paragraphs = text.split('\n\n')
149
+ paragraphs = [p.strip() for p in paragraphs if p.strip()]
150
+ paragraph_count = len(paragraphs)
151
+
152
+ # Line counts
153
+ lines = text.split('\n')
154
+ line_count = len(lines)
155
+
156
+ # Average lengths
157
+ avg_word_length = char_count_no_spaces / word_count if word_count > 0 else 0
158
+ avg_sentence_length = word_count / sentence_count if sentence_count > 0 else 0
159
+
160
+ return {
161
+ "character_count": char_count,
162
+ "character_count_no_spaces": char_count_no_spaces,
163
+ "word_count": word_count,
164
+ "sentence_count": sentence_count,
165
+ "paragraph_count": paragraph_count,
166
+ "line_count": line_count,
167
+ "average_word_length": round(avg_word_length, 2),
168
+ "average_sentence_length": round(avg_sentence_length, 2),
169
+ }
170
+
171
+ def _get_word_frequency(self, text: str, top_n: int) -> Dict[str, Any]:
172
+ """Get word frequency analysis.
173
+
174
+ Args:
175
+ text: Input text
176
+ top_n: Number of top words
177
+
178
+ Returns:
179
+ Word frequency dictionary
180
+ """
181
+ # Clean and tokenize
182
+ words = re.findall(r'\b\w+\b', text.lower())
183
+
184
+ # Count frequencies
185
+ word_counts = Counter(words)
186
+ total_unique_words = len(word_counts)
187
+
188
+ # Get top N words
189
+ top_words = word_counts.most_common(top_n)
190
+
191
+ return {
192
+ "total_unique_words": total_unique_words,
193
+ "top_words": [
194
+ {"word": word, "count": count, "frequency": count / len(words)}
195
+ for word, count in top_words
196
+ ],
197
+ }
198
+
199
+ def _get_patterns(self, text: str) -> Dict[str, Any]:
200
+ """Get pattern analysis.
201
+
202
+ Args:
203
+ text: Input text
204
+
205
+ Returns:
206
+ Patterns dictionary
207
+ """
208
+ # Email addresses
209
+ emails = re.findall(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b', text)
210
+
211
+ # URLs
212
+ urls = re.findall(r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', text)
213
+
214
+ # Phone numbers (simple pattern)
215
+ phones = re.findall(r'\b\d{3}[-.]?\d{3}[-.]?\d{4}\b', text)
216
+
217
+ # Numbers
218
+ numbers = re.findall(r'\b\d+\.?\d*\b', text)
219
+
220
+ # Hashtags
221
+ hashtags = re.findall(r'#\w+', text)
222
+
223
+ # Mentions
224
+ mentions = re.findall(r'@\w+', text)
225
+
226
+ return {
227
+ "emails": {"count": len(emails), "samples": emails[:5]},
228
+ "urls": {"count": len(urls), "samples": urls[:5]},
229
+ "phone_numbers": {"count": len(phones), "samples": phones[:5]},
230
+ "numbers": {"count": len(numbers), "samples": numbers[:10]},
231
+ "hashtags": {"count": len(hashtags), "samples": hashtags[:10]},
232
+ "mentions": {"count": len(mentions), "samples": mentions[:10]},
233
+ }
234
+
235
+ def _get_readability(self, text: str) -> Dict[str, Any]:
236
+ """Get readability metrics.
237
+
238
+ Args:
239
+ text: Input text
240
+
241
+ Returns:
242
+ Readability dictionary
243
+ """
244
+ # Basic counts
245
+ words = text.split()
246
+ word_count = len(words)
247
+
248
+ sentences = re.split(r'[.!?]+', text)
249
+ sentences = [s.strip() for s in sentences if s.strip()]
250
+ sentence_count = len(sentences)
251
+
252
+ # Count syllables (simple approximation)
253
+ syllable_count = 0
254
+ for word in words:
255
+ word = word.lower().strip('.,!?;:')
256
+ syllable_count += max(1, len(re.findall(r'[aeiou]+', word)))
257
+
258
+ # Flesch Reading Ease (approximation)
259
+ if sentence_count > 0 and word_count > 0:
260
+ avg_sentence_length = word_count / sentence_count
261
+ avg_syllables_per_word = syllable_count / word_count
262
+ flesch_score = 206.835 - 1.015 * avg_sentence_length - 84.6 * avg_syllables_per_word
263
+ flesch_score = max(0, min(100, flesch_score)) # Clamp between 0-100
264
+ else:
265
+ flesch_score = 0
266
+
267
+ # Interpret score
268
+ if flesch_score >= 90:
269
+ difficulty = "Very Easy"
270
+ elif flesch_score >= 80:
271
+ difficulty = "Easy"
272
+ elif flesch_score >= 70:
273
+ difficulty = "Fairly Easy"
274
+ elif flesch_score >= 60:
275
+ difficulty = "Standard"
276
+ elif flesch_score >= 50:
277
+ difficulty = "Fairly Difficult"
278
+ elif flesch_score >= 30:
279
+ difficulty = "Difficult"
280
+ else:
281
+ difficulty = "Very Difficult"
282
+
283
+ return {
284
+ "flesch_reading_ease": round(flesch_score, 2),
285
+ "difficulty_level": difficulty,
286
+ "average_sentence_length": round(word_count / sentence_count, 2) if sentence_count > 0 else 0,
287
+ "average_syllables_per_word": round(syllable_count / word_count, 2) if word_count > 0 else 0,
288
+ }
@@ -0,0 +1,175 @@
1
+ """XML processor tool for parsing and manipulating XML data."""
2
+
3
+ from typing import Any, Dict, List, Optional
4
+ import logging
5
+ import xml.etree.ElementTree as ET
6
+
7
+ from genxai.tools.base import Tool, ToolMetadata, ToolParameter, ToolCategory
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ class XMLProcessorTool(Tool):
13
+ """Process, parse, and manipulate XML data."""
14
+
15
+ def __init__(self) -> None:
16
+ """Initialize XML processor tool."""
17
+ metadata = ToolMetadata(
18
+ name="xml_processor",
19
+ description="Parse, validate, query, and transform XML data",
20
+ category=ToolCategory.DATA,
21
+ tags=["xml", "data", "parsing", "xpath", "transformation"],
22
+ version="1.0.0",
23
+ )
24
+
25
+ parameters = [
26
+ ToolParameter(
27
+ name="data",
28
+ type="string",
29
+ description="XML string to process",
30
+ required=True,
31
+ ),
32
+ ToolParameter(
33
+ name="operation",
34
+ type="string",
35
+ description="Operation to perform",
36
+ required=True,
37
+ enum=["parse", "validate", "query", "to_dict", "prettify"],
38
+ ),
39
+ ToolParameter(
40
+ name="xpath",
41
+ type="string",
42
+ description="XPath query (for query operation)",
43
+ required=False,
44
+ ),
45
+ ]
46
+
47
+ super().__init__(metadata, parameters)
48
+
49
+ async def _execute(
50
+ self,
51
+ data: str,
52
+ operation: str,
53
+ xpath: Optional[str] = None,
54
+ ) -> Dict[str, Any]:
55
+ """Execute XML processing.
56
+
57
+ Args:
58
+ data: XML string
59
+ operation: Operation to perform
60
+ xpath: XPath query
61
+
62
+ Returns:
63
+ Dictionary containing processed data
64
+ """
65
+ result: Dict[str, Any] = {
66
+ "operation": operation,
67
+ "success": False,
68
+ }
69
+
70
+ try:
71
+ # Parse XML
72
+ root = ET.fromstring(data)
73
+ result["parsed"] = True
74
+
75
+ if operation == "parse":
76
+ result["root_tag"] = root.tag
77
+ result["root_attributes"] = root.attrib
78
+ result["child_count"] = len(list(root))
79
+ result["success"] = True
80
+
81
+ elif operation == "validate":
82
+ # Basic validation (well-formed check)
83
+ result["valid"] = True
84
+ result["root_tag"] = root.tag
85
+ result["success"] = True
86
+
87
+ elif operation == "query":
88
+ if not xpath:
89
+ raise ValueError("xpath is required for query operation")
90
+
91
+ # Execute XPath query
92
+ elements = root.findall(xpath)
93
+ query_results = []
94
+
95
+ for elem in elements:
96
+ query_results.append({
97
+ "tag": elem.tag,
98
+ "text": elem.text,
99
+ "attributes": elem.attrib,
100
+ "children": [child.tag for child in elem],
101
+ })
102
+
103
+ result["data"] = query_results
104
+ result["match_count"] = len(query_results)
105
+ result["success"] = True
106
+
107
+ elif operation == "to_dict":
108
+ # Convert XML to dictionary
109
+ xml_dict = self._element_to_dict(root)
110
+ result["data"] = xml_dict
111
+ result["success"] = True
112
+
113
+ elif operation == "prettify":
114
+ # Pretty print XML
115
+ self._indent(root)
116
+ prettified = ET.tostring(root, encoding="unicode")
117
+ result["data"] = prettified
118
+ result["success"] = True
119
+
120
+ except ET.ParseError as e:
121
+ result["error"] = f"Invalid XML: {str(e)}"
122
+ except Exception as e:
123
+ result["error"] = str(e)
124
+
125
+ logger.info(f"XML {operation} operation completed: success={result['success']}")
126
+ return result
127
+
128
+ def _element_to_dict(self, element: ET.Element) -> Dict[str, Any]:
129
+ """Convert XML element to dictionary.
130
+
131
+ Args:
132
+ element: XML element
133
+
134
+ Returns:
135
+ Dictionary representation
136
+ """
137
+ result: Dict[str, Any] = {
138
+ "tag": element.tag,
139
+ }
140
+
141
+ # Add attributes
142
+ if element.attrib:
143
+ result["attributes"] = element.attrib
144
+
145
+ # Add text content
146
+ if element.text and element.text.strip():
147
+ result["text"] = element.text.strip()
148
+
149
+ # Add children
150
+ children = list(element)
151
+ if children:
152
+ result["children"] = [self._element_to_dict(child) for child in children]
153
+
154
+ return result
155
+
156
+ def _indent(self, elem: ET.Element, level: int = 0) -> None:
157
+ """Add indentation to XML element for pretty printing.
158
+
159
+ Args:
160
+ elem: XML element
161
+ level: Indentation level
162
+ """
163
+ indent = "\n" + " " * level
164
+ if len(elem):
165
+ if not elem.text or not elem.text.strip():
166
+ elem.text = indent + " "
167
+ if not elem.tail or not elem.tail.strip():
168
+ elem.tail = indent
169
+ for child in elem:
170
+ self._indent(child, level + 1)
171
+ if not child.tail or not child.tail.strip():
172
+ child.tail = indent
173
+ else:
174
+ if level and (not elem.tail or not elem.tail.strip()):
175
+ elem.tail = indent