aigroup-econ-mcp 0.3.5__py3-none-any.whl → 0.3.7__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.

Potentially problematic release.


This version of aigroup-econ-mcp might be problematic. Click here for more details.

@@ -10,7 +10,7 @@ AIGroup 计量经济学 MCP 服务
10
10
  - 模型诊断
11
11
  """
12
12
 
13
- __version__ = "0.3.4"
13
+ __version__ = "0.3.7"
14
14
  __author__ = "AIGroup"
15
15
  __description__ = "专业计量经济学MCP工具 - 让大模型直接进行数据分析"
16
16
 
@@ -9,6 +9,9 @@ from collections.abc import AsyncIterator
9
9
  from contextlib import asynccontextmanager
10
10
  from dataclasses import dataclass
11
11
  import importlib.metadata
12
+ import json
13
+ import os
14
+ from pathlib import Path
12
15
 
13
16
  import pandas as pd
14
17
  import numpy as np
@@ -21,6 +24,55 @@ from mcp.server.fastmcp import FastMCP, Context
21
24
  from mcp.server.session import ServerSession
22
25
  from mcp.types import CallToolResult, TextContent
23
26
 
27
+ # 导入超时管理器
28
+ from .tools.timeout import TimeoutManager
29
+
30
+
31
+ # 性能配置加载函数
32
+ def load_performance_config() -> Dict[str, Any]:
33
+ """加载性能配置"""
34
+ config_paths = [
35
+ Path("config/performance_config.json"),
36
+ Path("performance_config.json"),
37
+ Path.cwd() / "config" / "performance_config.json"
38
+ ]
39
+
40
+ for config_path in config_paths:
41
+ if config_path.exists():
42
+ try:
43
+ with open(config_path, 'r', encoding='utf-8') as f:
44
+ config = json.load(f)
45
+ print(f"✅ 性能配置已加载: {config_path}")
46
+ return config
47
+ except Exception as e:
48
+ print(f"⚠️ 加载性能配置失败 {config_path}: {e}")
49
+
50
+ # 返回默认配置
51
+ default_config = {
52
+ "performance": {
53
+ "cache": {"enabled": True, "ttl": 3600, "max_size": 1000},
54
+ "monitoring": {"enabled": True, "memory_check_interval": 1.0},
55
+ "timeouts": {
56
+ "simple_models": 30,
57
+ "complex_models": 120,
58
+ "machine_learning": 300,
59
+ "time_series": 180
60
+ }
61
+ },
62
+ "models": {
63
+ "descriptive_statistics": {"timeout": 30},
64
+ "ols_regression": {"timeout": 60},
65
+ "time_series_analysis": {"timeout": 120},
66
+ "panel_data_analysis": {"timeout": 180},
67
+ "var_model": {"timeout": 300},
68
+ "garch_model": {"timeout": 240},
69
+ "random_forest": {"timeout": 300},
70
+ "gradient_boosting": {"timeout": 300}
71
+ }
72
+ }
73
+ print("⚠️ 使用默认性能配置")
74
+ return default_config
75
+
24
76
 
25
77
  # 数据模型定义 - 使用Pydantic实现结构化输出
26
78
  class DescriptiveStatsResult(BaseModel):
@@ -70,6 +122,8 @@ class AppContext:
70
122
  """应用上下文,包含共享资源"""
71
123
  config: Dict[str, Any]
72
124
  version: str = importlib.metadata.version("aigroup-econ-mcp")
125
+ performance_config: Dict[str, Any] = None
126
+ timeout_manager: Any = None
73
127
 
74
128
 
75
129
  # 服务器图标(已移除,因为MCP库不再支持Icon类)
@@ -86,8 +140,20 @@ async def lifespan(server: FastMCP) -> AsyncIterator[AppContext]:
86
140
  "data_types": ["cross_section", "time_series", "panel"]
87
141
  }
88
142
 
143
+ # 加载性能配置
144
+ performance_config = load_performance_config()
145
+
146
+ # 初始化超时管理器
147
+ timeout_manager = TimeoutManager()
148
+ timeout_manager.set_timeout_config(performance_config.get("timeouts", {}))
149
+
89
150
  try:
90
- yield AppContext(config=config, version=importlib.metadata.version("aigroup-econ-mcp"))
151
+ yield AppContext(
152
+ config=config,
153
+ version=importlib.metadata.version("aigroup-econ-mcp"),
154
+ performance_config=performance_config,
155
+ timeout_manager=timeout_manager
156
+ )
91
157
  finally:
92
158
  # 清理资源
93
159
  pass
@@ -202,51 +268,44 @@ async def descriptive_statistics(
202
268
  await ctx.info(f"开始计算描述性统计,处理 {len(data)} 个变量")
203
269
 
204
270
  try:
205
- # 数据验证
206
- if not data:
207
- raise ValueError("数据不能为空")
271
+ # 获取超时管理器
272
+ timeout_manager = ctx.session.context.timeout_manager
273
+ model_timeout = ctx.session.context.performance_config.get("models", {}).get("descriptive_statistics", {}).get("timeout", 30)
208
274
 
209
- df = pd.DataFrame(data)
210
-
211
- # 检查数据一致性
212
- if len(df.columns) == 0:
213
- raise ValueError("至少需要一个变量")
214
-
215
- # 基础统计量 - 修复:返回所有变量的综合统计
216
- result = DescriptiveStatsResult(
217
- count=len(df),
218
- mean=df.mean().mean(), # 所有变量的均值
219
- std=df.std().mean(), # 所有变量的标准差均值
220
- min=df.min().min(), # 所有变量的最小值
221
- max=df.max().max(), # 所有变量的最大值
222
- median=df.median().mean(), # 所有变量的中位数均值
223
- skewness=df.skew().mean(), # 所有变量的偏度均值
224
- kurtosis=df.kurtosis().mean() # 所有变量的峰度均值
275
+ # 使用超时控制执行计算
276
+ result = await timeout_manager.execute_with_timeout(
277
+ model_name="descriptive_statistics",
278
+ timeout_seconds=model_timeout,
279
+ func=_compute_descriptive_stats,
280
+ data=data
225
281
  )
226
282
 
227
- # 计算相关系数矩阵
228
- correlation_matrix = df.corr().round(4)
229
-
230
- await ctx.info(f"描述性统计计算完成,样本大小: {len(df)}")
283
+ await ctx.info(f"描述性统计计算完成,样本大小: {result['count']}")
231
284
 
232
285
  return CallToolResult(
233
286
  content=[
234
287
  TextContent(
235
288
  type="text",
236
289
  text=f"描述性统计结果:\n"
237
- f"均值: {result.mean:.4f}\n"
238
- f"标准差: {result.std:.4f}\n"
239
- f"最小值: {result.min:.4f}\n"
240
- f"最大值: {result.max:.4f}\n"
241
- f"中位数: {result.median:.4f}\n"
242
- f"偏度: {result.skewness:.4f}\n"
243
- f"峰度: {result.kurtosis:.4f}\n\n"
244
- f"相关系数矩阵:\n{correlation_matrix.to_string()}"
290
+ f"均值: {result['mean']:.4f}\n"
291
+ f"标准差: {result['std']:.4f}\n"
292
+ f"最小值: {result['min']:.4f}\n"
293
+ f"最大值: {result['max']:.4f}\n"
294
+ f"中位数: {result['median']:.4f}\n"
295
+ f"偏度: {result['skewness']:.4f}\n"
296
+ f"峰度: {result['kurtosis']:.4f}\n\n"
297
+ f"相关系数矩阵:\n{result['correlation_matrix']}"
245
298
  )
246
299
  ],
247
- structuredContent=result.model_dump()
300
+ structuredContent=result
248
301
  )
249
302
 
303
+ except TimeoutError:
304
+ await ctx.error("描述性统计计算超时,请减少数据量或调整配置")
305
+ return CallToolResult(
306
+ content=[TextContent(type="text", text="错误: 计算超时,请减少数据量或调整配置")],
307
+ isError=True
308
+ )
250
309
  except Exception as e:
251
310
  await ctx.error(f"计算描述性统计时出错: {str(e)}")
252
311
  return CallToolResult(
@@ -255,6 +314,37 @@ async def descriptive_statistics(
255
314
  )
256
315
 
257
316
 
317
+ def _compute_descriptive_stats(data: Dict[str, List[float]]) -> Dict[str, Any]:
318
+ """计算描述性统计(同步函数,用于超时控制)"""
319
+ # 数据验证
320
+ if not data:
321
+ raise ValueError("数据不能为空")
322
+
323
+ df = pd.DataFrame(data)
324
+
325
+ # 检查数据一致性
326
+ if len(df.columns) == 0:
327
+ raise ValueError("至少需要一个变量")
328
+
329
+ # 基础统计量
330
+ result = {
331
+ "count": len(df),
332
+ "mean": float(df.mean().mean()), # 所有变量的均值
333
+ "std": float(df.std().mean()), # 所有变量的标准差均值
334
+ "min": float(df.min().min()), # 所有变量的最小值
335
+ "max": float(df.max().max()), # 所有变量的最大值
336
+ "median": float(df.median().mean()), # 所有变量的中位数均值
337
+ "skewness": float(df.skew().mean()), # 所有变量的偏度均值
338
+ "kurtosis": float(df.kurtosis().mean()) # 所有变量的峰度均值
339
+ }
340
+
341
+ # 计算相关系数矩阵
342
+ correlation_matrix = df.corr().round(4)
343
+ result["correlation_matrix"] = correlation_matrix.to_string()
344
+
345
+ return result
346
+
347
+
258
348
  @mcp.tool()
259
349
  async def ols_regression(
260
350
  ctx: Context[ServerSession, AppContext],
@@ -342,59 +432,18 @@ async def ols_regression(
342
432
  await ctx.info(f"开始OLS回归分析,样本大小: {len(y_data)},自变量数量: {len(x_data[0]) if x_data else 0}")
343
433
 
344
434
  try:
345
- # 数据验证
346
- if not y_data:
347
- raise ValueError("因变量数据不能为空")
348
- if not x_data:
349
- raise ValueError("自变量数据不能为空")
350
- if len(y_data) != len(x_data):
351
- raise ValueError(f"因变量和自变量的观测数量不一致: y_data={len(y_data)}, x_data={len(x_data)}")
352
-
353
- # 准备数据
354
- X = np.array(x_data)
355
- y = np.array(y_data)
356
-
357
- # 添加常数项
358
- X_with_const = sm.add_constant(X)
359
-
360
- # 拟合模型
361
- model = sm.OLS(y, X_with_const).fit()
362
-
363
- # 构建系数字典
364
- conf_int = model.conf_int()
365
- coefficients = {}
435
+ # 获取超时管理器
436
+ timeout_manager = ctx.session.context.timeout_manager
437
+ model_timeout = ctx.session.context.performance_config.get("models", {}).get("ols_regression", {}).get("timeout", 60)
366
438
 
367
- # 修复:正确处理feature_names为None的情况
368
- if feature_names is None:
369
- feature_names = [f"x{i+1}" for i in range(X.shape[1])]
370
- elif len(feature_names) != X.shape[1]:
371
- await ctx.warning(f"提供的feature_names数量({len(feature_names)})与自变量数量({X.shape[1]})不匹配,使用默认命名")
372
- feature_names = [f"x{i+1}" for i in range(X.shape[1])]
373
-
374
- for i, coef in enumerate(model.params):
375
- if i == 0:
376
- var_name = "const"
377
- else:
378
- var_name = feature_names[i-1]
379
-
380
- coefficients[var_name] = {
381
- "coef": float(coef), # 转换numpy.float64为float
382
- "std_err": float(model.bse[i]),
383
- "t_value": float(model.tvalues[i]),
384
- "p_value": float(model.pvalues[i]),
385
- "ci_lower": float(conf_int[i][0]),
386
- "ci_upper": float(conf_int[i][1])
387
- }
388
-
389
- # 构建结果
390
- result = OLSRegressionResult(
391
- rsquared=float(model.rsquared),
392
- rsquared_adj=float(model.rsquared_adj),
393
- f_statistic=float(model.fvalue),
394
- f_pvalue=float(model.f_pvalue),
395
- aic=float(model.aic),
396
- bic=float(model.bic),
397
- coefficients=coefficients
439
+ # 使用超时控制执行计算
440
+ result = await timeout_manager.execute_with_timeout(
441
+ model_name="ols_regression",
442
+ timeout_seconds=model_timeout,
443
+ func=_compute_ols_regression,
444
+ y_data=y_data,
445
+ x_data=x_data,
446
+ feature_names=feature_names
398
447
  )
399
448
 
400
449
  await ctx.info("OLS回归分析完成")
@@ -404,16 +453,22 @@ async def ols_regression(
404
453
  TextContent(
405
454
  type="text",
406
455
  text=f"OLS回归分析结果:\n"
407
- f"R² = {result.rsquared:.4f}\n"
408
- f"调整R² = {result.rsquared_adj:.4f}\n"
409
- f"F统计量 = {result.f_statistic:.4f} (p = {result.f_pvalue:.4f})\n"
410
- f"AIC = {result.aic:.2f}, BIC = {result.bic:.2f}\n\n"
411
- f"回归系数:\n{model.summary().tables[1]}"
456
+ f"R² = {result['rsquared']:.4f}\n"
457
+ f"调整R² = {result['rsquared_adj']:.4f}\n"
458
+ f"F统计量 = {result['f_statistic']:.4f} (p = {result['f_pvalue']:.4f})\n"
459
+ f"AIC = {result['aic']:.2f}, BIC = {result['bic']:.2f}\n\n"
460
+ f"回归系数:\n{result['summary_table']}"
412
461
  )
413
462
  ],
414
- structuredContent=result.model_dump()
463
+ structuredContent=result
415
464
  )
416
465
 
466
+ except TimeoutError:
467
+ await ctx.error("OLS回归分析超时,请减少数据量或调整配置")
468
+ return CallToolResult(
469
+ content=[TextContent(type="text", text="错误: 计算超时,请减少数据量或调整配置")],
470
+ isError=True
471
+ )
417
472
  except Exception as e:
418
473
  await ctx.error(f"OLS回归分析出错: {str(e)}")
419
474
  return CallToolResult(
@@ -422,6 +477,66 @@ async def ols_regression(
422
477
  )
423
478
 
424
479
 
480
+ def _compute_ols_regression(y_data: List[float], x_data: List[List[float]], feature_names: Optional[List[str]] = None) -> Dict[str, Any]:
481
+ """计算OLS回归(同步函数,用于超时控制)"""
482
+ # 数据验证
483
+ if not y_data:
484
+ raise ValueError("因变量数据不能为空")
485
+ if not x_data:
486
+ raise ValueError("自变量数据不能为空")
487
+ if len(y_data) != len(x_data):
488
+ raise ValueError(f"因变量和自变量的观测数量不一致: y_data={len(y_data)}, x_data={len(x_data)}")
489
+
490
+ # 准备数据
491
+ X = np.array(x_data)
492
+ y = np.array(y_data)
493
+
494
+ # 添加常数项
495
+ X_with_const = sm.add_constant(X)
496
+
497
+ # 拟合模型
498
+ model = sm.OLS(y, X_with_const).fit()
499
+
500
+ # 构建系数字典
501
+ conf_int = model.conf_int()
502
+ coefficients = {}
503
+
504
+ # 处理feature_names
505
+ if feature_names is None:
506
+ feature_names = [f"x{i+1}" for i in range(X.shape[1])]
507
+ elif len(feature_names) != X.shape[1]:
508
+ feature_names = [f"x{i+1}" for i in range(X.shape[1])]
509
+
510
+ for i, coef in enumerate(model.params):
511
+ if i == 0:
512
+ var_name = "const"
513
+ else:
514
+ var_name = feature_names[i-1]
515
+
516
+ coefficients[var_name] = {
517
+ "coef": float(coef),
518
+ "std_err": float(model.bse[i]),
519
+ "t_value": float(model.tvalues[i]),
520
+ "p_value": float(model.pvalues[i]),
521
+ "ci_lower": float(conf_int[i][0]),
522
+ "ci_upper": float(conf_int[i][1])
523
+ }
524
+
525
+ # 构建结果
526
+ result = {
527
+ "rsquared": float(model.rsquared),
528
+ "rsquared_adj": float(model.rsquared_adj),
529
+ "f_statistic": float(model.fvalue),
530
+ "f_pvalue": float(model.f_pvalue),
531
+ "aic": float(model.aic),
532
+ "bic": float(model.bic),
533
+ "coefficients": coefficients,
534
+ "summary_table": str(model.summary().tables[1])
535
+ }
536
+
537
+ return result
538
+
539
+
425
540
  @mcp.tool()
426
541
  async def hypothesis_testing(
427
542
  ctx: Context[ServerSession, AppContext],
@@ -0,0 +1,283 @@
1
+ """
2
+ 超时控制模块
3
+ 为复杂计算任务提供超时控制和资源管理
4
+ """
5
+
6
+ import asyncio
7
+ import signal
8
+ import threading
9
+ import time
10
+ from typing import Any, Callable, Optional, TypeVar, Union
11
+ from functools import wraps
12
+ from contextlib import contextmanager
13
+ import warnings
14
+
15
+ T = TypeVar('T')
16
+
17
+
18
+ class TimeoutError(Exception):
19
+ """超时错误"""
20
+ pass
21
+
22
+
23
+ class TimeoutManager:
24
+ """
25
+ 超时管理器
26
+ 提供同步和异步的超时控制
27
+ """
28
+
29
+ def __init__(self):
30
+ self._timeout_config = {}
31
+
32
+ def set_timeout_config(self, config: dict) -> None:
33
+ """设置超时配置"""
34
+ self._timeout_config = config
35
+
36
+ def get_timeout_for_function(self, function_name: str, default: int = 60) -> int:
37
+ """获取函数的超时时间"""
38
+ # 从配置中获取超时时间
39
+ if function_name in self._timeout_config:
40
+ return self._timeout_config[function_name]
41
+
42
+ # 根据函数类型返回默认超时
43
+ if function_name.startswith(('descriptive_', 'correlation_')):
44
+ return 30
45
+ elif function_name.startswith(('ols_', 'hypothesis_')):
46
+ return 60
47
+ elif function_name.startswith(('time_series_', 'panel_')):
48
+ return 120
49
+ elif function_name.startswith(('var_', 'vecm_', 'garch_')):
50
+ return 180
51
+ elif function_name.startswith(('random_forest_', 'gradient_boosting_')):
52
+ return 300
53
+ else:
54
+ return default
55
+
56
+ async def execute_with_timeout(self, model_name: str, timeout_seconds: int, func: callable, *args, **kwargs):
57
+ """使用超时执行函数"""
58
+ try:
59
+ if asyncio.iscoroutinefunction(func):
60
+ # 异步函数
61
+ return await asyncio.wait_for(
62
+ func(*args, **kwargs),
63
+ timeout=timeout_seconds
64
+ )
65
+ else:
66
+ # 同步函数 - 在线程池中执行
67
+ loop = asyncio.get_event_loop()
68
+ return await loop.run_in_executor(
69
+ None,
70
+ lambda: self._execute_sync_with_timeout(func, timeout_seconds, *args, **kwargs)
71
+ )
72
+ except asyncio.TimeoutError:
73
+ raise TimeoutError(f"模型 '{model_name}' 执行超时 ({timeout_seconds}秒)")
74
+
75
+ def _execute_sync_with_timeout(self, func: callable, timeout_seconds: int, *args, **kwargs):
76
+ """同步函数超时执行"""
77
+ import threading
78
+ import queue
79
+
80
+ result_queue = queue.Queue()
81
+ exception_queue = queue.Queue()
82
+
83
+ def worker():
84
+ try:
85
+ result = func(*args, **kwargs)
86
+ result_queue.put(result)
87
+ except Exception as e:
88
+ exception_queue.put(e)
89
+
90
+ thread = threading.Thread(target=worker)
91
+ thread.daemon = True
92
+ thread.start()
93
+ thread.join(timeout_seconds)
94
+
95
+ if thread.is_alive():
96
+ raise TimeoutError(f"同步函数执行超时 ({timeout_seconds}秒)")
97
+
98
+ if not exception_queue.empty():
99
+ raise exception_queue.get()
100
+
101
+ return result_queue.get()
102
+
103
+ @contextmanager
104
+ def timeout_context(self, seconds: int):
105
+ """
106
+ 同步超时上下文管理器
107
+
108
+ Args:
109
+ seconds: 超时时间(秒)
110
+ """
111
+ def timeout_handler(signum, frame):
112
+ raise TimeoutError(f"操作超时 ({seconds}秒)")
113
+
114
+ # 设置信号处理(仅适用于Unix系统)
115
+ original_handler = signal.signal(signal.SIGALRM, timeout_handler)
116
+ signal.alarm(seconds)
117
+
118
+ try:
119
+ yield
120
+ finally:
121
+ # 取消警报
122
+ signal.alarm(0)
123
+ signal.signal(signal.SIGALRM, original_handler)
124
+
125
+ async def async_timeout_context(self, seconds: int):
126
+ """
127
+ 异步超时上下文管理器
128
+
129
+ Args:
130
+ seconds: 超时时间(秒)
131
+ """
132
+ try:
133
+ await asyncio.wait_for(asyncio.sleep(0), timeout=seconds)
134
+ except asyncio.TimeoutError:
135
+ raise TimeoutError(f"异步操作超时 ({seconds}秒)")
136
+
137
+
138
+ def timeout(seconds: int = 60):
139
+ """
140
+ 同步函数超时装饰器
141
+
142
+ Args:
143
+ seconds: 超时时间(秒)
144
+ """
145
+ def decorator(func):
146
+ @wraps(func)
147
+ def wrapper(*args, **kwargs):
148
+ manager = TimeoutManager()
149
+
150
+ # 在Windows上使用线程实现超时
151
+ if hasattr(signal, 'SIGALRM'):
152
+ # Unix系统使用信号
153
+ with manager.timeout_context(seconds):
154
+ return func(*args, **kwargs)
155
+ else:
156
+ # Windows系统使用线程
157
+ result = [None]
158
+ exception = [None]
159
+
160
+ def target():
161
+ try:
162
+ result[0] = func(*args, **kwargs)
163
+ except Exception as e:
164
+ exception[0] = e
165
+
166
+ thread = threading.Thread(target=target)
167
+ thread.daemon = True
168
+ thread.start()
169
+ thread.join(seconds)
170
+
171
+ if thread.is_alive():
172
+ raise TimeoutError(f"函数 {func.__name__} 执行超时 ({seconds}秒)")
173
+
174
+ if exception[0]:
175
+ raise exception[0]
176
+
177
+ return result[0]
178
+
179
+ return wrapper
180
+ return decorator
181
+
182
+
183
+ def async_timeout(seconds: int = 60):
184
+ """
185
+ 异步函数超时装饰器
186
+
187
+ Args:
188
+ seconds: 超时时间(秒)
189
+ """
190
+ def decorator(func):
191
+ @wraps(func)
192
+ async def wrapper(*args, **kwargs):
193
+ try:
194
+ return await asyncio.wait_for(
195
+ func(*args, **kwargs),
196
+ timeout=seconds
197
+ )
198
+ except asyncio.TimeoutError:
199
+ raise TimeoutError(f"异步函数 {func.__name__} 执行超时 ({seconds}秒)")
200
+
201
+ return wrapper
202
+ return decorator
203
+
204
+
205
+ class ResourceMonitor:
206
+ """
207
+ 资源监控器
208
+ 监控内存和CPU使用情况
209
+ """
210
+
211
+ def __init__(self):
212
+ self._start_time = None
213
+ self._peak_memory = 0
214
+
215
+ @contextmanager
216
+ def monitor_resources(self):
217
+ """监控资源使用的上下文管理器"""
218
+ import psutil
219
+ import os
220
+
221
+ process = psutil.Process(os.getpid())
222
+ self._start_time = time.time()
223
+ initial_memory = process.memory_info().rss
224
+
225
+ try:
226
+ yield
227
+ finally:
228
+ current_memory = process.memory_info().rss
229
+ memory_increase = (current_memory - initial_memory) / 1024 / 1024 # MB
230
+
231
+ execution_time = time.time() - self._start_time
232
+
233
+ # 记录资源使用情况
234
+ if memory_increase > 100: # 超过100MB
235
+ warnings.warn(
236
+ f"高内存使用警告: 内存增加 {memory_increase:.2f}MB, "
237
+ f"执行时间: {execution_time:.2f}秒"
238
+ )
239
+
240
+
241
+ # 全局超时管理器实例
242
+ global_timeout_manager = TimeoutManager()
243
+
244
+
245
+ # 便捷装饰器
246
+ def with_timeout(seconds: int = 60):
247
+ """便捷超时装饰器,自动选择同步或异步"""
248
+ def decorator(func):
249
+ if asyncio.iscoroutinefunction(func):
250
+ return async_timeout(seconds)(func)
251
+ else:
252
+ return timeout(seconds)(func)
253
+ return decorator
254
+
255
+
256
+ def econometric_timeout(function_name: str = None):
257
+ """
258
+ 计量经济学专用超时装饰器
259
+ 根据函数类型自动设置合适的超时时间
260
+ """
261
+ def decorator(func):
262
+ name = function_name or func.__name__
263
+ timeout_seconds = global_timeout_manager.get_timeout_for_function(name)
264
+
265
+ if asyncio.iscoroutinefunction(func):
266
+ return async_timeout(timeout_seconds)(func)
267
+ else:
268
+ return timeout(timeout_seconds)(func)
269
+
270
+ return decorator
271
+
272
+
273
+ # 导出主要类和函数
274
+ __all__ = [
275
+ "TimeoutError",
276
+ "TimeoutManager",
277
+ "timeout",
278
+ "async_timeout",
279
+ "with_timeout",
280
+ "econometric_timeout",
281
+ "ResourceMonitor",
282
+ "global_timeout_manager"
283
+ ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aigroup-econ-mcp
3
- Version: 0.3.5
3
+ Version: 0.3.7
4
4
  Summary: 专业计量经济学MCP工具 - 让大模型直接进行数据分析
5
5
  Project-URL: Homepage, https://github.com/aigroup/aigroup-econ-mcp
6
6
  Project-URL: Repository, https://github.com/aigroup/aigroup-econ-mcp.git
@@ -1,7 +1,7 @@
1
- aigroup_econ_mcp/__init__.py,sha256=1JAE9wCfHjjYgRBmpjztBnEv_PRYxy_ikQDOjOpjXg4,490
1
+ aigroup_econ_mcp/__init__.py,sha256=gCoXRyTQdk2s8ZMgB8FKk5xXWKispDf4_NYYnQz6ZJU,490
2
2
  aigroup_econ_mcp/cli.py,sha256=oAYGd-BqTzvwx-sqcJsLiK2V8GieE90c68mGMtEoYjI,3378
3
3
  aigroup_econ_mcp/config.py,sha256=ab5X4-H8isIe2nma0c0AOqlyYgwhf5kfe9Zx5XRrzIo,18876
4
- aigroup_econ_mcp/server.py,sha256=Oipgc7SCxz_FwB3a6EX_gR1HxR7E1jw8r1uUoAb4Yj8,106706
4
+ aigroup_econ_mcp/server.py,sha256=inS_DGmFwlF_2FSMzW7JgRR9XAXL3PGM4dfa79B7w20,110588
5
5
  aigroup_econ_mcp/tools/__init__.py,sha256=gJCT-Tzx5cPnVhV68GRffModLCY5DdyETvK_UBZg7J0,325
6
6
  aigroup_econ_mcp/tools/base.py,sha256=CwZFtvagcv732OAyCecEfwj8vekrOHSKjPXwrWamW2g,8163
7
7
  aigroup_econ_mcp/tools/cache.py,sha256=Urv2zuycp5dS7Qh-XQWEMrwszq9RZ-il8cz_-WniGgc,15311
@@ -12,9 +12,10 @@ aigroup_econ_mcp/tools/panel_data.py,sha256=288CZoUN40lEApoYbTXkyoenV-eWFDbyPeV4
12
12
  aigroup_econ_mcp/tools/regression.py,sha256=uMGRGUQo4mU1sb8fwpP2FpkCqt_e9AtqEtUpInACtJo,6443
13
13
  aigroup_econ_mcp/tools/statistics.py,sha256=2cHgNSUXwPYPLxntVOEOL8yF-x92mrgjK-R8kkxDihg,4239
14
14
  aigroup_econ_mcp/tools/time_series.py,sha256=5Zy-CPH9HD5u268U3BkFK-DWq7akiEbMZBnrmNfjSK8,25187
15
+ aigroup_econ_mcp/tools/timeout.py,sha256=vNnGsR0sXW1xvIbKCF-qPUU3QNDAn_MaQgSxbGxkfW4,8404
15
16
  aigroup_econ_mcp/tools/validation.py,sha256=F7LHwog5xtFIMjD9D48kd8jAF5MsZb7wjdrgaOg8EKo,16657
16
- aigroup_econ_mcp-0.3.5.dist-info/METADATA,sha256=XJVRad8a6_UerZllbZLhRQfQ0fBmnjQzpL-4Idrmn_Q,11159
17
- aigroup_econ_mcp-0.3.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
18
- aigroup_econ_mcp-0.3.5.dist-info/entry_points.txt,sha256=j5ZJYOc4lAZV-X3XkAuGhzHtIRcJtZ6Gz8ZKPY_QTrM,62
19
- aigroup_econ_mcp-0.3.5.dist-info/licenses/LICENSE,sha256=DoyCJUWlDzKbqc5KRbFpsGYLwLh-XJRHKQDoITjb1yc,1083
20
- aigroup_econ_mcp-0.3.5.dist-info/RECORD,,
17
+ aigroup_econ_mcp-0.3.7.dist-info/METADATA,sha256=07BllMUNdovCygHp833oL6Wds94TdcNZKJ_PvFWGI-s,11159
18
+ aigroup_econ_mcp-0.3.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
19
+ aigroup_econ_mcp-0.3.7.dist-info/entry_points.txt,sha256=j5ZJYOc4lAZV-X3XkAuGhzHtIRcJtZ6Gz8ZKPY_QTrM,62
20
+ aigroup_econ_mcp-0.3.7.dist-info/licenses/LICENSE,sha256=DoyCJUWlDzKbqc5KRbFpsGYLwLh-XJRHKQDoITjb1yc,1083
21
+ aigroup_econ_mcp-0.3.7.dist-info/RECORD,,