aigroup-econ-mcp 0.4.0__py3-none-any.whl → 1.3.3__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.
- aigroup_econ_mcp/__init__.py +1 -1
- aigroup_econ_mcp/cli.py +81 -86
- aigroup_econ_mcp/server.py +451 -451
- aigroup_econ_mcp/tools/__init__.py +8 -7
- aigroup_econ_mcp/tools/base.py +204 -5
- aigroup_econ_mcp/tools/data_loader.py +51 -27
- aigroup_econ_mcp/tools/file_parser.py +1027 -560
- aigroup_econ_mcp/tools/machine_learning.py +56 -669
- aigroup_econ_mcp/tools/ml_ensemble.py +210 -0
- aigroup_econ_mcp/tools/ml_evaluation.py +272 -0
- aigroup_econ_mcp/tools/ml_models.py +54 -0
- aigroup_econ_mcp/tools/ml_regularization.py +186 -0
- aigroup_econ_mcp/tools/panel_data.py +70 -4
- aigroup_econ_mcp/tools/time_series.py +53 -22
- aigroup_econ_mcp/tools/tool_descriptions.py +410 -0
- aigroup_econ_mcp/tools/tool_handlers.py +681 -43
- aigroup_econ_mcp/tools/tool_registry.py +329 -21
- aigroup_econ_mcp-1.3.3.dist-info/METADATA +525 -0
- aigroup_econ_mcp-1.3.3.dist-info/RECORD +30 -0
- aigroup_econ_mcp/server_v1_backup.py +0 -1250
- aigroup_econ_mcp/server_v1_old.py +0 -1250
- aigroup_econ_mcp/server_with_file_support.py +0 -259
- aigroup_econ_mcp/tools/decorators.py +0 -178
- aigroup_econ_mcp/tools/file_input_handler.py +0 -268
- aigroup_econ_mcp-0.4.0.dist-info/METADATA +0 -718
- aigroup_econ_mcp-0.4.0.dist-info/RECORD +0 -30
- {aigroup_econ_mcp-0.4.0.dist-info → aigroup_econ_mcp-1.3.3.dist-info}/WHEEL +0 -0
- {aigroup_econ_mcp-0.4.0.dist-info → aigroup_econ_mcp-1.3.3.dist-info}/entry_points.txt +0 -0
- {aigroup_econ_mcp-0.4.0.dist-info → aigroup_econ_mcp-1.3.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -3,16 +3,17 @@
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
from . import regression, statistics, time_series, machine_learning, panel_data
|
|
6
|
-
from . import validation, cache, monitoring, file_parser
|
|
6
|
+
from . import validation, cache, monitoring, file_parser, tool_descriptions
|
|
7
7
|
|
|
8
8
|
__all__ = [
|
|
9
|
-
"regression",
|
|
10
|
-
"statistics",
|
|
11
|
-
"time_series",
|
|
12
|
-
"machine_learning",
|
|
9
|
+
"regression",
|
|
10
|
+
"statistics",
|
|
11
|
+
"time_series",
|
|
12
|
+
"machine_learning",
|
|
13
13
|
"panel_data",
|
|
14
14
|
"validation",
|
|
15
|
-
"cache",
|
|
15
|
+
"cache",
|
|
16
16
|
"monitoring",
|
|
17
|
-
"file_parser"
|
|
17
|
+
"file_parser",
|
|
18
|
+
"tool_descriptions"
|
|
18
19
|
]
|
aigroup_econ_mcp/tools/base.py
CHANGED
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
工具模块基类和装饰器
|
|
3
|
+
整合了工具基类、装饰器、错误处理和优化组件
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
import functools
|
|
7
7
|
from typing import Any, Dict, List, Optional, Callable, Type
|
|
8
|
+
from functools import wraps
|
|
9
|
+
from mcp.server.session import ServerSession
|
|
10
|
+
from mcp.server.fastmcp import Context
|
|
11
|
+
from mcp.types import CallToolResult, TextContent
|
|
12
|
+
|
|
8
13
|
from .validation import ValidationError, validate_econometric_data, validate_model_parameters
|
|
9
14
|
from .cache import cache_result, cache_model, global_econometric_cache
|
|
10
15
|
from .monitoring import monitor_performance, track_progress, global_performance_monitor
|
|
16
|
+
from .file_parser import FileParser
|
|
11
17
|
from ..config import get_config, econometric_config
|
|
12
18
|
|
|
13
19
|
|
|
20
|
+
# ============================================================================
|
|
21
|
+
# 错误类定义
|
|
22
|
+
# ============================================================================
|
|
23
|
+
|
|
14
24
|
class EconometricToolError(Exception):
|
|
15
25
|
"""计量经济学工具错误基类"""
|
|
16
26
|
|
|
@@ -45,6 +55,177 @@ class ConfigurationError(EconometricToolError):
|
|
|
45
55
|
pass
|
|
46
56
|
|
|
47
57
|
|
|
58
|
+
# ============================================================================
|
|
59
|
+
# 装饰器函数
|
|
60
|
+
# ============================================================================
|
|
61
|
+
|
|
62
|
+
def with_file_input(tool_type: str):
|
|
63
|
+
"""
|
|
64
|
+
为工具函数添加文件输入支持的装饰器
|
|
65
|
+
|
|
66
|
+
支持两种输入方式:
|
|
67
|
+
1. file_path: CSV/JSON文件路径
|
|
68
|
+
2. file_content: 文件内容字符串
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
tool_type: 工具类型 ('single_var', 'multi_var_dict', 'regression', 'panel', 'time_series')
|
|
72
|
+
|
|
73
|
+
使用示例:
|
|
74
|
+
@with_file_input('regression')
|
|
75
|
+
async def my_tool(ctx, y_data=None, x_data=None, file_path=None, file_content=None, file_format='auto', **kwargs):
|
|
76
|
+
# 如果提供了file_path或file_content,数据会被自动填充
|
|
77
|
+
pass
|
|
78
|
+
"""
|
|
79
|
+
def decorator(func: Callable) -> Callable:
|
|
80
|
+
@wraps(func)
|
|
81
|
+
async def wrapper(*args, **kwargs):
|
|
82
|
+
# 提取上下文和文件参数
|
|
83
|
+
ctx = args[0] if args else kwargs.get('ctx')
|
|
84
|
+
file_path = kwargs.get('file_path')
|
|
85
|
+
file_content = kwargs.get('file_content')
|
|
86
|
+
file_format = kwargs.get('file_format', 'auto')
|
|
87
|
+
|
|
88
|
+
# 优先处理file_path
|
|
89
|
+
if file_path:
|
|
90
|
+
try:
|
|
91
|
+
await ctx.info(f"检测到文件路径输入: {file_path}")
|
|
92
|
+
|
|
93
|
+
# 从文件路径解析
|
|
94
|
+
parsed = FileParser.parse_file_path(file_path, file_format)
|
|
95
|
+
|
|
96
|
+
await ctx.info(
|
|
97
|
+
f"文件解析成功:{parsed['n_variables']}个变量,"
|
|
98
|
+
f"{parsed['n_observations']}个观测"
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
# 转换为工具格式
|
|
102
|
+
converted = FileParser.convert_to_tool_format(parsed, tool_type)
|
|
103
|
+
|
|
104
|
+
# 更新kwargs
|
|
105
|
+
kwargs.update(converted)
|
|
106
|
+
|
|
107
|
+
await ctx.info(f"数据已转换为{tool_type}格式")
|
|
108
|
+
|
|
109
|
+
except Exception as e:
|
|
110
|
+
await ctx.error(f"文件解析失败: {str(e)}")
|
|
111
|
+
return CallToolResult(
|
|
112
|
+
content=[TextContent(type="text", text=f"文件解析错误: {str(e)}")],
|
|
113
|
+
isError=True
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# 如果没有file_path但有file_content,处理文件内容
|
|
117
|
+
elif file_content:
|
|
118
|
+
try:
|
|
119
|
+
await ctx.info("检测到文件内容输入,开始解析...")
|
|
120
|
+
|
|
121
|
+
# 解析文件内容
|
|
122
|
+
parsed = FileParser.parse_file_content(file_content, file_format)
|
|
123
|
+
|
|
124
|
+
await ctx.info(
|
|
125
|
+
f"文件解析成功:{parsed['n_variables']}个变量,"
|
|
126
|
+
f"{parsed['n_observations']}个观测"
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
# 转换为工具格式
|
|
130
|
+
converted = FileParser.convert_to_tool_format(parsed, tool_type)
|
|
131
|
+
|
|
132
|
+
# 更新kwargs
|
|
133
|
+
kwargs.update(converted)
|
|
134
|
+
|
|
135
|
+
await ctx.info(f"数据已转换为{tool_type}格式")
|
|
136
|
+
|
|
137
|
+
except Exception as e:
|
|
138
|
+
await ctx.error(f"文件解析失败: {str(e)}")
|
|
139
|
+
return CallToolResult(
|
|
140
|
+
content=[TextContent(type="text", text=f"文件解析错误: {str(e)}")],
|
|
141
|
+
isError=True
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# 调用原函数
|
|
145
|
+
return await func(*args, **kwargs)
|
|
146
|
+
|
|
147
|
+
return wrapper
|
|
148
|
+
return decorator
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def with_error_handling(func: Callable) -> Callable:
|
|
152
|
+
"""
|
|
153
|
+
为工具函数添加统一错误处理的装饰器
|
|
154
|
+
"""
|
|
155
|
+
@wraps(func)
|
|
156
|
+
async def wrapper(*args, **kwargs):
|
|
157
|
+
ctx = args[0] if args else kwargs.get('ctx')
|
|
158
|
+
tool_name = func.__name__
|
|
159
|
+
|
|
160
|
+
try:
|
|
161
|
+
return await func(*args, **kwargs)
|
|
162
|
+
except Exception as e:
|
|
163
|
+
await ctx.error(f"{tool_name}执行出错: {str(e)}")
|
|
164
|
+
return CallToolResult(
|
|
165
|
+
content=[TextContent(type="text", text=f"错误: {str(e)}")],
|
|
166
|
+
isError=True
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
return wrapper
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def with_logging(func: Callable) -> Callable:
|
|
173
|
+
"""
|
|
174
|
+
为工具函数添加日志记录的装饰器
|
|
175
|
+
"""
|
|
176
|
+
@wraps(func)
|
|
177
|
+
async def wrapper(*args, **kwargs):
|
|
178
|
+
ctx = args[0] if args else kwargs.get('ctx')
|
|
179
|
+
tool_name = func.__name__
|
|
180
|
+
|
|
181
|
+
await ctx.info(f"开始执行 {tool_name}")
|
|
182
|
+
result = await func(*args, **kwargs)
|
|
183
|
+
await ctx.info(f"{tool_name} 执行完成")
|
|
184
|
+
|
|
185
|
+
return result
|
|
186
|
+
|
|
187
|
+
return wrapper
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def with_file_support_decorator(
|
|
191
|
+
tool_type: str,
|
|
192
|
+
enable_error_handling: bool = True,
|
|
193
|
+
enable_logging: bool = True
|
|
194
|
+
):
|
|
195
|
+
"""
|
|
196
|
+
组合装饰器:为计量经济学工具添加文件支持和其他标准功能
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
tool_type: 工具类型
|
|
200
|
+
enable_error_handling: 是否启用错误处理
|
|
201
|
+
enable_logging: 是否启用日志记录
|
|
202
|
+
|
|
203
|
+
使用示例:
|
|
204
|
+
@with_file_support_decorator('regression')
|
|
205
|
+
async def ols_regression(ctx, y_data=None, x_data=None, **kwargs):
|
|
206
|
+
# 只需要编写核心业务逻辑
|
|
207
|
+
pass
|
|
208
|
+
"""
|
|
209
|
+
def decorator(func: Callable) -> Callable:
|
|
210
|
+
wrapped = func
|
|
211
|
+
|
|
212
|
+
if enable_error_handling:
|
|
213
|
+
wrapped = with_error_handling(wrapped)
|
|
214
|
+
|
|
215
|
+
wrapped = with_file_input(tool_type)(wrapped)
|
|
216
|
+
|
|
217
|
+
if enable_logging:
|
|
218
|
+
wrapped = with_logging(wrapped)
|
|
219
|
+
|
|
220
|
+
return wrapped
|
|
221
|
+
|
|
222
|
+
return decorator
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
# ============================================================================
|
|
226
|
+
# 工具基类
|
|
227
|
+
# ============================================================================
|
|
228
|
+
|
|
48
229
|
class EconometricTool:
|
|
49
230
|
"""
|
|
50
231
|
计量经济学工具基类
|
|
@@ -206,16 +387,27 @@ class EconometricTool:
|
|
|
206
387
|
return global_econometric_cache.result_cache.get_function_cache_stats(self.tool_name) or {}
|
|
207
388
|
|
|
208
389
|
|
|
390
|
+
# ============================================================================
|
|
209
391
|
# 便捷装饰器函数
|
|
210
|
-
|
|
392
|
+
# ============================================================================
|
|
393
|
+
|
|
394
|
+
def econometric_tool_with_optimization(tool_name: str = None):
|
|
211
395
|
"""
|
|
212
|
-
|
|
396
|
+
计量经济学工具装饰器(带优化)
|
|
397
|
+
|
|
398
|
+
应用缓存、性能监控和错误处理
|
|
213
399
|
|
|
214
400
|
Args:
|
|
215
401
|
tool_name: 工具名称
|
|
216
402
|
|
|
217
403
|
Returns:
|
|
218
404
|
Callable: 装饰器函数
|
|
405
|
+
|
|
406
|
+
使用示例:
|
|
407
|
+
@econometric_tool_with_optimization('my_analysis')
|
|
408
|
+
def my_analysis(data):
|
|
409
|
+
# 自动获得缓存、监控和错误处理
|
|
410
|
+
pass
|
|
219
411
|
"""
|
|
220
412
|
def decorator(func):
|
|
221
413
|
name = tool_name or func.__name__
|
|
@@ -261,11 +453,18 @@ def validate_input(data_type: str = "generic"):
|
|
|
261
453
|
|
|
262
454
|
# 导出主要类和函数
|
|
263
455
|
__all__ = [
|
|
456
|
+
# 错误类
|
|
264
457
|
"EconometricToolError",
|
|
265
458
|
"DataValidationError",
|
|
266
459
|
"ModelFittingError",
|
|
267
460
|
"ConfigurationError",
|
|
461
|
+
# 基类
|
|
268
462
|
"EconometricTool",
|
|
269
|
-
|
|
463
|
+
# 装饰器
|
|
464
|
+
"with_file_input",
|
|
465
|
+
"with_error_handling",
|
|
466
|
+
"with_logging",
|
|
467
|
+
"with_file_support_decorator",
|
|
468
|
+
"econometric_tool_with_optimization",
|
|
270
469
|
"validate_input"
|
|
271
470
|
]
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"""
|
|
2
2
|
数据加载辅助模块
|
|
3
|
-
|
|
3
|
+
提供通用的文件加载功能,支持CSV、JSON和TXT格式
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
from typing import Dict, List, Union
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
import pandas as pd
|
|
9
|
+
from .file_parser import FileParser
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
async def load_data_if_path(
|
|
@@ -16,7 +17,7 @@ async def load_data_if_path(
|
|
|
16
17
|
智能加载数据:如果是字符串则作为文件路径加载,否则直接返回
|
|
17
18
|
|
|
18
19
|
Args:
|
|
19
|
-
data:
|
|
20
|
+
data: 数据字典或文件路径(支持CSV/JSON/TXT)
|
|
20
21
|
ctx: MCP上下文对象(可选,用于日志)
|
|
21
22
|
|
|
22
23
|
Returns:
|
|
@@ -40,23 +41,28 @@ async def load_data_if_path(
|
|
|
40
41
|
if not path.exists():
|
|
41
42
|
raise ValueError(f"文件不存在: {data}")
|
|
42
43
|
|
|
43
|
-
#
|
|
44
|
-
|
|
44
|
+
# 使用FileParser解析文件(支持CSV/JSON/TXT自动检测)
|
|
45
|
+
parsed = FileParser.parse_file_path(str(path), "auto")
|
|
45
46
|
|
|
46
|
-
#
|
|
47
|
-
result =
|
|
47
|
+
# 返回数据字典
|
|
48
|
+
result = parsed["data"]
|
|
48
49
|
|
|
49
50
|
if ctx:
|
|
50
|
-
await ctx.info(
|
|
51
|
+
await ctx.info(
|
|
52
|
+
f"✅ {parsed['format'].upper()}文件加载成功:"
|
|
53
|
+
f"{parsed['n_variables']}个变量,{parsed['n_observations']}个观测"
|
|
54
|
+
)
|
|
51
55
|
|
|
52
56
|
return result
|
|
53
57
|
|
|
54
58
|
except FileNotFoundError:
|
|
55
59
|
raise ValueError(f"文件不存在: {data}")
|
|
56
60
|
except Exception as e:
|
|
57
|
-
raise ValueError(f"
|
|
61
|
+
raise ValueError(f"文件读取失败: {str(e)}")
|
|
58
62
|
|
|
59
63
|
# 其他类型报错
|
|
64
|
+
raise TypeError(f"不支持的数据类型: {type(data)},期望Dict或str")
|
|
65
|
+
|
|
60
66
|
|
|
61
67
|
async def load_single_var_if_path(
|
|
62
68
|
data: Union[List[float], str],
|
|
@@ -67,9 +73,9 @@ async def load_single_var_if_path(
|
|
|
67
73
|
智能加载单变量数据:如果是字符串则作为文件路径加载,否则直接返回
|
|
68
74
|
|
|
69
75
|
Args:
|
|
70
|
-
data:
|
|
76
|
+
data: 数据列表或文件路径(支持CSV/JSON/TXT)
|
|
71
77
|
ctx: MCP上下文对象(可选,用于日志)
|
|
72
|
-
column_name:
|
|
78
|
+
column_name: 文件中要读取的列名(可选,默认读取第一列)
|
|
73
79
|
|
|
74
80
|
Returns:
|
|
75
81
|
数据列表
|
|
@@ -92,32 +98,41 @@ async def load_single_var_if_path(
|
|
|
92
98
|
if not path.exists():
|
|
93
99
|
raise ValueError(f"文件不存在: {data}")
|
|
94
100
|
|
|
95
|
-
#
|
|
96
|
-
|
|
101
|
+
# 使用FileParser解析文件
|
|
102
|
+
parsed = FileParser.parse_file_path(str(path), "auto")
|
|
103
|
+
data_dict = parsed["data"]
|
|
97
104
|
|
|
98
105
|
# 确定要读取的列
|
|
99
106
|
if column_name:
|
|
100
|
-
if column_name not in
|
|
101
|
-
raise ValueError(
|
|
102
|
-
|
|
107
|
+
if column_name not in data_dict:
|
|
108
|
+
raise ValueError(
|
|
109
|
+
f"列'{column_name}'不存在于文件中。"
|
|
110
|
+
f"可用列: {list(data_dict.keys())}"
|
|
111
|
+
)
|
|
112
|
+
result = data_dict[column_name]
|
|
103
113
|
else:
|
|
104
114
|
# 默认读取第一列
|
|
105
|
-
|
|
115
|
+
first_col = parsed["variables"][0]
|
|
116
|
+
result = data_dict[first_col]
|
|
106
117
|
if ctx:
|
|
107
|
-
await ctx.info(f"未指定列名,使用第一列: {
|
|
118
|
+
await ctx.info(f"未指定列名,使用第一列: {first_col}")
|
|
108
119
|
|
|
109
120
|
if ctx:
|
|
110
|
-
await ctx.info(
|
|
121
|
+
await ctx.info(
|
|
122
|
+
f"✅ {parsed['format'].upper()}文件加载成功:{len(result)}个观测"
|
|
123
|
+
)
|
|
111
124
|
|
|
112
125
|
return result
|
|
113
126
|
|
|
114
127
|
except FileNotFoundError:
|
|
115
128
|
raise ValueError(f"文件不存在: {data}")
|
|
116
129
|
except Exception as e:
|
|
117
|
-
raise ValueError(f"
|
|
130
|
+
raise ValueError(f"文件读取失败: {str(e)}")
|
|
118
131
|
|
|
119
132
|
# 其他类型报错
|
|
120
133
|
raise TypeError(f"不支持的数据类型: {type(data)},期望List或str")
|
|
134
|
+
|
|
135
|
+
|
|
121
136
|
async def load_x_data_if_path(
|
|
122
137
|
data: Union[List[List[float]], str],
|
|
123
138
|
ctx = None
|
|
@@ -126,7 +141,7 @@ async def load_x_data_if_path(
|
|
|
126
141
|
智能加载自变量数据:如果是字符串则作为文件路径加载,否则直接返回
|
|
127
142
|
|
|
128
143
|
Args:
|
|
129
|
-
data:
|
|
144
|
+
data: 自变量数据(二维列表)或文件路径(支持CSV/JSON/TXT)
|
|
130
145
|
ctx: MCP上下文对象(可选,用于日志)
|
|
131
146
|
|
|
132
147
|
Returns:
|
|
@@ -150,22 +165,31 @@ async def load_x_data_if_path(
|
|
|
150
165
|
if not path.exists():
|
|
151
166
|
raise ValueError(f"文件不存在: {data}")
|
|
152
167
|
|
|
153
|
-
#
|
|
154
|
-
|
|
168
|
+
# 使用FileParser解析文件
|
|
169
|
+
parsed = FileParser.parse_file_path(str(path), "auto")
|
|
170
|
+
data_dict = parsed["data"]
|
|
155
171
|
|
|
156
172
|
# 转换为二维列表格式
|
|
157
|
-
|
|
173
|
+
variables = parsed["variables"]
|
|
174
|
+
n_obs = parsed["n_observations"]
|
|
175
|
+
|
|
176
|
+
result = []
|
|
177
|
+
for i in range(n_obs):
|
|
178
|
+
row = [data_dict[var][i] for var in variables]
|
|
179
|
+
result.append(row)
|
|
158
180
|
|
|
159
181
|
if ctx:
|
|
160
|
-
await ctx.info(
|
|
182
|
+
await ctx.info(
|
|
183
|
+
f"✅ 自变量{parsed['format'].upper()}文件加载成功:"
|
|
184
|
+
f"{len(result)}个观测,{len(variables)}个自变量"
|
|
185
|
+
)
|
|
161
186
|
|
|
162
187
|
return result
|
|
163
188
|
|
|
164
189
|
except FileNotFoundError:
|
|
165
190
|
raise ValueError(f"文件不存在: {data}")
|
|
166
191
|
except Exception as e:
|
|
167
|
-
raise ValueError(f"
|
|
192
|
+
raise ValueError(f"自变量文件读取失败: {str(e)}")
|
|
168
193
|
|
|
169
194
|
# 其他类型报错
|
|
170
|
-
raise TypeError(f"不支持的数据类型: {type(data)},期望List[List[float]]或str")
|
|
171
|
-
raise TypeError(f"不支持的数据类型: {type(data)},期望Dict或str")
|
|
195
|
+
raise TypeError(f"不支持的数据类型: {type(data)},期望List[List[float]]或str")
|