mm-qa-mcp 3.0.1__tar.gz → 3.0.3__tar.gz

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 (39) hide show
  1. {mm_qa_mcp-3.0.1/mm_qa_mcp.egg-info → mm_qa_mcp-3.0.3}/PKG-INFO +2 -1
  2. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/minimax_qa_mcp/server.py +27 -3
  3. mm_qa_mcp-3.0.3/minimax_qa_mcp/src/auto_case/case_write.py +182 -0
  4. mm_qa_mcp-3.0.3/minimax_qa_mcp/src/auto_case/pdf_jiexi.py +28 -0
  5. mm_qa_mcp-3.0.3/minimax_qa_mcp/utils/__init__.py +0 -0
  6. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3/mm_qa_mcp.egg-info}/PKG-INFO +2 -1
  7. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/mm_qa_mcp.egg-info/SOURCES.txt +3 -0
  8. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/mm_qa_mcp.egg-info/requires.txt +1 -0
  9. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/pyproject.toml +3 -2
  10. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/MANIFEST.in +0 -0
  11. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/README.md +0 -0
  12. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/minimax_qa_mcp/__init__.py +0 -0
  13. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/minimax_qa_mcp/conf/__init__.py +0 -0
  14. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/minimax_qa_mcp/conf/conf.ini +0 -0
  15. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/minimax_qa_mcp/src/__init__.py +0 -0
  16. {mm_qa_mcp-3.0.1/minimax_qa_mcp/src/gateway_case → mm_qa_mcp-3.0.3/minimax_qa_mcp/src/auto_case}/__init__.py +0 -0
  17. {mm_qa_mcp-3.0.1/minimax_qa_mcp/src/grafana → mm_qa_mcp-3.0.3/minimax_qa_mcp/src/gateway_case}/__init__.py +0 -0
  18. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/minimax_qa_mcp/src/gateway_case/get_case.py +0 -0
  19. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/minimax_qa_mcp/src/generator_case/__init__.py +0 -0
  20. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/minimax_qa_mcp/src/generator_case/generator_case.py +0 -0
  21. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/minimax_qa_mcp/src/generator_case/generator_case_langchain.py +0 -0
  22. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/minimax_qa_mcp/src/get_full_api_call_chain/__init__.py +0 -0
  23. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/minimax_qa_mcp/src/get_full_api_call_chain/get_full_api_call_chain.py +0 -0
  24. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/minimax_qa_mcp/src/get_weaviate_info/__init__.py +0 -0
  25. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/minimax_qa_mcp/src/get_weaviate_info/get_weaviate_info.py +0 -0
  26. {mm_qa_mcp-3.0.1/minimax_qa_mcp/utils → mm_qa_mcp-3.0.3/minimax_qa_mcp/src/grafana}/__init__.py +0 -0
  27. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/minimax_qa_mcp/src/grafana/service.py +0 -0
  28. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/minimax_qa_mcp/src/query_segments/__init__.py +0 -0
  29. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/minimax_qa_mcp/src/query_segments/query_segments.py +0 -0
  30. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/minimax_qa_mcp/src/xmind2markdown/__init__.py +0 -0
  31. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/minimax_qa_mcp/src/xmind2markdown/xmind_to_markdown.py +0 -0
  32. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/minimax_qa_mcp/utils/logger.py +0 -0
  33. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/minimax_qa_mcp/utils/mysql_op.py +0 -0
  34. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/minimax_qa_mcp/utils/utils.py +0 -0
  35. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/mm_qa_mcp.egg-info/dependency_links.txt +0 -0
  36. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/mm_qa_mcp.egg-info/entry_points.txt +0 -0
  37. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/mm_qa_mcp.egg-info/top_level.txt +0 -0
  38. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/setup.cfg +0 -0
  39. {mm_qa_mcp-3.0.1 → mm_qa_mcp-3.0.3}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mm_qa_mcp
3
- Version: 3.0.1
3
+ Version: 3.0.3
4
4
  Summary: QA agent service 集合
