auto-coder 0.1.288__py3-none-any.whl → 0.1.290__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 auto-coder might be problematic. Click here for more details.
- {auto_coder-0.1.288.dist-info → auto_coder-0.1.290.dist-info}/METADATA +2 -2
- {auto_coder-0.1.288.dist-info → auto_coder-0.1.290.dist-info}/RECORD +25 -21
- autocoder/auto_coder_rag.py +10 -0
- autocoder/chat_auto_coder_lang.py +16 -16
- autocoder/common/__init__.py +4 -0
- autocoder/common/auto_coder_lang.py +16 -4
- autocoder/common/mcp_hub.py +99 -77
- autocoder/common/mcp_server.py +162 -61
- autocoder/index/filter/quick_filter.py +373 -3
- autocoder/rag/api_server.py +48 -0
- autocoder/rag/cache/byzer_storage_cache.py +254 -44
- autocoder/rag/cache/cache_result_merge.py +265 -0
- autocoder/rag/cache/file_monitor_cache.py +117 -4
- autocoder/rag/cache/local_byzer_storage_cache.py +286 -58
- autocoder/rag/cache/rag_file_meta.py +494 -0
- autocoder/rag/cache/simple_cache.py +67 -3
- autocoder/rag/conversation_to_queries.py +139 -0
- autocoder/rag/long_context_rag.py +31 -12
- autocoder/rag/qa_conversation_strategy.py +21 -10
- autocoder/rag/searchable.py +58 -0
- autocoder/version.py +1 -1
- {auto_coder-0.1.288.dist-info → auto_coder-0.1.290.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.288.dist-info → auto_coder-0.1.290.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.288.dist-info → auto_coder-0.1.290.dist-info}/entry_points.txt +0 -0
- {auto_coder-0.1.288.dist-info → auto_coder-0.1.290.dist-info}/top_level.txt +0 -0
autocoder/common/mcp_server.py
CHANGED
|
@@ -13,6 +13,7 @@ from pydantic import BaseModel
|
|
|
13
13
|
import sys
|
|
14
14
|
from loguru import logger
|
|
15
15
|
from autocoder.utils.llms import get_single_llm
|
|
16
|
+
from autocoder.chat_auto_coder_lang import get_message_with_format
|
|
16
17
|
|
|
17
18
|
@dataclass
|
|
18
19
|
class McpRequest:
|
|
@@ -157,55 +158,135 @@ class McpServer:
|
|
|
157
158
|
print(f"We have already updated the server configuration in ~/.autocoder/mcp/settings.json.\n")
|
|
158
159
|
print(f"After installation, you can restart the auto-coder.chat using the server.\033[0m\n")
|
|
159
160
|
|
|
161
|
+
def _deep_merge_dicts(self, dict1, dict2):
|
|
162
|
+
"""
|
|
163
|
+
深度合并两个字典,包括嵌套的字典
|
|
164
|
+
dict1是基础字典,dict2是优先字典(当有冲突时保留dict2的值)
|
|
165
|
+
"""
|
|
166
|
+
result = dict1.copy()
|
|
167
|
+
for key, value in dict2.items():
|
|
168
|
+
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
|
|
169
|
+
# 如果两个字典都包含相同的键,并且两个值都是字典,则递归合并
|
|
170
|
+
result[key] = self._deep_merge_dicts(result[key], value)
|
|
171
|
+
else:
|
|
172
|
+
# 否则,使用dict2的值覆盖或添加到结果
|
|
173
|
+
result[key] = value
|
|
174
|
+
return result
|
|
175
|
+
|
|
176
|
+
def _parse_command_line_args(self, server_name_or_config: str) -> tuple[str, dict]:
|
|
177
|
+
"""Parse command-line style arguments into name and config"""
|
|
178
|
+
name = ""
|
|
179
|
+
config = {}
|
|
180
|
+
args = server_name_or_config.strip().split()
|
|
181
|
+
i = 0
|
|
182
|
+
while i < len(args):
|
|
183
|
+
if args[i] == "--name" and i + 1 < len(args):
|
|
184
|
+
name = args[i + 1]
|
|
185
|
+
i += 2
|
|
186
|
+
elif args[i] == "--command" and i + 1 < len(args):
|
|
187
|
+
config["command"] = args[i + 1]
|
|
188
|
+
i += 2
|
|
189
|
+
elif args[i] == "--args":
|
|
190
|
+
config["args"] = []
|
|
191
|
+
i += 1
|
|
192
|
+
while i < len(args) and not args[i].startswith("--"):
|
|
193
|
+
config["args"].append(args[i])
|
|
194
|
+
i += 1
|
|
195
|
+
elif args[i] == "--env":
|
|
196
|
+
config["env"] = {}
|
|
197
|
+
i += 1
|
|
198
|
+
while i < len(args) and not args[i].startswith("--"):
|
|
199
|
+
if "=" in args[i]:
|
|
200
|
+
key, value = args[i].split("=", 1)
|
|
201
|
+
config["env"][key] = value
|
|
202
|
+
i += 1
|
|
203
|
+
else:
|
|
204
|
+
i += 1
|
|
205
|
+
|
|
206
|
+
template_config = {}
|
|
207
|
+
|
|
208
|
+
if name in MCP_BUILD_IN_SERVERS:
|
|
209
|
+
template_config = MCP_BUILD_IN_SERVERS[name]
|
|
210
|
+
else:
|
|
211
|
+
# 查找外部server
|
|
212
|
+
external_servers = get_mcp_external_servers()
|
|
213
|
+
for s in external_servers:
|
|
214
|
+
if s.name == name:
|
|
215
|
+
if s.runtime == "python":
|
|
216
|
+
self._install_python_package(name)
|
|
217
|
+
template_config = {
|
|
218
|
+
"command": "python",
|
|
219
|
+
"args": [
|
|
220
|
+
"-m", name.replace("-", "_")
|
|
221
|
+
],
|
|
222
|
+
}
|
|
223
|
+
elif s.runtime == "node":
|
|
224
|
+
self._install_node_package(name)
|
|
225
|
+
template_config = {
|
|
226
|
+
"command": "npx",
|
|
227
|
+
"args": [
|
|
228
|
+
"-y",
|
|
229
|
+
"-g",
|
|
230
|
+
name
|
|
231
|
+
]
|
|
232
|
+
}
|
|
233
|
+
break
|
|
234
|
+
|
|
235
|
+
# 深度合并两个配置,以用户配置为主
|
|
236
|
+
config = self._deep_merge_dicts(template_config, config)
|
|
237
|
+
|
|
238
|
+
if not config.get("args") and (name.startswith("@") or config.get("command") in ["npx", "npm"]):
|
|
239
|
+
config["args"] = ["-y", "-g", name]
|
|
240
|
+
|
|
241
|
+
## 如果有模板,则无需再次安装,处理模板的时候会自动安装
|
|
242
|
+
if not template_config:
|
|
243
|
+
# Install package if needed
|
|
244
|
+
if name.startswith("@") or config.get("command") in ["npx", "npm"]:
|
|
245
|
+
self._install_node_package(name)
|
|
246
|
+
else:
|
|
247
|
+
self._install_python_package(name)
|
|
248
|
+
|
|
249
|
+
return name, config
|
|
250
|
+
|
|
251
|
+
def _parse_json_config(self, server_name_or_config: str) -> tuple[str, dict]:
|
|
252
|
+
"""Parse JSON configuration into name and config"""
|
|
253
|
+
raw_config = json.loads(server_name_or_config)
|
|
254
|
+
# 用户给了一个完整的配置
|
|
255
|
+
if "mcpServers" in raw_config:
|
|
256
|
+
raw_config = raw_config["mcpServers"]
|
|
257
|
+
|
|
258
|
+
# 取第一个server 配置
|
|
259
|
+
config = list(raw_config.values())[0]
|
|
260
|
+
name = list(raw_config.keys())[0]
|
|
261
|
+
if name.startswith("@") or config["command"] in ["npx", "npm"]:
|
|
262
|
+
for item in config["args"]:
|
|
263
|
+
if name in item:
|
|
264
|
+
self._install_node_package(item)
|
|
265
|
+
else:
|
|
266
|
+
self._install_python_package(name)
|
|
267
|
+
|
|
268
|
+
return name, config
|
|
269
|
+
|
|
270
|
+
|
|
160
271
|
async def _install_server(self, request: McpInstallRequest, hub: McpHub) -> McpResponse:
|
|
161
272
|
"""Install an MCP server with module dependency check"""
|
|
162
273
|
name = ""
|
|
163
274
|
config = {}
|
|
164
275
|
try:
|
|
165
276
|
server_name_or_config = request.server_name_or_config
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
else:
|
|
180
|
-
self._install_python_package(name)
|
|
181
|
-
except json.JSONDecodeError:
|
|
182
|
-
name = server_name_or_config.strip()
|
|
183
|
-
if name not in MCP_BUILD_IN_SERVERS:
|
|
184
|
-
# 查找外部server
|
|
185
|
-
external_servers = get_mcp_external_servers()
|
|
186
|
-
for s in external_servers:
|
|
187
|
-
if s.name == name:
|
|
188
|
-
if s.runtime == "python":
|
|
189
|
-
self._install_python_package(name)
|
|
190
|
-
config = {
|
|
191
|
-
"command": "python",
|
|
192
|
-
"args": [
|
|
193
|
-
"-m", name.replace("-", "_")
|
|
194
|
-
],
|
|
195
|
-
}
|
|
196
|
-
elif s.runtime == "node":
|
|
197
|
-
self._install_node_package(name)
|
|
198
|
-
config = {
|
|
199
|
-
"command": "npx",
|
|
200
|
-
"args": [
|
|
201
|
-
"-y",
|
|
202
|
-
"-g",
|
|
203
|
-
name
|
|
204
|
-
]
|
|
205
|
-
}
|
|
206
|
-
break
|
|
207
|
-
else:
|
|
208
|
-
config = MCP_BUILD_IN_SERVERS[name]
|
|
277
|
+
|
|
278
|
+
# Try different parsing methods
|
|
279
|
+
if server_name_or_config.strip().startswith("--"):
|
|
280
|
+
# Command-line style arguments
|
|
281
|
+
name, config = self._parse_command_line_args(server_name_or_config)
|
|
282
|
+
else:
|
|
283
|
+
try:
|
|
284
|
+
# Try parsing as JSON
|
|
285
|
+
name, config = self._parse_json_config(server_name_or_config)
|
|
286
|
+
except json.JSONDecodeError:
|
|
287
|
+
logger.error(f"Failed to parse JSON config: {server_name_or_config}")
|
|
288
|
+
pass
|
|
289
|
+
|
|
209
290
|
if not name:
|
|
210
291
|
raise ValueError(
|
|
211
292
|
"MCP server name is not available in MCP_BUILD_IN_SERVERS or external servers")
|
|
@@ -214,10 +295,13 @@ class McpServer:
|
|
|
214
295
|
if not config:
|
|
215
296
|
raise ValueError(f"MCP server {name} config is not available")
|
|
216
297
|
|
|
217
|
-
await hub.add_server_config(name, config)
|
|
218
|
-
|
|
298
|
+
is_success = await hub.add_server_config(name, config)
|
|
299
|
+
if is_success:
|
|
300
|
+
return McpResponse(result=get_message_with_format("mcp_install_success", result=request.server_name_or_config))
|
|
301
|
+
else:
|
|
302
|
+
return McpResponse(result="", error=get_message_with_format("mcp_install_error", error="Failed to establish connection"))
|
|
219
303
|
except Exception as e:
|
|
220
|
-
return McpResponse(result="", error=
|
|
304
|
+
return McpResponse(result="", error=get_message_with_format("mcp_install_error", error=str(e)))
|
|
221
305
|
|
|
222
306
|
async def _process_request(self):
|
|
223
307
|
hub = McpHub()
|
|
@@ -236,9 +320,11 @@ class McpServer:
|
|
|
236
320
|
elif isinstance(request, McpRemoveRequest):
|
|
237
321
|
try:
|
|
238
322
|
await hub.remove_server_config(request.server_name)
|
|
239
|
-
await self._response_queue.put(McpResponse(
|
|
323
|
+
await self._response_queue.put(McpResponse(
|
|
324
|
+
result=get_message_with_format("mcp_remove_success", result=request.server_name)))
|
|
240
325
|
except Exception as e:
|
|
241
|
-
await self._response_queue.put(McpResponse(
|
|
326
|
+
await self._response_queue.put(McpResponse(
|
|
327
|
+
result="", error=get_message_with_format("mcp_remove_error", error=str(e))))
|
|
242
328
|
|
|
243
329
|
elif isinstance(request, McpListRequest):
|
|
244
330
|
try:
|
|
@@ -253,20 +339,22 @@ class McpServer:
|
|
|
253
339
|
|
|
254
340
|
# Combine results
|
|
255
341
|
all_servers = builtin_servers + external_list
|
|
256
|
-
result =
|
|
257
|
-
"\n".join(all_servers)
|
|
342
|
+
result = "\n".join(all_servers)
|
|
258
343
|
|
|
259
344
|
await self._response_queue.put(McpResponse(result=result))
|
|
260
345
|
except Exception as e:
|
|
261
|
-
await self._response_queue.put(McpResponse(
|
|
346
|
+
await self._response_queue.put(McpResponse(
|
|
347
|
+
result="", error=get_message_with_format("mcp_list_builtin_error", error=str(e))))
|
|
262
348
|
|
|
263
349
|
elif isinstance(request, McpListRunningRequest):
|
|
264
350
|
try:
|
|
265
351
|
running_servers = "\n".join(
|
|
266
352
|
[f"- {server.name}" for server in hub.get_servers()])
|
|
267
|
-
|
|
353
|
+
result = running_servers if running_servers else ""
|
|
354
|
+
await self._response_queue.put(McpResponse(result=result))
|
|
268
355
|
except Exception as e:
|
|
269
|
-
await self._response_queue.put(McpResponse(
|
|
356
|
+
await self._response_queue.put(McpResponse(
|
|
357
|
+
result="", error=get_message_with_format("mcp_list_running_error", error=str(e))))
|
|
270
358
|
|
|
271
359
|
elif isinstance(request, McpRefreshRequest):
|
|
272
360
|
try:
|
|
@@ -274,23 +362,36 @@ class McpServer:
|
|
|
274
362
|
await hub.refresh_server_connection(request.name)
|
|
275
363
|
else:
|
|
276
364
|
await hub.initialize()
|
|
277
|
-
await self._response_queue.put(McpResponse(
|
|
365
|
+
await self._response_queue.put(McpResponse(
|
|
366
|
+
result=get_message_with_format("mcp_refresh_success")))
|
|
278
367
|
except Exception as e:
|
|
279
|
-
await self._response_queue.put(McpResponse(
|
|
368
|
+
await self._response_queue.put(McpResponse(
|
|
369
|
+
result="", error=get_message_with_format("mcp_refresh_error", error=str(e))))
|
|
280
370
|
|
|
281
371
|
else:
|
|
282
|
-
|
|
372
|
+
if not request.query.strip():
|
|
373
|
+
await self._response_queue.put(McpResponse(
|
|
374
|
+
result="", error=get_message_with_format("mcp_query_empty")))
|
|
375
|
+
continue
|
|
376
|
+
|
|
377
|
+
llm = get_single_llm(request.model, product_mode=request.product_mode)
|
|
283
378
|
mcp_executor = McpExecutor(hub, llm)
|
|
284
379
|
conversations = [
|
|
285
380
|
{"role": "user", "content": request.query}]
|
|
286
381
|
_, results = await mcp_executor.run(conversations)
|
|
382
|
+
|
|
287
383
|
if not results:
|
|
288
|
-
await self._response_queue.put(McpResponse(
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
384
|
+
await self._response_queue.put(McpResponse(
|
|
385
|
+
result=get_message_with_format("mcp_error_title"),
|
|
386
|
+
error="No results"))
|
|
387
|
+
else:
|
|
388
|
+
results_str = "\n\n".join(
|
|
389
|
+
mcp_executor.format_mcp_result(result) for result in results)
|
|
390
|
+
await self._response_queue.put(McpResponse(
|
|
391
|
+
result=get_message_with_format("mcp_response_title") + "\n" + results_str))
|
|
292
392
|
except Exception as e:
|
|
293
|
-
await self._response_queue.put(McpResponse(
|
|
393
|
+
await self._response_queue.put(McpResponse(
|
|
394
|
+
result="", error=get_message_with_format("mcp_error_title") + ": " + str(e)))
|
|
294
395
|
|
|
295
396
|
def send_request(self, request: McpRequest) -> McpResponse:
|
|
296
397
|
async def _send():
|