cnks 0.3.1__py3-none-any.whl → 0.3.2__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.
src/server.py CHANGED
@@ -9,60 +9,16 @@
9
9
 
10
10
  Exposed Tools:
11
11
  - search_keyword: 搜索指定关键词并获取相关引用
12
- - process_link: 处理特定链接并提取引用信息
13
- - close_browser: 关闭浏览器实例
14
12
  """
15
13
 
16
14
  import asyncio
17
15
  import json
18
- import logging
19
16
  import os
20
17
  import sys
21
18
  import time
22
- import traceback
23
19
  import uuid
24
20
  from typing import Dict, List, Any, Optional
25
21
 
26
- # 配置日志记录
27
- try:
28
- # 尝试使用绝对路径
29
- log_dir = os.path.dirname(os.path.abspath(__file__))
30
- log_file = os.path.join(os.path.dirname(log_dir), "cnks_server.log")
31
-
32
- # 创建处理器
33
- file_handler = logging.FileHandler(log_file, mode="a")
34
- console_handler = logging.StreamHandler()
35
-
36
- # 设置格式
37
- formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
38
- file_handler.setFormatter(formatter)
39
- console_handler.setFormatter(formatter)
40
-
41
- # 获取日志记录器并添加处理器
42
- logger = logging.getLogger("cnks.server")
43
- logger.setLevel(logging.DEBUG)
44
-
45
- # 移除现有处理器以避免重复
46
- if logger.handlers:
47
- for handler in logger.handlers:
48
- logger.removeHandler(handler)
49
-
50
- logger.addHandler(file_handler)
51
- logger.addHandler(console_handler)
52
-
53
- # 打印确认信息
54
- print(f"Server logger initialized, logging to: {log_file}")
55
- logger.info(f"Server logging to: {log_file}")
56
- except Exception as e:
57
- # 回退到基本控制台日志记录
58
- logging.basicConfig(
59
- level=logging.DEBUG,
60
- format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
61
- )
62
- logger = logging.getLogger("cnks.server")
63
- logger.error(f"Failed to set up file logging: {str(e)}")
64
- print(f"Error setting up server file logging: {str(e)}")
65
-
66
22
  # 导入MCP服务器模块
67
23
  try:
68
24
  from mcp.server.models import InitializationOptions
@@ -72,14 +28,14 @@ try:
72
28
  MCP_AVAILABLE = True
73
29
  except ImportError:
74
30
  MCP_AVAILABLE = False
75
- logger.error("MCP not available. Install with: pip install mcp-py")
31
+ print("MCP不可用。请安装: pip install mcp-py")
76
32
 
77
33
  # 尝试导入dotenv支持环境变量
78
34
  try:
79
35
  from dotenv import load_dotenv
80
36
  load_dotenv()
81
37
  except ImportError:
82
- logger.warning("dotenv not available, environment variables may not be loaded")
38
+ pass
83
39
 
84
40
  # 导入Worker模块
85
41
  try:
@@ -88,8 +44,8 @@ except ImportError:
88
44
  try:
89
45
  from worker import Worker
90
46
  except ImportError:
91
- logger.error("Worker module not available")
92
- raise ImportError("Worker module not available")
47
+ print("Worker模块不可用")
48
+ raise ImportError("Worker模块不可用")
93
49
 
94
50
  # 初始化MCP服务器
95
51
  server = Server("CNKS Server")
@@ -149,25 +105,6 @@ async def handle_list_tools() -> list[types.Tool]:
149
105
  },
150
106
  "required": ["keyword"]
151
107
  }
152
- ),
153
- types.Tool(
154
- name="process_link",
155
- description="处理特定链接并提取引用信息",
156
- inputSchema={
157
- "type": "object",
158
- "properties": {
159
- "link": {"type": "string", "description": "要处理的文章链接"}
160
- },
161
- "required": ["link"]
162
- }
163
- ),
164
- types.Tool(
165
- name="close_browser",
166
- description="关闭浏览器资源",
167
- inputSchema={
168
- "type": "object",
169
- "properties": {}
170
- }
171
108
  )
172
109
  ]
173
110
 
@@ -189,7 +126,7 @@ class SearchKeywordToolHandler(ToolHandler):
189
126
  return [types.TextContent(type="text", text="错误: 关键词不能为空")]
190
127
 
191
128
  message_id = str(uuid.uuid4())
192
- logger.info(f"开始处理关键词: {keyword}")
129
+ print(f"开始处理关键词: {keyword}")
193
130
 
194
131
  try:
195
132
  # 记录请求
@@ -210,8 +147,7 @@ class SearchKeywordToolHandler(ToolHandler):
210
147
 
211
148
  except Exception as e:
212
149
  error_msg = f"处理关键词 '{keyword}' 时出错: {str(e)}"
213
- logger.error(error_msg)
214
- logger.error(traceback.format_exc())
150
+ print(error_msg)
215
151
 
216
152
  # 更新请求状态
217
153
  active_requests[message_id]["status"] = "error"
@@ -219,61 +155,9 @@ class SearchKeywordToolHandler(ToolHandler):
219
155
 
220
156
  return [types.TextContent(type="text", text=f"错误: {error_msg}")]
221
157
 
222
- class ProcessLinkToolHandler(ToolHandler):
223
- async def handle(self, name: str, arguments: dict | None) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
224
- link = arguments.get("link", "")
225
-
226
- if not link:
227
- return [types.TextContent(type="text", text="错误: 链接不能为空")]
228
-
229
- message_id = str(uuid.uuid4())
230
- logger.info(f"开始处理链接: {link}")
231
-
232
- try:
233
- # 记录请求
234
- active_requests[message_id] = {
235
- "link": link,
236
- "status": "processing",
237
- "timestamp": time.time()
238
- }
239
-
240
- # 调用Worker API处理链接
241
- result = await worker_instance.citzer.process_link(link)
242
- logger.info(f"链接 '{link}' 处理完成")
243
-
244
- # 更新请求状态
245
- active_requests[message_id]["status"] = "completed"
246
-
247
- # 返回结果
248
- return [types.TextContent(type="text", text=json.dumps(result, ensure_ascii=False, indent=2))]
249
-
250
- except Exception as e:
251
- error_msg = f"处理链接 '{link}' 时出错: {str(e)}"
252
- logger.error(error_msg)
253
- logger.error(traceback.format_exc())
254
-
255
- # 更新请求状态
256
- active_requests[message_id]["status"] = "error"
257
- active_requests[message_id]["error"] = str(e)
258
-
259
- return [types.TextContent(type="text", text=f"错误: {error_msg}")]
260
-
261
- class CloseBrowserToolHandler(ToolHandler):
262
- async def handle(self, name: str, arguments: dict | None) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
263
- try:
264
- await worker_instance.close()
265
- logger.info("浏览器资源已关闭")
266
- return [types.TextContent(type="text", text="浏览器资源已成功关闭")]
267
- except Exception as e:
268
- error_msg = f"关闭浏览器失败: {str(e)}"
269
- logger.error(error_msg)
270
- return [types.TextContent(type="text", text=f"错误: {error_msg}")]
271
-
272
158
  # 工具处理器映射表
273
159
  tool_handlers = {
274
- "search_keyword": SearchKeywordToolHandler(),
275
- "process_link": ProcessLinkToolHandler(),
276
- "close_browser": CloseBrowserToolHandler()
160
+ "search_keyword": SearchKeywordToolHandler()
277
161
  }
278
162
 
279
163
  @server.call_tool()
@@ -290,12 +174,12 @@ async def handle_call_tool(
290
174
  Returns:
291
175
  list: 包含文本或图像内容的响应
292
176
  """