5
5
  Author-email: xingyun <xingyun@minimaxi.com>
6
6
  License-Expression: MIT
@@ -60,6 +60,7 @@ Requires-Dist: validators<1.0.0,>=0.21.2
60
60
  Requires-Dist: wcwidth>=0.2.13
61
61
  Requires-Dist: weaviate-client<4.0.0,>=3.25.0
62
62
  Requires-Dist: zstandard>=0.23.0
63
+ Requires-Dist: pdfplumber>=0.11.6
63
64
  Provides-Extra: dev
64
65
  Requires-Dist: pytest>=7.0.0; extra == "dev"
65
66
  Requires-Dist: mypy>=1.0.0; extra == "dev"
@@ -2,6 +2,7 @@
2
2
  import asyncio
3
3
  import sys
4
4
  import os
5
+ import json
5
6
 
6
7
  # 将项目根目录添加到Python路径中
7
8
  current_dir = os.path.dirname(os.path.abspath(__file__))
@@ -18,7 +19,9 @@ from minimax_qa_mcp.src.generator_case.generator_case import GeneratorCase
18
19
  from minimax_qa_mcp.src.get_weaviate_info.get_weaviate_info import GetWeaviateInfo
19
20
  from minimax_qa_mcp.src.grafana.service import GetFromGrafana, GetApiFromGrafana
20
21
  from minimax_qa_mcp.src.gateway_case.get_case import CaseGrafanaService
21
- from minimax_qa_mcp.src.query_segments.query_segments import query_main, TYPE_API, TYPE_FUNC, TYPE_CODE, TYPE_ANY, TYPE_FUNC_DETAIL
22
+ from minimax_qa_mcp.src.query_segments.query_segments import query_main, TYPE_API, TYPE_FUNC, TYPE_CODE, TYPE_ANY, \
23
+ TYPE_FUNC_DETAIL
24
+ from minimax_qa_mcp.src.auto_case.case_write import PDFWeaviateInfo
22
25
  from minimax_qa_mcp.src.get_full_api_call_chain.get_full_api_call_chain import GetFullApiCallChain
23
26
 
24
27
  # Initialize FastMCP server
@@ -162,6 +165,24 @@ async def get_weaviate_info(input_data: str) -> dict:
162
165
  return result
163
166
 
164
167
 
168
+ @mcp.tool()
169
+ async def get_auto_case(file_path: str, ref_case_name: str, use_moxing: bool = True) -> dict:
170
+ """
171
+ 根据给定的文件路径和参考case名称,自动生成case
172
+
173
+ Args:
174
+ file_path: 需要处理的文件路径
175
+ ref_case_name: 参考的case名称
176
+ use_moxing: 是否使用模型,开关默认开启
177
+ Returns:
178
+ 包含自动生成case的相关信息的字典
179
+ """
180
+ # 调用 PDFWeaviateInfo.get_pdf_and_weaviate_info 方法
181
+ prd_case = PDFWeaviateInfo(file_path, ref_case_name, use_moxing=use_moxing)
182
+ case_response = prd_case.get_pdf_and_weaviate_info()
183
+ return case_response
184
+
185
+
165
186
  @mcp.tool()
166
187
  async def get_full_api_call_chain(api_path: str) -> dict:
167
188
  """
@@ -171,6 +192,9 @@ async def get_full_api_call_chain(api_path: str) -> dict:
171
192
  Return:
172
193
  API调用链信息
173
194
  """
195
+ # 判断api_path是不是'/'开头 不是的话 拼接
196
+ if not api_path.startswith('/'):
197
+ api_path = '/' + api_path
174
198
  logger.info(f"===== The input params is :{api_path}")
175
199
 
176
200
  api_call_chain = GetFullApiCallChain(api_path)
@@ -190,10 +214,10 @@ def run_server():
190
214
  # 确保当前工作目录在sys.path中
191
215
  if os.getcwd() not in sys.path:
192
216
  sys.path.insert(0, os.getcwd())
193
-
217
+
194
218
  # 输出启动信息
