mm-qa-mcp 0.2.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.
File without changes
@@ -0,0 +1,38 @@
1
+ import logging
2
+ import os
3
+ from logging.handlers import RotatingFileHandler
4
+
5
+ # 创建日志目录(如果不存在)
6
+ log_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'logs')
7
+ os.makedirs(log_dir, exist_ok=True)
8
+ log_file = os.path.join(log_dir, 'agent_service.log')
9
+
10
+ # 配置日志格式
11
+ log_format = '[%(asctime)s]-[%(process)d]-[%(levelname)s]- %(message)s'
12
+ formatter = logging.Formatter(log_format)
13
+
14
+ # 创建logger
15
+ logger = logging.getLogger("agent_service")
16
+ logger.setLevel(logging.INFO)
17
+
18
+ # 清除已有的处理器,避免重复
19
+ if logger.handlers:
20
+ logger.handlers.clear()
21
+
22
+ # 添加控制台处理器
23
+ console_handler = logging.StreamHandler()
24
+ console_handler.setFormatter(formatter)
25
+ logger.addHandler(console_handler)
26
+
27
+ # 添加文件处理器(使用RotatingFileHandler支持日志轮转)
28
+ file_handler = RotatingFileHandler(
29
+ log_file,
30
+ maxBytes=10*1024*1024, # 10MB
31
+ backupCount=5,
32
+ encoding='utf-8'
33
+ )
34
+ file_handler.setFormatter(formatter)
35
+ logger.addHandler(file_handler)
36
+
37
+ # 避免日志传播到根logger
38
+ logger.propagate = False
@@ -0,0 +1,246 @@
1
+ """
2
+ coding:utf-8
3
+ @Software: PyCharm
4
+ @Time: 2024/8/21 上午11:25
5
+ @Author: xingyun
6
+ """
7
+ import configparser
8
+ import json
9
+ import os
10
+ import subprocess
11
+ import sys
12
+ import pytz
13
+ import importlib.resources
14
+ import pkgutil
15
+
16
+ from datetime import datetime
17
+
18
+
19
+ class Utils:
20
+ @staticmethod
21
+ def get_env_info():
22
+ if 'bedrock' in os.environ:
23
+ print(os.environ['bedrock'])
24
+ if 'env' in json.loads(os.environ['bedrock']):
25
+ return json.loads(os.environ['bedrock'])['env'], json.loads(os.environ['bedrock'])['business']
26
+ return Utils.get_conf("common", "env"), Utils.get_conf("common", "business")
27
+
28
+ @staticmethod
29
+ def get_conf(section, key):
30
+ if os.environ.get(key, None):
31
+ return os.environ[key]
32
+ else:
33
+ file = Utils.get_conf_abspath()
34
+ config = configparser.ConfigParser()
35
+ config.read(file)
36
+ try:
37
+ return config[section][key]
38
+ except KeyError:
39
+ raise KeyError(f"conf.ini, section:{section},key:{key} not fould, please check!")
40
+
41
+ @staticmethod
42
+ def set_conf(section, key, value):
43
+ """
44
+ 设置或更新配置文件中的指定项。
45
+ :param section: 配置文件中的 section 名称
46
+ :param key: 配置项名称
47
+ :param value: 配置值
48
+ """
49
+ file = Utils.get_conf_abspath()
50
+ config = configparser.ConfigParser()
51
+
52
+ # 读取现有配置文件(如果存在)
53
+ if os.path.exists(file):
54
+ config.read(file)
55
+
56
+ # 如果 section 不存在,创建新的 section
57
+ if not config.has_section(section):
58
+ config.add_section(section)
59
+
60
+ # 设置或更新 key 的值
61
+ config.set(section, key, value)
62
+ # 写回配置文件
63
+ with open(file, "w") as configfile:
64
+ config.write(configfile)
65
+
66
+ @staticmethod
67
+ def get_conf_abspath():
68
+ # 首先尝试从包资源中读取配置
69
+ try:
70
+ # 检查配置是否作为包资源存在
71
+ if pkgutil.find_loader("minimax_qa_mcp.conf"):
72
+ conf_content = pkgutil.get_data("minimax_qa_mcp.conf", "conf.ini")
73
+ if conf_content:
74
+ # 创建临时文件并写入配置内容
75
+ temp_dir = os.path.join(os.path.expanduser("~"), ".minimax_qa_mcp")
76
+ os.makedirs(temp_dir, exist_ok=True)
77
+ temp_file = os.path.join(temp_dir, "conf.ini")
78
+
79
+ # 如果文件不存在或需要更新,则写入内容
80
+ if not os.path.exists(temp_file):
81
+ with open(temp_file, "wb") as f:
82
+ f.write(conf_content)
83
+
84
+ return temp_file
85
+ except Exception as e:
86
+ print(f"从包资源读取配置失败: {e}")
87
+
88
+ # 如果从包资源读取失败,尝试通过路径查找
89
+ runner_abspath_dirname = os.path.dirname(os.path.abspath(sys.argv[-1]))
90
+ now_abspath_dirname = runner_abspath_dirname
91
+ for i in range(10):
92
+ for root, dirs, files in os.walk(now_abspath_dirname):
93
+ if "conf" in dirs:
94
+ conf_path = os.path.join(now_abspath_dirname, "conf", "conf.ini")
95
+ if os.path.exists(conf_path):
96
+ return conf_path
97
+ now_abspath_dirname = os.path.dirname(now_abspath_dirname)
98
+
99
+ # 最后,尝试从当前工作目录查找
100
+ local_conf = os.path.join(os.getcwd(), "conf", "conf.ini")
101
+ if os.path.exists(local_conf):
102
+ return local_conf
103
+
104
+ raise Exception("not found /conf/conf.ini")
105
+
106
+ @staticmethod
107
+ def get_link_conf_abspath():
108
+ # 首先尝试从包资源中读取配置
109
+ try:
110
+ # 检查配置是否作为包资源存在
111
+ if pkgutil.find_loader("minimax_qa_mcp.conf"):
112
+ conf_content = pkgutil.get_data("minimax_qa_mcp.conf", "link_api.txt")
113
+ if conf_content:
114
+ # 创建临时文件并写入配置内容
115
+ temp_dir = os.path.join(os.path.expanduser("~"), ".minimax_qa_mcp")
116
+ os.makedirs(temp_dir, exist_ok=True)
117
+ temp_file = os.path.join(temp_dir, "link_api.txt")
118
+
119
+ # 如果文件不存在或需要更新,则写入内容
120
+ if not os.path.exists(temp_file):
121
+ with open(temp_file, "wb") as f:
122
+ f.write(conf_content)
123
+
124
+ return temp_file
125
+ except Exception as e:
126
+ print(f"从包资源读取配置失败: {e}")
127
+
128
+ # 如果从包资源读取失败,尝试通过路径查找
129
+ runner_abspath_dirname = os.path.dirname(os.path.abspath(sys.argv[-1]))
130
+ now_abspath_dirname = runner_abspath_dirname
131
+ for i in range(10):
132
+ for root, dirs, files in os.walk(now_abspath_dirname):
133
+ if "conf" in dirs:
134
+ link_path = os.path.join(now_abspath_dirname, "conf", "link_api.txt")
135
+ if os.path.exists(link_path):
136
+ return link_path
137
+ now_abspath_dirname = os.path.dirname(now_abspath_dirname)
138
+
139
+ # 最后,尝试从当前工作目录查找
140
+ local_conf = os.path.join(os.getcwd(), "conf", "link_api.txt")
141
+ if os.path.exists(local_conf):
142
+ return local_conf
143
+
144
+ return None
145
+
146
+ @staticmethod
147
+ def get_json_data(path):
148
+ with open(path, 'r') as f:
149
+ load_dict = json.load(f)
150
+ return load_dict
151
+
152
+ @staticmethod
153
+ def write_json(json_data, json_path):
154
+ with open(json_path, "w") as fp:
155
+ fp.write(json.dumps(json_data, indent=4))
156
+
157
+ @staticmethod
158
+ def replace_bool_values(data):
159
+ if isinstance(data, dict):
160
+ return {k: Utils.replace_bool_values(v) for k, v in data.items()}
161
+ elif isinstance(data, list):
162
+ return [Utils.replace_bool_values(item) for item in data]
163
+ elif data is False:
164
+ return False
165
+ elif data is True:
166
+ return True
167
+ else:
168
+ return data
169
+
170
+ @staticmethod
171
+ def get_report_path() -> str:
172
+ if 'lib' in os.getcwd():
173
+ root_path = os.path.dirname(os.path.abspath(os.getcwd()))
174
+ # 命令行处理
175
+ else:
176
+ root_path = os.getcwd()
177
+ report_path = root_path + '/allure/'
178
+
179
+ return report_path
180
+
181
+ @staticmethod
182
+ def time_formatted() -> str:
183
+ # 获取当前日期和时间
184
+ current_date = datetime.now()
185
+ # 获取上海时区
186
+ shanghai_tz = pytz.timezone('Asia/Shanghai')
187
+ # 将当前日期和时间转换为上海时区
188
+ shanghai_time = current_date.astimezone(shanghai_tz)
189
+ # 格式化日期为 YYYYMMDD 格式
190
+ formatted_date = shanghai_time.strftime('%Y%m%d')
191
+ return formatted_date
192
+
193
+ @staticmethod
194
+ def time_formatted_shanghai() -> str:
195
+ # 获取当前日期和时间
196
+ current_date = datetime.now()
197
+ # 获取上海时区
198
+ shanghai_tz = pytz.timezone('Asia/Shanghai')
199
+ # 将当前日期和时间转换为上海时区
200
+ shanghai_time = current_date.astimezone(shanghai_tz)
201
+ # 格式化日期为 YYYYMMDD 格式
202
+ formatted_date = shanghai_time.strftime('%Y%m%d%H%M%S')
203
+ return formatted_date
204
+
205
+ @staticmethod
206
+ def run_cmd(cmd):
207
+ """运行cmd 命令"""
208
+
209
+ # 运行CMD命令并获取输出
210
+ result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
211
+
212
+ # 打印命令的输出
213
+ print("Output:", result.stdout)
214
+ print("Error:", result.stderr)
215
+
216
+
217
+ if __name__ == '__main__':
218
+ utils = Utils()
219
+ req = {
220
+ "cid": 180114436932074,
221
+ "user_id": 160633434722592,
222
+ "npc_id": 174392236024298,
223
+ "mid": 180115467682152,
224
+ "suggest_count": 0,
225
+ "scene": 1,
226
+ "use_safe_model": False, # 这里是 False 而不是 false
227
+ "user_msg_text": "(语气平静)多谢。",
228
+ "base_req": {
229
+ "AppID": 600,
230
+ "os": 2,
231
+ "sys_language": "zh",
232
+ "ip_region": "cn",
233
+ "channel": "xy_XIAOMI",
234
+ "bus_data": {},
235
+ "Extra": {
236
+ "UserID": "160633434722592",
237
+ "DeviceID": "160634651599227",
238
+ "VersionCode": 1350003
239
+ }
240
+ }
241
+ }
242
+
243
+ # 调用函数进行替换
244
+ req = utils.replace_bool_values(req)
245
+
246
+ print(req)
@@ -0,0 +1,167 @@
1
+ Metadata-Version: 2.4
2
+ Name: mm_qa_mcp
3
+ Version: 0.2.0
4
+ Summary: QA agent service 集合
5
+ Author-email: xingyun <xingyun@minimaxi.com>
6
+ License-Expression: MIT
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.9
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Requires-Python: >=3.10
13
+ Description-Content-Type: text/markdown
14
+ Requires-Dist: annotated-types>=0.7.0
15
+ Requires-Dist: anyio>=4.9.0
16
+ Requires-Dist: Authlib>=1.5.2
17
+ Requires-Dist: certifi>=2025.1.31
18
+ Requires-Dist: cffi>=1.17.1
19
+ Requires-Dist: charset-normalizer>=3.4.1
20
+ Requires-Dist: classes>=0.4.1
21
+ Requires-Dist: click>=8.1.8
22
+ Requires-Dist: cryptography>=44.0.2
23
+ Requires-Dist: h11>=0.14.0
24
+ Requires-Dist: httpcore>=1.0.7
25
+ Requires-Dist: httpx>=0.28.1
26
+ Requires-Dist: httpx-sse>=0.4.0
27
+ Requires-Dist: idna>=3.10
28
+ Requires-Dist: jsonpatch>=1.33
29
+ Requires-Dist: jsonpointer>=3.0.0
30
+ Requires-Dist: langchain-core>=0.3.51
31
+ Requires-Dist: langsmith>=0.3.27
32
+ Requires-Dist: mcp>=1.6.0
33
+ Requires-Dist: numpy>=2.2.4
34
+ Requires-Dist: orjson>=3.10.16
35
+ Requires-Dist: packaging>=24.2
36
+ Requires-Dist: pandas>=2.2.3
37
+ Requires-Dist: prettytable>=3.16.0
38
+ Requires-Dist: pycparser>=2.22
39
+ Requires-Dist: pydantic>=2.11.3
40
+ Requires-Dist: pydantic-settings>=2.8.1
41
+ Requires-Dist: pydantic_core>=2.33.1
42
+ Requires-Dist: python-dateutil>=2.9.0.post0
43
+ Requires-Dist: python-dotenv>=1.1.0
44
+ Requires-Dist: pytz>=2025.2
45
+ Requires-Dist: PyYAML>=6.0.2
46
+ Requires-Dist: requests>=2.32.3
47
+ Requires-Dist: requests-toolbelt>=1.0.0
48
+ Requires-Dist: six>=1.17.0
49
+ Requires-Dist: sniffio>=1.3.1
50
+ Requires-Dist: sse-starlette>=2.2.1
51
+ Requires-Dist: starlette>=0.46.1
52
+ Requires-Dist: tabulate>=0.9.0
53
+ Requires-Dist: tenacity>=9.1.2
54
+ Requires-Dist: typing-inspection>=0.4.0
55
+ Requires-Dist: typing_extensions>=4.13.1
56
+ Requires-Dist: tzdata>=2025.2
57
+ Requires-Dist: urllib3>=2.3.0
58
+ Requires-Dist: uvicorn>=0.34.0
59
+ Requires-Dist: validators>=0.34.0
60
+ Requires-Dist: wcwidth>=0.2.13
61
+ Requires-Dist: weaviate>=0.1.2
62
+ Requires-Dist: weaviate-client>=3.26.7
63
+ Requires-Dist: zstandard>=0.23.0
64
+ Provides-Extra: dev
65
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
66
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
67
+ Requires-Dist: black>=23.0.0; extra == "dev"
68
+ Requires-Dist: isort>=5.12.0; extra == "dev"
69
+ Requires-Dist: twine>=4.0.0; extra == "dev"
70
+
71
+ # QA - agent service 集合
72
+
73
+ ## 项目结构
74
+ - `server.py`: MCP接口定义和服务启动入口
75
+ - `src/`: 核心功能模块实现
76
+ - `conf/`: 配置文件
77
+ - `utils/`: 工具函数
78
+ - `logs/`: 日志文件夹
79
+
80
+ ## 功能特性
81
+ 1. 天气服务(demo):
82
+ - 获取美国各州天气警报信息
83
+ - 基于经纬度获取天气预报
84
+
85
+ 2. 日志检索服务:
86
+ - 从Grafana获取各业务服务的日志
87
+ - 支持按关键字、时间等条件筛选
88
+ - 支持获取服务接口列表
89
+
90
+ ## 环境配置与启动
91
+
92
+ 项目现在使用ux进行构建和打包,提供了多种安装运行方式。
93
+
94
+ ### 1. 从PyPI安装
95
+
96
+ ```shell
97
+ # 直接从PyPI安装
98
+ pip install minimax-qa-mcp
99
+
100
+ # 运行服务
101
+ minimax-qa-mcp
102
+ ```
103
+
104
+ ### 2. 开发模式
105
+
106
+ ```shell
107
+ # 安装依赖
108
+ pip install ux
109
+ pip install -r requirements.txt
110
+
111
+ # 开发模式运行
112
+ bash run.sh
113
+ # 或
114
+ ux run
115
+ ```
116
+
117
+ ### 3. 构建与发布
118
+
119
+ ```shell
120
+ # 构建项目
121
+ bash build.sh
122
+
123
+ # 发布到PyPI
124
+ bash publish.sh
125
+ ```
126
+
127
+ ### 4. 查看启动
128
+ ```
129
+ 本地生成 log/agent_log.log文件,且打印
130
+ INFO: Uvicorn running on http://0.0.0.0:8888 (Press CTRL+C to quit)
131
+ ```
132
+
133
+ ### 5. 集成到MCP客户端
134
+
135
+ #### curl
136
+ ```shell
137
+ http://0.0.0.0:8888/sse
138
+ ```
139
+
140
+ #### Python模块方式
141
+ ```shell
142
+ python -m minimax_qa_mcp.server
143
+ ```
144
+
145
+ #### Claude Desktop 配置
146
+ ```json
147
+ {
148
+ "agent_name": {
149
+ "command": "minimax-qa-mcp",
150
+ "args": ["--host", "0.0.0.0", "--port", "8888"]
151
+ }
152
+ }
153
+ ```
154
+
155
+ ## API 说明
156
+
157
+ ### 天气服务(demo)
158
+ - `get_alerts(state)`: 获取指定州的天气警报
159
+ - `get_forecast(latitude, longitude)`: 获取指定位置的天气预报
160
+
161
+ ### 日志服务
162
+ - `get_grafana_data(scene, psm, msg, from_time, to_time)`: 获取业务的grafana日志
163
+ - `get_top_methods(scene, psm)`: 获取服务存在调用的接口列表
164
+ - `get_http_data(scene, psm, api_path, from_time, to_time)`: 获取业务http接口流量日志
165
+ - `query_code_segments(query, query_type, limit, exact, advanced, depth, direction, output)`: 查询代码片段
166
+ - `gen_case(input_data, pwd)`: 生成link_case
167
+ - `get_weaviate_info(input_data)`: 获取业务相关信息
@@ -0,0 +1,28 @@
1
+ minimax_qa_mcp/__init__.py,sha256=7r3jCBYTrmA8VWKtzFA-WSHnlRND2xg9oaZDFV3ozWA,188
2
+ minimax_qa_mcp/server.py,sha256=jgozKCjL-YiA0Sfje4jnCse9tLzRny-oMwccvgWpomE,8178
3
+ minimax_qa_mcp/conf/__init__.py,sha256=s_q9K05OfxIzPXLshF3qPedjADeGzjlfU4w_6PiR62g,85
4
+ minimax_qa_mcp/conf/conf.ini,sha256=ny4CtXIib0ggTxI7wmT4v3dZcdNadN7EcUFcyeREaqo,2393
5
+ minimax_qa_mcp/src/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ minimax_qa_mcp/src/demo_langchain/__init__.py,sha256=PZDCBXwlgCJ-UMfOikaTGBrLcOtpJVbuPBzM6RdiLlg,80
7
+ minimax_qa_mcp/src/demo_langchain/langchain_demo.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
8
+ minimax_qa_mcp/src/gateway_case/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ minimax_qa_mcp/src/gateway_case/get_case.py,sha256=djn77_zn_252Mhmk_qn_9k_uz4NAjadq2bEnKyPi7iw,27116
10
+ minimax_qa_mcp/src/generator_case/__init__.py,sha256=Sy4ncJFUvzzFAlwlJAC7cQ2YdD4JFXpmovG3jVVboHc,79
11
+ minimax_qa_mcp/src/generator_case/generator_case.py,sha256=AO6K-LUrpboW54gDZrx4_mpF3jkhmucrPs2_oCXbnnc,50074
12
+ minimax_qa_mcp/src/generator_case/generator_case_langchain.py,sha256=i0OnLBFEWtQymBVH28iILR1XqfKWXhgpYZzewUNSzl0,43256
13
+ minimax_qa_mcp/src/get_weaviate_info/__init__.py,sha256=sMKXpobyDsZBnRFR7ozqPfpJEN1x3cIBM7mTTPrv1PE,80
14
+ minimax_qa_mcp/src/get_weaviate_info/get_weaviate_info.py,sha256=kKjfqDZ0Ka13YuAAT5ru8nB1Twm-JkeJnv1a80lHk8Q,10390
15
+ minimax_qa_mcp/src/grafana/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ minimax_qa_mcp/src/grafana/service.py,sha256=PwgbB99kgwPhy7g1zafkdzCHqwXW8zfG53mlZfAMYqA,4255
17
+ minimax_qa_mcp/src/query_segments/__init__.py,sha256=nNfm8AmQKuQMPPVMtimtgiz_f_yYKY70JoUCa-s7TJ0,80
18
+ minimax_qa_mcp/src/query_segments/query_segments.py,sha256=ZtFEjR9npd1zoCQOVkukvUFXmCub6MaJ_GTy0abLQes,93237
19
+ minimax_qa_mcp/src/xmind2markdown/__init__.py,sha256=IqnCkza9xw5r-0mEetNQoQrkoX-E8yaMmkcgi4MOoPk,80
20
+ minimax_qa_mcp/src/xmind2markdown/xmind_to_markdown.py,sha256=86WZ4aTf1Y4jDLsoBaJZySOeU0G__j_8ZBAtQqccdV4,41903
21
+ minimax_qa_mcp/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ minimax_qa_mcp/utils/logger.py,sha256=R1SyLTra9ngjSu4vKJ21krgl83Or33Q0dRz6-K_OYS8,1089
23
+ minimax_qa_mcp/utils/utils.py,sha256=Xb9d6lz4p3wVDbLKrctfG3v5_Lx1ge6vFMmtAqZ9qF4,8703
24
+ mm_qa_mcp-0.2.0.dist-info/METADATA,sha256=ubINaJAX3AZQRdshpFS6j8VBJO5GiS46IUEe9ngjsjM,4405
25
+ mm_qa_mcp-0.2.0.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
26
+ mm_qa_mcp-0.2.0.dist-info/entry_points.txt,sha256=iP2Pl72UfrJCDakHv_Zxg6ci6gtvZipc8cN1SvqueJk,63
27
+ mm_qa_mcp-0.2.0.dist-info/top_level.txt,sha256=cXfV8AjI2zW5yK2aw4Z2TMV9oRPx6eU04K9ZxwqAtxw,15
28
+ mm_qa_mcp-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.3.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ mm_qa_mcp = minimax_qa_mcp.server:run_server
@@ -0,0 +1 @@
1
+ minimax_qa_mcp