293
- logger.info(f"收到工具调用请求: {name}, 参数: {arguments}")
177
+ print(f"收到工具调用请求: {name}, 参数: {arguments}")
294
178
 
295
179
  if name in tool_handlers:
296
180
  return await tool_handlers[name].handle(name, arguments)
297
181
  else:
298
- logger.error(f"未知工具: {name}")
182
+ print(f"未知工具: {name}")
299
183
  return [types.TextContent(type="text", text=f"错误: 未知工具: {name}")]
300
184
 
301
185
  def cleanup_expired_requests():
@@ -311,7 +195,7 @@ def cleanup_expired_requests():
311
195
  # 移除过期请求
312
196
  for msg_id in expired_ids:
313
197
  active_requests.pop(msg_id, None)
314
- logger.info(f"已清理过期请求: {msg_id}")
198
+ print(f"已清理过期请求: {msg_id}")
315
199
 
316
200
  async def handle_simple_request(reader, writer):
317
201
  """
@@ -323,11 +207,11 @@ async def handle_simple_request(reader, writer):
323
207
  # 读取请求
324
208
  request_line = await reader.readline()
325
209
  if not request_line:
326
- logger.error("收到空请求")
210
+ print("收到空请求")
327
211
  return
328
212
 
329
213
  request_data = json.loads(request_line.decode('utf-8'))
330
- logger.info(f"收到请求: {request_data}")
214
+ print(f"收到请求: {request_data}")
331
215
 
332
216
  # 处理请求
333
217
  if request_data.get("type") == "tool_call":
@@ -363,35 +247,6 @@ async def handle_simple_request(reader, writer):
363
247
  response = {"status": "error", "message": "处理器未返回结果"}
364
248
  else:
365
249
  response = {"status": "error", "message": "找不到工具处理器"}
366
- elif tool_name == "process_link":
367
- # 获取参数
368
- link = params.get("link", "")
369
-
370
- if not link:
371
- response = {"status": "error", "message": "链接不能为空"}
372
- else:
373
- # 调用处理器
374
- handler = tool_handlers.get("process_link")
375
-
376
- if handler:
377
- result_content = await handler.handle("process_link", params)
378
- if result_content and len(result_content) > 0:
379
- # 从TextContent提取JSON字符串并解析
380
- try:
381
- result_data = json.loads(result_content[0].text)
382
- response = {
383
- "status": "success",
384
- "result": result_data
385
- }
386
- except json.JSONDecodeError:
387
- response = {
388
- "status": "success",
389
- "result": {"message": result_content[0].text}
390
- }
391
- else:
392
- response = {"status": "error", "message": "处理器未返回结果"}
393
- else:
394
- response = {"status": "error", "message": "找不到工具处理器"}
395
250
  else:
396
251
  response = {"status": "error", "message": f"未知工具: {tool_name}"}
397
252
  else:
@@ -402,12 +257,11 @@ async def handle_simple_request(reader, writer):
402
257
  await writer.drain()
403
258
 
404
259
  except json.JSONDecodeError:
405
- logger.error("无法解析JSON请求")
260
+ print("无法解析JSON请求")
406
261
  writer.write(json.dumps({"status": "error", "message": "无法解析JSON请求"}).encode('utf-8') + b'\n')
407
262
  await writer.drain()
408
263
  except Exception as e:
409
- logger.error(f"处理请求时出错: {str(e)}")
410
- logger.error(traceback.format_exc())
264
+ print(f"处理请求时出错: {str(e)}")
411
265
  writer.write(json.dumps({"status": "error", "message": f"服务器错误: {str(e)}"}).encode('utf-8') + b'\n')
412
266
  await writer.drain()
413
267
  finally:
@@ -426,7 +280,7 @@ async def run_simple_server():
426
280
  )
427
281
 
428
282
  addr = server.sockets[0].getsockname()
429
- logger.info(f'简易服务器运行在 {addr}')
283
+ print(f'简易服务器运行在 {addr}')
430
284
 
431
285
  async with server:
432
286
  await server.serve_forever()
@@ -437,22 +291,21 @@ async def main():
437
291
  """