195
219
  print("Starting Minimax QA MCP server from CLI")
196
-
220
+
197
221
  # 调用主函数
198
222
  main()
199
223
 
@@ -0,0 +1,182 @@
1
+ import functools
2
+ import re
3
+ from minimax_qa_mcp.src.get_weaviate_info.get_weaviate_info import GetWeaviateInfo
4
+ from minimax_qa_mcp.src.auto_case.pdf_jiexi import read_pdf_text
5
+ import logging
6
+ import requests
7
+ import json
8
+ import time
9
+
10
+ # 设置日志
11
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
12
+ logger = logging.getLogger('生成日志case')
13
+
14
+
15
+ def retry(max_attempts=3, wait=2):
16
+ def decorator(func):
17
+ @functools.wraps(func)
18
+ def wrapper(*args, **kwargs):
19
+ last_exception = None
20
+ for attempt in range(1, max_attempts + 1):
21
+ try:
22
+ return func(*args, **kwargs)
23
+ except Exception as e:
24
+ logger.warning(f"第{attempt}次请求异常:{e}")
25
+ last_exception = e
26
+ if attempt < max_attempts:
27
+ time.sleep(wait)
28
+ logger.error(f"请求失败,已重试{max_attempts}次。")
29
+ return f"请求失败,已重试{max_attempts}次,最后异常:{last_exception}"
30
+
31
+ return wrapper
32
+
33
+ return decorator
34
+
35
+
36
+ class PDFWeaviateInfo(GetWeaviateInfo):
37
+ def __init__(self, prd_path, input_question,use_moxing, is_need_module=False):
38
+ super().__init__(input_question, is_need_module)
39
+ self.prd_path = prd_path
40
+ self.timeout = 120
41
+ self.use_moxing = use_moxing
42
+
43
+ def get_pdf_and_weaviate_info(self):
44
+ # 解析PDF内容
45
+ pdf_text = read_pdf_text(self.prd_path)
46
+ # 获取Weaviate信息
47
+ weaviate_info = self.get_knowledge()
48
+ # 拼接结果
49
+ result = {
50
+ 'pdf_text': pdf_text,
51
+ 'weaviate_info': weaviate_info
52
+ }
53
+
54
+ case_response = self.call_model(result)
55
+ return case_response
56
+
57
+ def call_model(self, prd_content, max_attempts=5, wait=6):
58
+ """
59
+ 调用模型,给出参考case
60
+ Args:
61
+ prd_content: prd信息+参考case
62
+ max_attempts: 最大重试次数
63
+ wait: 重试等待时间(秒)
64
+
65
+ Returns:
66
+ content内容拼接后的字符串,或错误信息
67
+ """
68
+ prd_case = prd_content.get("pdf_text", "")
69
+ prompt = f"需求内容:{prd_case}"
70
+ cankao_case = prd_content.get("weaviate_info", {}).get("results", [])
71
+ for i, content in enumerate(cankao_case):
72
+ case_content = content.get('content', '').strip()
73
+ case_content_clean = re.sub(r'\s+', ' ', case_content)
74
+ prompt += f"参考用例{i + 1}、用例名称:{content.get('title', 'N/A')}、用例内容:{case_content_clean}\n\n"
75
+ break
76
+
77
+ prompt += " 帮我写一份测试用例,参考用例若与本次需求相关可借鉴其设计思路和细节,不相关则忽略,用例设计需覆盖功能、边界、异常、安全、兼容性、性能、数据一致性、UI等多个维度,每个测试用例需包含:用例名称、用例编号、前置条件、测试步骤、预期结果、测试环境,用例内容要具体、细致,测试步骤要可操作,预期结果要明确"
78
+ # prompt += "帮我写一份测试用例,参考用例若与本次需求相关可借鉴其设计思路和细节,不相关则忽略"
79
+ clean_params = prompt.replace('\\"', "'")
80
+ prd_prompt = clean_params.replace("\n", " ").strip()
81
+
82
+ if not self.use_moxing:
83
+ return prd_prompt
84
+
85
+ payload = {
86
+ "scene": "qa_agent",
87
+ "params": {
88
+ "user_content": prd_prompt
89
+ }
90
+ }
91
+
92
+ logger.info(f"==== 发送请求调用模型 ======")
93
+ # print(payload)
94
+ last_exception = None
95
+ for attempt in range(1, max_attempts + 1):
96
+ try:
97
+ response = requests.post(
98
+ self.api_url,
99
+ json=payload,
100
+ headers={'Content-Type': 'application/json'},
101
+ verify=False,
102
+ timeout=self.timeout
103
+ )
104
+
105
+ # logger.info(f"API响应状态码: {response.status_code}")
106
+ # logger.info(f"API响应内容: {response.text}")
107
+
108
+ if response.status_code != 200:
109
+ logger.error(f"API请求失败,状态码: {response.status_code}")
110
+ raise Exception(f"API请求失败,状态码: {response.status_code}")
111
+
112
+ try:
113
+ resp_json = response.json()
114
+ if 'response' in resp_json:
115
+ try:
116
+ model_response = json.loads(resp_json['response'])
117
+ if 'choices' in model_response and isinstance(model_response['choices'], list):
118
+ # 兼容OpenAI/Claude等大模型返回结构
119
+ for choice in model_response['choices']:
120
+ message = choice.get('message', {})
121
+ content = message.get('content')
122
+ if content:
123
+ content += "\n- 需求内容" + f"{prd_case}"
124
+ return content
125
+ # 如果没找到content,兜底返回整个choices
126
+ return str(model_response['choices'])
127
+ elif 'content' in model_response and isinstance(model_response['content'], list):
128
+ # 兼容content直接在顶层的情况
129
+ text_content = ""
130
+ for item in model_response['content']:
131
+ if item.get('type') == 'text':
132
+ text_content += item.get('text', '')
133
+ text_content += "\n- 需求内容" + f"{prd_case}"
134
+ return text_content
135
+ else:
136
+ return str(model_response)
137
+ except json.JSONDecodeError as json_e:
138
+ logger.error(f"解析二层JSON失败: {json_e}")
139
+ # 抛出异常,触发重试机制
140
+ raise Exception(f"解析二层JSON失败: {json_e}")
141
+ except Exception as e:
142
+ logger.error(f"处理二层JSON时发生未知错误: {e}")
143
+ # 抛出异常,触发重试机制
144
+ raise Exception(f"处理二层JSON时发生未知错误: {e}")
145
+
146
+ # 如果没有response字段,直接返回原始响应
147
+ return response.text
148
+
149
+ except json.JSONDecodeError as json_e:
150
+ logger.error(f"解析一层JSON失败: {json_e}")
151
+ # 抛出异常,触发重试机制
152
+ raise Exception(f"解析一层JSON失败: {json_e}")
153
+ except Exception as e:
154
+ logger.error(f"处理响应时发生未知错误: {e}")
155
+ # 抛出异常,触发重试机制
156
+ raise Exception(f"处理响应时发生未知错误: {e}")
157
+
158
+ except requests.RequestException as e:
159
+ logger.warning(f"第{attempt}次网络请求异常:{e}")
160
+ last_exception = e
161
+ if attempt < max_attempts:
162
+ time.sleep(wait)
163
+ continue
164
+ else:
165
+ break
166
+ except Exception as e:
167
+ logger.warning(f"第{attempt}次请求处理异常:{e}")
168
+ last_exception = e
169
+ if attempt < max_attempts:
170
+ time.sleep(wait)
171
+ continue
172
+ else:
173
+ break
174
+
175
+ logger.error(f"请求失败,已重试{max_attempts}次。最后异常:{last_exception}")
176
+ return f"请求失败,已重试{max_attempts}次,最后异常:{last_exception}"
177
+
178
+
179
+ if __name__ == '__main__':
180
+ a = PDFWeaviateInfo(prd_path="支持搜索用户功能.pdf", input_question="星野支持搜索用户功能的相关用例")
181
+ print(a.get_pdf_and_weaviate_info())
182
+ # read_pdf_text(prd_path = "注销流程优化.pdf")
@@ -0,0 +1,28 @@
1
+ import pdfplumber
2
+ import warnings
3
+ import re
4
+ warnings.filterwarnings("ignore")
5
+ def read_pdf_text(pdf_path):
6
+ """
7
+ 读取指定路径的PDF文件内容,返回所有文本内容的字符串。
8
+ :param pdf_path: PDF文件的本地路径
9
+ :return: PDF中的全部文本内容
10
+ """
11
+ all_text = ""
12
+ with pdfplumber.open(pdf_path) as pdf:
13
+ for page in pdf.pages:
14
+ all_text += page.extract_text() or ""
15
+ # 清除特殊不可见字符
16
+ all_text = re.sub(r'[\x00-\x1F\x7F]', '', all_text)
17
+ # all_text = clean_encoding(all_text)
18
+ return all_text
19
+
20
+ # def clean_encoding(text):
21
+ # # 先转换为bytes,再解码回字符串
22
+ # text_bytes = text.encode('utf-8', errors='ignore')
23
+ # cleaned_text = text_bytes.decode('utf-8')
24
+ # return cleaned_text
25
+ if __name__ == "__main__":
26
+ pdf_path = "创作输入框改版.pdf"
27
+ text = read_pdf_text(pdf_path)
28
+ print(text)
File without changes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mm_qa_mcp
3
- Version: 3.0.1
3
+ Version: 3.0.3
4
4
  Summary: QA agent service 集合
