quantalogic 0.35.0__py3-none-any.whl → 0.50.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 (107) hide show
  1. quantalogic/__init__.py +0 -4
  2. quantalogic/agent.py +603 -363
  3. quantalogic/agent_config.py +233 -46
  4. quantalogic/agent_factory.py +34 -22
  5. quantalogic/coding_agent.py +16 -14
  6. quantalogic/config.py +2 -1
  7. quantalogic/console_print_events.py +4 -8
  8. quantalogic/console_print_token.py +2 -2
  9. quantalogic/docs_cli.py +15 -10
  10. quantalogic/event_emitter.py +258 -83
  11. quantalogic/flow/__init__.py +23 -0
  12. quantalogic/flow/flow.py +595 -0
  13. quantalogic/flow/flow_extractor.py +672 -0
  14. quantalogic/flow/flow_generator.py +89 -0
  15. quantalogic/flow/flow_manager.py +407 -0
  16. quantalogic/flow/flow_manager_schema.py +169 -0
  17. quantalogic/flow/flow_yaml.md +419 -0
  18. quantalogic/generative_model.py +109 -77
  19. quantalogic/get_model_info.py +5 -5
  20. quantalogic/interactive_text_editor.py +100 -73
  21. quantalogic/main.py +17 -21
  22. quantalogic/model_info_list.py +3 -3
  23. quantalogic/model_info_litellm.py +14 -14
  24. quantalogic/prompts.py +2 -1
  25. quantalogic/{llm.py → quantlitellm.py} +29 -39
  26. quantalogic/search_agent.py +4 -4
  27. quantalogic/server/models.py +4 -1
  28. quantalogic/task_file_reader.py +5 -5
  29. quantalogic/task_runner.py +20 -20
  30. quantalogic/tool_manager.py +10 -21
  31. quantalogic/tools/__init__.py +98 -68
  32. quantalogic/tools/composio/composio.py +416 -0
  33. quantalogic/tools/{generate_database_report_tool.py → database/generate_database_report_tool.py} +4 -9
  34. quantalogic/tools/database/sql_query_tool_advanced.py +261 -0
  35. quantalogic/tools/document_tools/markdown_to_docx_tool.py +620 -0
  36. quantalogic/tools/document_tools/markdown_to_epub_tool.py +438 -0
  37. quantalogic/tools/document_tools/markdown_to_html_tool.py +362 -0
  38. quantalogic/tools/document_tools/markdown_to_ipynb_tool.py +319 -0
  39. quantalogic/tools/document_tools/markdown_to_latex_tool.py +420 -0
  40. quantalogic/tools/document_tools/markdown_to_pdf_tool.py +623 -0
  41. quantalogic/tools/document_tools/markdown_to_pptx_tool.py +319 -0
  42. quantalogic/tools/duckduckgo_search_tool.py +2 -4
  43. quantalogic/tools/finance/alpha_vantage_tool.py +440 -0
  44. quantalogic/tools/finance/ccxt_tool.py +373 -0
  45. quantalogic/tools/finance/finance_llm_tool.py +387 -0
  46. quantalogic/tools/finance/google_finance.py +192 -0
  47. quantalogic/tools/finance/market_intelligence_tool.py +520 -0
  48. quantalogic/tools/finance/technical_analysis_tool.py +491 -0
  49. quantalogic/tools/finance/tradingview_tool.py +336 -0
  50. quantalogic/tools/finance/yahoo_finance.py +236 -0
  51. quantalogic/tools/git/bitbucket_clone_repo_tool.py +181 -0
  52. quantalogic/tools/git/bitbucket_operations_tool.py +326 -0
  53. quantalogic/tools/git/clone_repo_tool.py +189 -0
  54. quantalogic/tools/git/git_operations_tool.py +532 -0
  55. quantalogic/tools/google_packages/google_news_tool.py +480 -0
  56. quantalogic/tools/grep_app_tool.py +123 -186
  57. quantalogic/tools/{dalle_e.py → image_generation/dalle_e.py} +37 -27
  58. quantalogic/tools/jinja_tool.py +6 -10
  59. quantalogic/tools/language_handlers/__init__.py +22 -9
  60. quantalogic/tools/list_directory_tool.py +131 -42
  61. quantalogic/tools/llm_tool.py +45 -15
  62. quantalogic/tools/llm_vision_tool.py +59 -7
  63. quantalogic/tools/markitdown_tool.py +17 -5
  64. quantalogic/tools/nasa_packages/models.py +47 -0
  65. quantalogic/tools/nasa_packages/nasa_apod_tool.py +232 -0
  66. quantalogic/tools/nasa_packages/nasa_neows_tool.py +147 -0
  67. quantalogic/tools/nasa_packages/services.py +82 -0
  68. quantalogic/tools/presentation_tools/presentation_llm_tool.py +396 -0
  69. quantalogic/tools/product_hunt/product_hunt_tool.py +258 -0
  70. quantalogic/tools/product_hunt/services.py +63 -0
  71. quantalogic/tools/rag_tool/__init__.py +48 -0
  72. quantalogic/tools/rag_tool/document_metadata.py +15 -0
  73. quantalogic/tools/rag_tool/query_response.py +20 -0
  74. quantalogic/tools/rag_tool/rag_tool.py +566 -0
  75. quantalogic/tools/rag_tool/rag_tool_beta.py +264 -0
  76. quantalogic/tools/read_html_tool.py +24 -38
  77. quantalogic/tools/replace_in_file_tool.py +10 -10
  78. quantalogic/tools/safe_python_interpreter_tool.py +10 -24
  79. quantalogic/tools/search_definition_names.py +2 -2
  80. quantalogic/tools/sequence_tool.py +14 -23
  81. quantalogic/tools/sql_query_tool.py +17 -19
  82. quantalogic/tools/tool.py +39 -15
  83. quantalogic/tools/unified_diff_tool.py +1 -1
  84. quantalogic/tools/utilities/csv_processor_tool.py +234 -0
  85. quantalogic/tools/utilities/download_file_tool.py +179 -0
  86. quantalogic/tools/utilities/mermaid_validator_tool.py +661 -0
  87. quantalogic/tools/utils/__init__.py +1 -4
  88. quantalogic/tools/utils/create_sample_database.py +24 -38
  89. quantalogic/tools/utils/generate_database_report.py +74 -82
  90. quantalogic/tools/wikipedia_search_tool.py +17 -21
  91. quantalogic/utils/ask_user_validation.py +1 -1
  92. quantalogic/utils/async_utils.py +35 -0
  93. quantalogic/utils/check_version.py +3 -5
  94. quantalogic/utils/get_all_models.py +2 -1
  95. quantalogic/utils/git_ls.py +21 -7
  96. quantalogic/utils/lm_studio_model_info.py +9 -7
  97. quantalogic/utils/python_interpreter.py +113 -43
  98. quantalogic/utils/xml_utility.py +178 -0
  99. quantalogic/version_check.py +1 -1
  100. quantalogic/welcome_message.py +7 -7
  101. quantalogic/xml_parser.py +0 -1
  102. {quantalogic-0.35.0.dist-info → quantalogic-0.50.0.dist-info}/METADATA +40 -1
  103. quantalogic-0.50.0.dist-info/RECORD +148 -0
  104. quantalogic-0.35.0.dist-info/RECORD +0 -102
  105. {quantalogic-0.35.0.dist-info → quantalogic-0.50.0.dist-info}/LICENSE +0 -0
  106. {quantalogic-0.35.0.dist-info → quantalogic-0.50.0.dist-info}/WHEEL +0 -0
  107. {quantalogic-0.35.0.dist-info → quantalogic-0.50.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,440 @@
1
+ import asyncio
2
+ import json
3
+ import time
4
+ from concurrent.futures import ThreadPoolExecutor
5
+ from dataclasses import dataclass
6
+ from enum import Enum
7
+ from pathlib import Path
8
+ from typing import Any, ClassVar, Dict, List
9
+
10
+ import numpy as np
11
+ import pandas as pd
12
+ import requests
13
+ import ta
14
+ from loguru import logger
15
+ from pydantic import model_validator
16
+
17
+ from quantalogic.tools import Tool, ToolArgument
18
+
19
+
20
+ class AssetType(str, Enum):
21
+ STOCK = "stock"
22
+ FOREX = "forex"
23
+ CRYPTO = "crypto"
24
+ COMMODITY = "commodity"
25
+ ETF = "etf"
26
+ INDEX = "index"
27
+
28
+ class DataType(str, Enum):
29
+ INTRADAY = "intraday"
30
+ DAILY = "daily"
31
+ WEEKLY = "weekly"
32
+ MONTHLY = "monthly"
33
+ QUOTE = "quote"
34
+ SEARCH = "search"
35
+ FUNDAMENTAL = "fundamental"
36
+ ECONOMIC = "economic"
37
+ NEWS = "news"
38
+
39
+ @dataclass
40
+ class MarketData:
41
+ """Container for market data and analysis."""
42
+ symbol: str
43
+ asset_type: AssetType
44
+ interval: str
45
+ data: pd.DataFrame
46
+ metadata: Dict[str, Any] = None
47
+ indicators: Dict[str, pd.Series] = None
48
+ fundamental_data: Dict[str, Any] = None
49
+ news_sentiment: Dict[str, Any] = None
50
+ economic_data: Dict[str, Any] = None
51
+
52
+ class AlphaVantageTool(Tool):
53
+ """Advanced multi-asset financial data and analysis tool using Alpha Vantage."""
54
+
55
+ name: ClassVar[str] = "alpha_vantage_tool"
56
+ description: ClassVar[str] = "Enhanced multi-asset financial data and analysis tool using Alpha Vantage API"
57
+
58
+ INTERVALS: ClassVar[List[str]] = ['1min', '5min', '15min', '30min', '60min', 'daily', 'weekly', 'monthly']
59
+
60
+ # Rate limiting settings
61
+ MAX_REQUESTS_PER_MINUTE: ClassVar[int] = 5
62
+ last_request_time: float = 0
63
+
64
+ arguments: ClassVar[list[ToolArgument]] = [
65
+ ToolArgument(
66
+ name="symbols",
67
+ arg_type="string",
68
+ description="Comma-separated list of symbols (e.g., 'AAPL,MSFT,GOOGL')",
69
+ required=True
70
+ ),
71
+ ToolArgument(
72
+ name="asset_type",
73
+ arg_type="string",
74
+ description="Type of asset (stock/forex/crypto)",
75
+ required=True
76
+ ),
77
+ ToolArgument(
78
+ name="function",
79
+ arg_type="string",
80
+ description="Alpha Vantage function (TIME_SERIES_INTRADAY/DAILY/WEEKLY/MONTHLY)",
81
+ required=True
82
+ ),
83
+ ToolArgument(
84
+ name="interval",
85
+ arg_type="string",
86
+ description="Time interval (1min/5min/15min/30min/60min) - only for intraday",
87
+ required=False,
88
+ default="5min"
89
+ ),
90
+ ToolArgument(
91
+ name="outputsize",
92
+ arg_type="string",
93
+ description="Output size (compact/full)",
94
+ required=False,
95
+ default="compact"
96
+ ),
97
+ ToolArgument(
98
+ name="api_key",
99
+ arg_type="string",
100
+ description="Alpha Vantage API key",
101
+ required=True
102
+ ),
103
+ ToolArgument(
104
+ name="indicators",
105
+ arg_type="string",
106
+ description="Comma-separated technical indicators to calculate (e.g., 'SMA,RSI,MACD')",
107
+ required=False,
108
+ default="all"
109
+ ),
110
+ ToolArgument(
111
+ name="lookback_periods",
112
+ arg_type="string",
113
+ description="Number of periods to analyze",
114
+ required=False,
115
+ default="500"
116
+ )
117
+ ]
118
+
119
+ @model_validator(mode='before')
120
+ def validate_arguments(cls, values):
121
+ """Validate tool arguments."""
122
+ try:
123
+ # Validate interval format
124
+ if 'interval' in values and values['interval'] not in cls.INTERVALS:
125
+ raise ValueError(f"Invalid interval: {values['interval']}")
126
+
127
+ # Validate symbols and asset types match
128
+ if len(values.get('symbols', '').split(',')) != len(values.get('asset_type', '').split(',')):
129
+ raise ValueError("Number of symbols must match number of asset types")
130
+
131
+ return values
132
+ except Exception as e:
133
+ logger.error(f"Error validating arguments: {e}")
134
+ raise
135
+
136
+ def __init__(self, **kwargs):
137
+ super().__init__(**kwargs)
138
+ self.api_key = None
139
+ self.base_url = "https://www.alphavantage.co/query"
140
+ self.cache = {}
141
+ self.executor = ThreadPoolExecutor(max_workers=4)
142
+
143
+ def _load_api_key(self, api_key_path: str) -> None:
144
+ """Load Alpha Vantage API key from config file."""
145
+ try:
146
+ config_path = Path(api_key_path)
147
+ if config_path.exists():
148
+ with open(config_path) as f:
149
+ config = json.load(f)
150
+ self.api_key = config.get('api_key')
151
+ if not self.api_key:
152
+ raise ValueError("API key not found in config file")
153
+ except Exception as e:
154
+ logger.error(f"Error loading API key: {e}")
155
+ raise
156
+
157
+ async def _make_request(self, params: Dict[str, str]) -> Dict:
158
+ """Make rate-limited request to Alpha Vantage API."""
159
+ try:
160
+ # Implement rate limiting
161
+ current_time = time.time()
162
+ time_since_last_request = current_time - self.last_request_time
163
+ if time_since_last_request < (60 / self.MAX_REQUESTS_PER_MINUTE):
164
+ await asyncio.sleep((60 / self.MAX_REQUESTS_PER_MINUTE) - time_since_last_request)
165
+
166
+ params['apikey'] = self.api_key
167
+
168
+ # Make request using ThreadPoolExecutor for blocking IO
169
+ response = await asyncio.get_event_loop().run_in_executor(
170
+ self.executor,
171
+ lambda: requests.get(self.base_url, params=params)
172
+ )
173
+ response.raise_for_status()
174
+
175
+ self.last_request_time = time.time()
176
+ return response.json()
177
+
178
+ except Exception as e:
179
+ logger.error(f"Error making API request: {e}")
180
+ raise
181
+
182
+ async def _fetch_time_series(
183
+ self,
184
+ symbol: str,
185
+ asset_type: AssetType,
186
+ interval: str,
187
+ output_size: str
188
+ ) -> MarketData:
189
+ """Fetch time series data for any asset type."""
190
+ try:
191
+ # Determine the appropriate API function
192
+ function = self._get_time_series_function(asset_type, interval)
193
+
194
+ params = {
195
+ 'function': function,
196
+ 'symbol': symbol,
197
+ 'outputsize': output_size
198
+ }
199
+
200
+ if 'INTRADAY' in function:
201
+ params['interval'] = interval
202
+
203
+ data = await self._make_request(params)
204
+
205
+ # Parse the response into a DataFrame
206
+ time_series_key = [k for k in data.keys() if 'Time Series' in k][0]
207
+ df = pd.DataFrame.from_dict(data[time_series_key], orient='index')
208
+
209
+ # Clean up column names and convert to numeric
210
+ df.columns = [col.split('. ')[1].lower() for col in df.columns]
211
+ for col in df.columns:
212
+ df[col] = pd.to_numeric(df[col])
213
+
214
+ df.index = pd.to_datetime(df.index)
215
+
216
+ return MarketData(
217
+ symbol=symbol,
218
+ asset_type=asset_type,
219
+ interval=interval,
220
+ data=df,
221
+ metadata=data.get('Meta Data')
222
+ )
223
+
224
+ except Exception as e:
225
+ logger.error(f"Error fetching time series data for {symbol}: {e}")
226
+ raise
227
+
228
+ async def _fetch_fundamental_data(self, symbol: str) -> Dict[str, Any]:
229
+ """Fetch comprehensive fundamental data for stocks."""
230
+ try:
231
+ fundamental_data = {}
232
+
233
+ # Company Overview
234
+ overview = await self._make_request({
235
+ 'function': 'OVERVIEW',
236
+ 'symbol': symbol
237
+ })
238
+ fundamental_data['overview'] = overview
239
+
240
+ # Income Statement
241
+ income_stmt = await self._make_request({
242
+ 'function': 'INCOME_STATEMENT',
243
+ 'symbol': symbol
244
+ })
245
+ fundamental_data['income_statement'] = income_stmt
246
+
247
+ # Balance Sheet
248
+ balance_sheet = await self._make_request({
249
+ 'function': 'BALANCE_SHEET',
250
+ 'symbol': symbol
251
+ })
252
+ fundamental_data['balance_sheet'] = balance_sheet
253
+
254
+ # Cash Flow
255
+ cash_flow = await self._make_request({
256
+ 'function': 'CASH_FLOW',
257
+ 'symbol': symbol
258
+ })
259
+ fundamental_data['cash_flow'] = cash_flow
260
+
261
+ # Earnings
262
+ earnings = await self._make_request({
263
+ 'function': 'EARNINGS',
264
+ 'symbol': symbol
265
+ })
266
+ fundamental_data['earnings'] = earnings
267
+
268
+ return fundamental_data
269
+
270
+ except Exception as e:
271
+ logger.error(f"Error fetching fundamental data for {symbol}: {e}")
272
+ raise
273
+
274
+ async def _fetch_economic_data(self, indicators: List[str]) -> Dict[str, pd.DataFrame]:
275
+ """Fetch economic indicators data."""
276
+ try:
277
+ economic_data = {}
278
+
279
+ for indicator in indicators:
280
+ data = await self._make_request({
281
+ 'function': indicator
282
+ })
283
+
284
+ # Convert to DataFrame
285
+ df = pd.DataFrame.from_dict(data['data'], orient='index')
286
+ df.index = pd.to_datetime(df.index)
287
+ economic_data[indicator] = df
288
+
289
+ return economic_data
290
+
291
+ except Exception as e:
292
+ logger.error(f"Error fetching economic data: {e}")
293
+ raise
294
+
295
+ async def _fetch_news_sentiment(self, symbols: List[str]) -> Dict[str, Any]:
296
+ """Fetch news and sentiment data."""
297
+ try:
298
+ params = {
299
+ 'function': 'NEWS_SENTIMENT',
300
+ 'tickers': ','.join(symbols)
301
+ }
302
+
303
+ news_data = await self._make_request(params)
304
+ return news_data
305
+
306
+ except Exception as e:
307
+ logger.error(f"Error fetching news sentiment: {e}")
308
+ raise
309
+
310
+ def _calculate_technical_indicators(self, market_data: MarketData) -> None:
311
+ """Calculate comprehensive technical indicators."""
312
+ df = market_data.data
313
+ indicators = {}
314
+
315
+ try:
316
+ # Trend Indicators
317
+ indicators['sma_20'] = ta.trend.sma_indicator(df['close'], 20)
318
+ indicators['sma_50'] = ta.trend.sma_indicator(df['close'], 50)
319
+ indicators['sma_200'] = ta.trend.sma_indicator(df['close'], 200)
320
+ indicators['ema_12'] = ta.trend.ema_indicator(df['close'], 12)
321
+ indicators['ema_26'] = ta.trend.ema_indicator(df['close'], 26)
322
+ indicators['macd'] = ta.trend.macd(df['close'])
323
+ indicators['macd_signal'] = ta.trend.macd_signal(df['close'])
324
+ indicators['adx'] = ta.trend.adx(df['high'], df['low'], df['close'])
325
+
326
+ # Momentum Indicators
327
+ indicators['rsi'] = ta.momentum.rsi(df['close'])
328
+ indicators['stoch'] = ta.momentum.stoch(df['high'], df['low'], df['close'])
329
+ indicators['stoch_signal'] = ta.momentum.stoch_signal(df['high'], df['low'], df['close'])
330
+ indicators['williams_r'] = ta.momentum.williams_r(df['high'], df['low'], df['close'])
331
+
332
+ # Volatility Indicators
333
+ indicators['bbands_upper'] = ta.volatility.bollinger_hband(df['close'])
334
+ indicators['bbands_lower'] = ta.volatility.bollinger_lband(df['close'])
335
+ indicators['atr'] = ta.volatility.average_true_range(df['high'], df['low'], df['close'])
336
+
337
+ # Volume Indicators
338
+ if 'volume' in df.columns:
339
+ indicators['obv'] = ta.volume.on_balance_volume(df['close'], df['volume'])
340
+ indicators['mfi'] = ta.volume.money_flow_index(df['high'], df['low'], df['close'], df['volume'])
341
+
342
+ market_data.indicators = indicators
343
+
344
+ except Exception as e:
345
+ logger.error(f"Error calculating technical indicators: {e}")
346
+ raise
347
+
348
+ def _get_time_series_function(self, asset_type: AssetType, interval: str) -> str:
349
+ """Get the appropriate Alpha Vantage API function based on asset type and interval."""
350
+ if interval in ['1min', '5min', '15min', '30min', '60min']:
351
+ suffix = '_INTRADAY'
352
+ elif interval == 'daily':
353
+ suffix = '_DAILY'
354
+ elif interval == 'weekly':
355
+ suffix = '_WEEKLY'
356
+ else:
357
+ suffix = '_MONTHLY'
358
+
359
+ if asset_type == AssetType.STOCK:
360
+ return f'TIME_SERIES{suffix}'
361
+ elif asset_type == AssetType.FOREX:
362
+ return f'FX{suffix}'
363
+ elif asset_type == AssetType.CRYPTO:
364
+ return f'CRYPTO{suffix}'
365
+ elif asset_type == AssetType.COMMODITY:
366
+ return f'COMMODITY{suffix}'
367
+ else:
368
+ return f'TIME_SERIES{suffix}'
369
+
370
+ async def execute(self, **kwargs) -> Dict[str, Any]:
371
+ """Execute the Alpha Vantage tool with comprehensive analysis."""
372
+ try:
373
+ # Load API key
374
+ self._load_api_key(kwargs.get('api_key_path', 'config/alphavantage_config.json'))
375
+
376
+ symbols = kwargs['symbols'].split(',')
377
+ asset_type = AssetType(kwargs['asset_type'])
378
+ function = kwargs['function']
379
+ interval = kwargs.get('interval', '5min')
380
+ outputsize = kwargs.get('outputsize', 'compact')
381
+ lookback_periods = int(kwargs.get('lookback_periods', '500'))
382
+ indicators = kwargs.get('indicators', 'all').split(',')
383
+
384
+ results = {}
385
+
386
+ # Fetch time series data for each symbol
387
+ for symbol in symbols:
388
+ if any(dt in [DataType.INTRADAY, DataType.DAILY, DataType.WEEKLY, DataType.MONTHLY] for dt in [DataType(function)]):
389
+ market_data = await self._fetch_time_series(
390
+ symbol, asset_type, interval, outputsize
391
+ )
392
+
393
+ # Calculate technical indicators
394
+ self._calculate_technical_indicators(market_data)
395
+
396
+ # Fetch fundamental data for stocks
397
+ if asset_type == AssetType.STOCK and function == 'FUNDAMENTAL':
398
+ market_data.fundamental_data = await self._fetch_fundamental_data(symbol)
399
+
400
+ results[symbol] = {
401
+ 'market_data': market_data.data.to_dict(orient='records'),
402
+ 'metadata': market_data.metadata,
403
+ 'indicators': {k: v.to_dict() for k, v in market_data.indicators.items()} if market_data.indicators else None,
404
+ 'fundamental_data': market_data.fundamental_data
405
+ }
406
+
407
+ # Fetch news sentiment if requested
408
+ if function == 'NEWS':
409
+ news_data = await self._fetch_news_sentiment(symbols)
410
+ for symbol in symbols:
411
+ if symbol in results:
412
+ results[symbol]['news_sentiment'] = news_data
413
+
414
+ # Fetch economic data if requested
415
+ if function == 'ECONOMIC':
416
+ economic_indicators = [
417
+ 'REAL_GDP',
418
+ 'REAL_GDP_PER_CAPITA',
419
+ 'TREASURY_YIELD',
420
+ 'FEDERAL_FUNDS_RATE',
421
+ 'CPI',
422
+ 'INFLATION',
423
+ 'RETAIL_SALES',
424
+ 'DURABLES',
425
+ 'UNEMPLOYMENT',
426
+ 'NONFARM_PAYROLL'
427
+ ]
428
+ economic_data = await self._fetch_economic_data(economic_indicators)
429
+ for symbol in results:
430
+ results[symbol]['economic_data'] = economic_data
431
+
432
+ return results
433
+
434
+ except Exception as e:
435
+ logger.error(f"Error executing Alpha Vantage tool: {e}")
436
+ raise
437
+
438
+ def validate_arguments(self, **kwargs) -> bool:
439
+ """Validate the provided arguments."""
440
+ return super().validate_arguments(**kwargs)