438
292
  try:
439
293
  print("正在启动CNKS服务器...")
440
- logger.info("正在启动CNKS服务器")
441
294
 
442
295
  # 检查MCP是否可用
443
296
  if not MCP_AVAILABLE:
444
- logger.warning("MCP模块不可用,使用简单服务器替代")
297
+ print("MCP模块不可用,使用简单服务器替代")
445
298
  await run_simple_server()
446
299
  return
447
300
 
448
301
  # 检查是否从命令行直接运行或被导入
449
302
  if sys.stdin.isatty():
450
303
  # 命令行运行,启动简单服务器
451
- logger.info("从命令行运行,启动简单服务器")
304
+ print("从命令行运行,启动简单服务器")
452
305
  await run_simple_server()
453
306
  else:
454
307
  # 标准输入/输出流可用,使用stdio模式运行MCP服务器
455
- logger.info("检测到标准输入/输出流,启动MCP标准服务器")
308
+ print("检测到标准输入/输出流,启动MCP标准服务器")
456
309
  async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
457
310
  await server.run(
458
311
  read_stream,
@@ -469,18 +322,15 @@ async def main():
469
322
 
470
323
  except KeyboardInterrupt:
471
324
  print("\n收到中断信号,服务器正在关闭...")
472
- logger.info("收到中断信号,服务器正在关闭")
473
325
  except Exception as e:
474
- logger.error(f"服务器启动失败: {str(e)}")
475
- logger.error(traceback.format_exc())
476
326
  print(f"服务器启动失败: {str(e)}")
477
327
  finally:
478
328
  # 关闭Worker资源
479
329
  try:
480
330
  await worker_instance.close()
481
- logger.info("Worker资源已关闭")
331
+ print("Worker资源已关闭")
482
332
  except Exception as e:
483
- logger.error(f"关闭Worker资源时出错: {str(e)}")
333
+ print(f"关闭Worker资源时出错: {str(e)}")
484
334
 
485
335
  if __name__ == "__main__":
486
336
  # 启动主循环
src/worker.py CHANGED
@@ -16,51 +16,41 @@
16
16
 
17
17
  import asyncio
18
18
  import json
19
- import logging
20
19
  import os
21
20
  import traceback
22
21
  import time
23
22
  from typing import Dict, List, Any, Optional, Union
24
23
 
25
- # 配置日志记录
26
- try:
27
- # 尝试使用绝对路径
28
- log_dir = os.path.dirname(os.path.abspath(__file__))
29
- log_file = os.path.join(os.path.dirname(log_dir), "cnks_worker.log")
24
+ # 禁用日志记录
25
+ class DummyLogger:
26
+ """空日志记录器,用于禁用日志输出"""
27
+ def __init__(self, *args, **kwargs):
28
+ pass
29
+
30
+ def debug(self, *args, **kwargs):
31
+ pass
30
32
 
31
- # 创建处理器
32
- file_handler = logging.FileHandler(log_file, mode="a")
33
- console_handler = logging.StreamHandler()
33
+ def info(self, *args, **kwargs):
34
+ pass
34
35
 
35
- # 设置格式
36
- formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
37
- file_handler.setFormatter(formatter)
38
- console_handler.setFormatter(formatter)
36
+ def warning(self, *args, **kwargs):
37
+ pass
39
38
 
40
- # 获取日志记录器并添加处理器
41
- logger = logging.getLogger("cnks.worker")
42
- logger.setLevel(logging.DEBUG)
39
+ def error(self, *args, **kwargs):
40
+ pass
43
41
 
44
- # 移除现有处理器以避免重复
45
- if logger.handlers:
46
- for handler in logger.handlers:
47
- logger.removeHandler(handler)
42
+ def critical(self, *args, **kwargs):
43
+ pass
48
44
 
49
- logger.addHandler(file_handler)
50
- logger.addHandler(console_handler)
45
+ def addHandler(self, *args, **kwargs):
46
+ pass
51
47
 
52
- # 打印确认信息
53
- print(f"Worker logger initialized, logging to: {log_file}")
54
- logger.info(f"Worker logging to: {log_file}")
55
- except Exception as e:
56
- # 回退到基本控制台日志记录
57
- logging.basicConfig(
58
- level=logging.DEBUG,
59
- format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
60
- )
61
- logger = logging.getLogger("cnks.worker")
62
- logger.error(f"Failed to set up file logging: {str(e)}")
63
- print(f"Error setting up worker file logging: {str(e)}")
48
+ def setLevel(self, *args, **kwargs):
49
+ pass
50
+
51
+ # 使用空日志记录器
52
+ logger = DummyLogger()
53
+ print = lambda *args, **kwargs: None # 禁用print函数
64
54
 
65
55
  # 导入其他模块
66
56
  try:
@@ -73,7 +63,7 @@ except ImportError:
73
63
  from citzer import Citzer
74
64
  from cache import Cache
75
65
  except ImportError:
76
- logger.warning("无法导入searcher、citzer或cache模块,功能将受限")
66
+ pass
77
67
 
78
68
  class Worker:
79
69
  """
@@ -90,7 +80,6 @@ class Worker:
90
80
  self.citzer.browser_started = False
91
81
  # 创建Cache实例
92
82
  self.cache = Cache()
93
- logger.info("Worker初始化完成")
94
83
 
95
84
  async def process_keyword(self, keyword: str) -> Dict[str, Any]:
96
85
  """
@@ -102,29 +91,20 @@ class Worker:
102
91
  Returns:
103
92
  Dict[str, Any]: 包含处理结果的字典
104
93
  """
105
- logger.info(f"处理关键词请求: {keyword}")
106
-
107
94
  try:
108
95
  # 检查缓存中是否有该关键词
109
96
  if not self.cache.has_keyword(keyword):
110
- logger.info(f"缓存中没有关键词 {keyword},执行搜索")
111
-
112
97
  # 使用searcher搜索关键词
113
98
  links = await self.searcher.search_keyword(keyword)
114
- logger.info(f"搜索到 {len(links)} 个链接")
115
99
 
116
100
  # 将结果存入缓存
117
101
  self.cache.add_links(keyword, links)
118
- logger.info(f"已将关键词 {keyword} 的链接存入缓存")
119
- else:
120
- logger.info(f"缓存中已有关键词 {keyword}")
121
102
 
122
103
  # 将Searcher的浏览器实例共享给Citzer
123
104
  if self.searcher.browser_started and not self.citzer.browser_started:
124
105
  self.citzer.context = self.searcher.context
125
106
  self.citzer.playwright = self.searcher.playwright
126
107
  self.citzer.browser_started = True
127
- logger.info("已将Searcher的浏览器实例共享给Citzer")
128
108
 
129
109
  # 处理缓存中未处理的链接
130
110
  while True:
@@ -132,26 +112,20 @@ class Worker:
132
112
  link = self.cache.get_unprocessed_link(keyword)
133
113
 
134
114
  if not link:
135
- logger.info(f"关键词 {keyword} 的所有链接已处理完毕")
136
115
  break
137
116
 
138
- logger.info(f"处理链接: {link}")
139
-
140
117
  # 使用citzer处理链接
141
118
  result = await self.citzer.process_link(link)
142
119
 
143
120
  if result:
144
121
  # 将结果存入缓存
145
122
  self.cache.add_result(link, result)
146
- logger.info(f"已将链接 {link} 的处理结果存入缓存")
147
123
 
148
124
  # 标记链接为已处理
149
125
  self.cache.mark_as_processed(link)
150
- logger.info(f"已标记链接 {link} 为已处理")
151
126
 
152
127
  # 获取所有处理结果
153
128
  results = self.cache.get_all_results(keyword)
154
- logger.info(f"关键词 {keyword} 的处理结果数量: {len(results)}")
155
129
 
156
130
  return {
157
131
  "success": True,
@@ -160,9 +134,6 @@ class Worker:
160
134
  }
161
135
 
162
136
  except Exception as e:
163
- logger.error(f"处理关键词 {keyword} 时出错: {str(e)}")
164
- logger.error(traceback.format_exc())
165
-
166
137
  return {
167
138
  "success": False,
168
139
  "keyword": keyword,
@@ -176,8 +147,8 @@ class Worker:
176
147
  self.citzer.browser_started = False
177
148
  # 关闭Searcher的浏览器
178
149
  await self.searcher.close_browser()
179
- except Exception as e:
180
- logger.warning(f"关闭浏览器时出错: {str(e)}")
150
+ except Exception:
151
+ pass
181
152
 
182
153
  async def close(self):
183
154
  """关闭工作者资源"""
@@ -189,10 +160,8 @@ class Worker:
189
160
 
190
161
  # 关闭Searcher的浏览器
191
162
  await self.searcher.close_browser()
192
- logger.info("已关闭工作者资源")
193
- except Exception as e:
194
- logger.error(f"关闭工作者资源时出错: {str(e)}")
195
- logger.error(traceback.format_exc())
163
+ except Exception:
164
+ pass
196
165
 
197
166
  # 如果作为主程序运行,提供测试功能
198
167
  async def main():
@@ -202,14 +171,9 @@ async def main():
202
171
  try:
203
172
  # 测试关键词
204
173
  test_keyword = "人工智能"
205
- print(f"测试处理关键词: {test_keyword}")
206
174
 
207
175
  # 处理关键词
208
176
  result = await worker.process_keyword(test_keyword)
209
-
210
- # 打印结果
211
- print(f"处理结果:")
212
- print(json.dumps(result, ensure_ascii=False, indent=2))
213
177
  finally:
214
178
  # 关闭资源
215
179
  await worker.close()
File without changes