5
5
  Author-email: xingyun <xingyun@minimaxi.com>
6
6
  License-Expression: MIT
@@ -60,6 +60,7 @@ Requires-Dist: validators<1.0.0,>=0.21.2
60
60
  Requires-Dist: wcwidth>=0.2.13
61
61
  Requires-Dist: weaviate-client<4.0.0,>=3.25.0
62
62
  Requires-Dist: zstandard>=0.23.0
63
+ Requires-Dist: pdfplumber>=0.11.6
63
64
  Provides-Extra: dev
64
65
  Requires-Dist: pytest>=7.0.0; extra == "dev"
65
66
  Requires-Dist: mypy>=1.0.0; extra == "dev"
@@ -7,6 +7,9 @@ minimax_qa_mcp/server.py
7
7
  minimax_qa_mcp/conf/__init__.py
8
8
  minimax_qa_mcp/conf/conf.ini
9
9
  minimax_qa_mcp/src/__init__.py
10
+ minimax_qa_mcp/src/auto_case/__init__.py
11
+ minimax_qa_mcp/src/auto_case/case_write.py
12
+ minimax_qa_mcp/src/auto_case/pdf_jiexi.py
10
13
  minimax_qa_mcp/src/gateway_case/__init__.py
11
14
  minimax_qa_mcp/src/gateway_case/get_case.py
12
15
  minimax_qa_mcp/src/generator_case/__init__.py
@@ -47,6 +47,7 @@ validators<1.0.0,>=0.21.2
47
47
  wcwidth>=0.2.13
48
48
  weaviate-client<4.0.0,>=3.25.0
49
49
  zstandard>=0.23.0
50
+ pdfplumber>=0.11.6
50
51
 
51
52
  [dev]
52
53
  pytest>=7.0.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "mm_qa_mcp"
7
- version = "3.0.1"
7
+ version = "3.0.3"
8
8
  description = "QA agent service 集合"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -68,7 +68,8 @@ dependencies = [
68
68
  "validators>=0.21.2,<1.0.0",
69
69
  "wcwidth>=0.2.13",
70
70
  "weaviate-client>=3.25.0,<4.0.0",
71
- "zstandard>=0.23.0"
71
+ "zstandard>=0.23.0",
72
+ "pdfplumber>=0.11.6"
72
73
  ]
73
74
 
74
75
  [project.optional-dependencies]
File without changes
File without changes
File without changes
File without changes