LightAgent 0.2.85__tar.gz → 0.2.95__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.
@@ -1,6 +1,5 @@
1
1
  import re
2
- import traceback
3
- from typing import List, Dict, Any, Callable, Union, Iterable, Optional, Generator
2
+ from typing import List, Dict, Any, Callable, Union, Iterable, Optional, Generator, AsyncGenerator
4
3
  from copy import deepcopy
5
4
  import importlib.util
6
5
  import random
@@ -12,14 +11,24 @@ import os
12
11
  import httpx
13
12
  import importlib
14
13
  from openai.types.chat import ChatCompletionChunk
14
+ import inspect
15
+ import traceback
16
+ from mcp import ClientSession, StdioServerParameters, types
17
+ from contextlib import AsyncExitStack
18
+
19
+ from mcp.client.sse import sse_client
20
+ from mcp.client.stdio import stdio_client
21
+ import asyncio
22
+ from functools import partial
23
+
15
24
 
16
25
  # 全局工具注册表
17
26
  _FUNCTION_MAPPINGS = {} # 工具名称 -> 工具函数
18
- _FUNCTION_INFO = {} # 工具名称 -> 工具info信息
27
+ _FUNCTION_INFO = {} # 工具名称 -> 工具info信息
19
28
  _OPENAI_FUNCTION_SCHEMAS = [] # OpenAI 格式的工具描述
20
29
  _PROMPT_FUNCTION_SCHEMAS = [] # prompt 格式的工具描述
21
30
 
22
- __version__ = "0.2.85" # 你可以根据需要设置版本号
31
+ __version__ = "0.3.0" # 你可以根据需要设置版本号
23
32
 
24
33
 
25
34
  def register_tool_manually(tools: List[Union[str, Callable]]) -> bool:
@@ -88,35 +97,44 @@ def load_tool(tool_name: str, tools_directory: str = "tools"):
88
97
  raise AttributeError(f"Tool '{tool_name}' is not properly defined in {tool_path}")
89
98
 
90
99
 
91
- def dispatch_tool(tool_name: str, tool_params: Dict[str, Any]) -> Union[str, Generator[str, None, None]]:
100
+ async def dispatch_tool(tool_name: str, tool_params: Dict[str, Any]) -> Union[
101
+ str, Generator[str, None, None], AsyncGenerator[str, None]]:
92
102
  """
93
- 调用工具执行,支持流式输出。
94
-
95
- :param tool_name: 工具名称
96
- :param tool_params: 工具参数
97
- :return: 如果工具是流式输出,返回生成器;否则返回字符串结果。
103
+ 调用工具执行,支持同步/异步工具及流式输出。
98
104
  """
99
105
  if tool_name not in _FUNCTION_MAPPINGS:
100
106
  return f"Tool `{tool_name}` not found."
101
107
 
102
108
  tool_call = _FUNCTION_MAPPINGS[tool_name]
103
109
  try:
104
- # print(f"Calling tool: {tool_name} with params: {tool_params}") # 调试信息
105
- result = tool_call(**tool_params)
110
+ # 处理不同类型的流式输出
111
+ # 区分同步/异步工具
112
+ if inspect.iscoroutinefunction(tool_call):
113
+ # result = await tool_call(**tool_params)
114
+ # 将参数以字典形式传递给包装器
115
+ result = await tool_call(**tool_params) if inspect.iscoroutinefunction(tool_call) else tool_call(
116
+ **tool_params)
117
+ else:
118
+ result = tool_call(**tool_params)
106
119
 
107
- # 如果工具返回的是可迭代对象(如生成器),则逐块处理并返回生成器
108
- if isinstance(result, Iterable) and not isinstance(result, (str, bytes)):
120
+ # 处理不同类型的流式输出
121
+ if inspect.isasyncgen(result):
122
+ return async_stream_generator(result)
123
+ elif inspect.isgenerator(result):
109
124
  return stream_generator(result)
110
- # 否则,返回字符串结果
111
125
  else:
112
126
  return str(result)
113
127
  except Exception as e:
114
- # print(f"Tool call failed: {e}") # 调试信息
115
128
  return traceback.format_exc()
116
129
 
117
130
 
118
- def stream_generator(result):
119
- for chunk in result:
131
+ async def async_stream_generator(async_gen: AsyncGenerator) -> AsyncGenerator[str, None]:
132
+ async for chunk in async_gen:
133
+ yield chunk
134
+
135
+
136
+ def stream_generator(sync_gen: Generator) -> Generator[str, None, None]:
137
+ for chunk in sync_gen:
120
138
  yield chunk
121
139
 
122
140
 
@@ -154,26 +172,223 @@ def get_tools_str() -> str:
154
172
  return tools_str
155
173
 
156
174
 
175
+ class MCPClientManager:
176
+ """增强版MCP客户端管理器"""
177
+
178
+ def __init__(self, config: dict):
179
+ self.config = config
180
+ self.session: Optional[ClientSession] = None
181
+ self.exit_stack = AsyncExitStack()
182
+ self._streams_context = None
183
+ self._session_context = None
184
+ self.server_sessions = {} # 存储不同服务器的会话
185
+
186
+ async def _call_tool_wrapper(self, tool_name: str, target_server: str, **kwargs):
187
+ """参数转换适配器"""
188
+ return await self.call_tool(
189
+ tool_name=tool_name,
190
+ arguments=kwargs,
191
+ target_server=target_server
192
+ )
193
+
194
+ async def _create_session(self, server_name: str, config: dict):
195
+ """创建并管理会话上下文"""
196
+ if 'url' in config:
197
+ # SSE 服务器连接
198
+ self._streams_context = sse_client(
199
+ url=config['url'],
200
+ headers=config.get('headers', {})
201
+ )
202
+ streams = await self.exit_stack.enter_async_context(self._streams_context)
203
+ self._session_context = ClientSession(*streams)
204
+ self.session = await self.exit_stack.enter_async_context(self._session_context)
205
+ else:
206
+ # 标准输入输出服务器连接
207
+ server_params = StdioServerParameters(
208
+ command=config["command"],
209
+ args=config["args"],
210
+ env=config.get("env")
211
+ )
212
+ transport = await self.exit_stack.enter_async_context(stdio_client(server_params))
213
+ stdio, write = transport
214
+ self._session_context = ClientSession(stdio, write)
215
+ self.session = await self.exit_stack.enter_async_context(self._session_context)
216
+
217
+ await self.session.initialize()
218
+ self.server_sessions[server_name] = self.session
219
+
220
+ async def cleanup(self):
221
+ """清理所有会话资源"""
222
+ await self.exit_stack.__aexit__(None, None, None)
223
+ self.server_sessions.clear()
224
+
225
+ async def register_mcp_tool(self) -> bool:
226
+ """
227
+ 自动注册所有MCP服务的工具到全局字典
228
+ :param config: MCP服务配置(与call_tool使用的相同配置)
229
+ :return: 是否至少成功注册一个工具
230
+ """
231
+ registered_count = 0
232
+ enabled_servers = [
233
+ (name, config)
234
+ for name, config in self.config["mcpServers"].items()
235
+ if not config["disabled"]
236
+ ]
237
+
238
+ for server_name, config in enabled_servers:
239
+ try:
240
+ # 创建会话连接
241
+ # print(server_name,config)
242
+ await self._create_session(server_name, config)
243
+
244
+ # 获取工具列表
245
+ tools_response = await self.session.list_tools()
246
+ print(f"🔍 Registering tools for server : {server_name} ...")
247
+
248
+ # 注册工具处理逻辑
249
+ for tool in tools_response.tools:
250
+ try:
251
+ # 构建工具元数据
252
+ tool_info = {
253
+ "tool_name": tool.name,
254
+ "tool_description": tool.description,
255
+ "tool_params": []
256
+ }
257
+
258
+ # 解析参数模式
259
+ properties = tool.inputSchema.get("properties", {})
260
+ required_fields = tool.inputSchema.get("required", [])
261
+
262
+ for param_name, param_schema in properties.items():
263
+ tool_info["tool_params"].append({
264
+ "name": param_name,
265
+ "type": param_schema.get("type", "string"),
266
+ "description": param_schema.get("title", ""),
267
+ "required": param_name in required_fields
268
+ })
269
+
270
+ # 注册到全局字典
271
+ _FUNCTION_INFO[tool.name] = tool_info
272
+ _FUNCTION_MAPPINGS[tool.name] = partial(
273
+ self._call_tool_wrapper,
274
+ tool_name=tool.name,
275
+ target_server=server_name
276
+ )
277
+
278
+ # 构建OpenAI格式
279
+ openai_schema = {
280
+ "type": "function",
281
+ "function": {
282
+ "name": tool.name,
283
+ "description": tool.description,
284
+ "parameters": {
285
+ "type": "object",
286
+ "properties": {
287
+ k: {"type": v["type"], "description": v.get("title", "")}
288
+ for k, v in properties.items()
289
+ },
290
+ "required": required_fields
291
+ }
292
+ }
293
+ }
294
+ _OPENAI_FUNCTION_SCHEMAS.append(openai_schema)
295
+
296
+ registered_count += 1
297
+ print(f"✅ The registered tool : {tool.name}")
298
+
299
+ except Exception as e:
300
+ print(f"⚠️ 工具 {tool.name} 注册失败: {str(e)}")
301
+ continue
302
+ print(f"🟢 {registered_count} tools have been registered")
303
+
304
+ except Exception as e:
305
+ print(f"🔴 服务器 {server_name} 连接失败: {str(e)}")
306
+ continue
307
+ # 清理
308
+ await self.cleanup()
309
+ return registered_count > 0
310
+
311
+ async def call_tool(self, tool_name: str, arguments: dict, target_server: str = None):
312
+ """
313
+ 通用工具调用方法
314
+ :param tool_name: 要调用的工具名称
315
+ :param arguments: 工具参数字典
316
+ :param target_server: 指定服务器名称(可选)
317
+ :return: 工具调用结果
318
+ """
319
+ enabled_servers = [
320
+ (name, config)
321
+ for name, config in self.config["mcpServers"].items()
322
+ if not config["disabled"]
323
+ ]
324
+
325
+ if target_server:
326
+ enabled_servers = [s for s in enabled_servers if s[0] == target_server]
327
+
328
+ for server_name, config in enabled_servers:
329
+ try:
330
+ # 复用已建立的会话
331
+ session = self.server_sessions.get(server_name)
332
+ # print(111,server_name,session)
333
+ # print(222,server_name,config)
334
+ if not session:
335
+ await self._create_session(server_name, config)
336
+ session = self.session
337
+
338
+ # 获取工具列表
339
+ tools = await session.list_tools()
340
+ available_tools = {t.name: t for t in tools.tools}
341
+
342
+ if tool_name in available_tools:
343
+ # 验证参数类型
344
+ schema = available_tools[tool_name].inputSchema
345
+ self._validate_arguments(arguments, schema)
346
+
347
+ # 执行调用
348
+ result = await session.call_tool(tool_name, arguments)
349
+ # print(f"mcp工具运行结果: {result.content[0].text}")
350
+ # 调用完成清理session
351
+ await self.cleanup()
352
+ return {
353
+ "server": server_name,
354
+ "tool": tool_name,
355
+ "result": result.content[0].text
356
+ }
357
+
358
+ except Exception as e:
359
+ print(f"调用服务器 {server_name} 失败: {str(e)}")
360
+ continue
361
+
362
+ raise ValueError(f"工具 {tool_name} 在可用服务器中未找到")
363
+
364
+ def _validate_arguments(self, arguments: dict, schema: dict):
365
+ """简单参数校验(可选)"""
366
+ required_fields = schema.get("required", [])
367
+ for field in required_fields:
368
+ if field not in arguments:
369
+ raise ValueError(f"缺少必要参数: {field}")
370
+
371
+
157
372
  class LightAgent:
158
- __version__ = "0.2.85" # 将版本号放在类中
373
+ __version__ = "0.3.0" # 将版本号放在类中
159
374
 
160
375
  def __init__(
161
376
  self,
162
377
  *,
163
378
  name: Optional[str] = None, # 代理名称
164
379
  instructions: Optional[str] = None, # 代理指令
165
- role: Optional[str] = None,
166
- model: str,
167
- api_key: str | None = None,
168
- base_url: str | httpx.URL | None = None,
169
- websocket_base_url: str | httpx.URL | None = None,
380
+ role: Optional[str] = None, # 代理角色
381
+ model: str, # agent模型名称
382
+ api_key: str | None = None, # 模型 api key
383
+ base_url: str | httpx.URL | None = None, # 模型 base url
384
+ websocket_base_url: str | httpx.URL | None = None, # 模型 websocket base url
170
385
  memory=None, # 支持外部传入记忆模块
171
386
  tree_of_thought: bool = False, # 是否启用链式思考
172
- tot_model: str | None = None,
173
- tot_api_key: str | None = None,
174
- tot_base_url: str | httpx.URL | None = None,
387
+ tot_model: str | None = None, # 链式思考模型
388
+ tot_api_key: str | None = None, # 链式思考模型API密钥
389
+ tot_base_url: str | httpx.URL | None = None, # 链式思考模型base_url
175
390
  self_learning: bool = False, # 是否启用agent自我学习
176
- tools: List[Union[str, Callable]] = None, # 支持混合输入
391
+ tools: List[Union[str, Callable]] = None, # 支持工具混合输入
177
392
  debug: bool = False, # 是否启用调试模式
178
393
  log_level: str = "INFO", # 日志级别(INFO, DEBUG, ERROR)
179
394
  log_file: Optional[str] = None # 日志文件路径
@@ -198,6 +413,8 @@ class LightAgent:
198
413
  :param log_level: 日志级别(INFO, DEBUG, ERROR)。
199
414
  :param log_file: 日志文件路径。
200
415
  """
416
+ self.mcp_setting = None
417
+ self.mcp_client = None
201
418
  if not model:
202
419
  model = "gpt-4o-mini" # 默认模型
203
420
  if not api_key:
@@ -206,7 +423,7 @@ class LightAgent:
206
423
  base_url = os.environ.get("OPENAI_BASE_URL")
207
424
  self.loaded_tools = {} # 用于存储已加载的工具函数
208
425
  if not name:
209
- random_suffix = random.randint(10000000, 99999999) # 生成一个8位随机数
426
+ random_suffix = random.randint(10000000, 99999999) # 生成一个8位随机数作为agent编号
210
427
  name = f"LightAgent{random_suffix}"
211
428
  self.name = name
212
429
  if not instructions:
@@ -236,6 +453,8 @@ class LightAgent:
236
453
  # 初始化工具列表
237
454
  self.load_tools(tools)
238
455
  # register_tool_manually(tools)
456
+
457
+
239
458
  if api_key is None:
240
459
  raise ValueError(
241
460
  "The api_key client option must be set either by passing api_key to the client or by setting the OPENAI_API_KEY environment variable"
@@ -247,8 +466,8 @@ class LightAgent:
247
466
  base_url = f"https://api.openai.com/v1"
248
467
 
249
468
  self.client = OpenAI(
250
- base_url = base_url,
251
- api_key = self.api_key
469
+ base_url=base_url,
470
+ api_key=self.api_key
252
471
  )
253
472
  if self.tree_of_thought:
254
473
  if tot_api_key is None:
@@ -259,8 +478,8 @@ class LightAgent:
259
478
  tot_model = "deepseek-r1" # 默认思维推理模型为deepseek-r1
260
479
  self.tot_model = tot_model
261
480
  self.tot_client = OpenAI(
262
- base_url = tot_base_url,
263
- api_key = tot_api_key
481
+ base_url=tot_base_url,
482
+ api_key=tot_api_key
264
483
  )
265
484
 
266
485
  def get_tool(self, tool_name: str) -> Callable:
@@ -278,7 +497,6 @@ class LightAgent:
278
497
  用于外部可以获取已加载的工具函数列表
279
498
  :return: 工具函数
280
499
  """
281
-
282
500
  return list(_FUNCTION_MAPPINGS.keys())
283
501
 
284
502
  def load_tools(self, tool_names: List[Union[str, Callable]], tools_directory: str = "tools"):
@@ -377,6 +595,18 @@ class LightAgent:
377
595
  elif level == "ERROR":
378
596
  self.logger.error(log_message)
379
597
 
598
+ async def setup_mcp(
599
+ self,
600
+ mcp_setting: dict | None = None, # mcp 设置
601
+ ):
602
+ if mcp_setting:
603
+ self.mcp_setting = mcp_setting
604
+ """单独初始化 MCP 模块"""
605
+ if self.mcp_setting and not self.mcp_client:
606
+ self.mcp_client = MCPClientManager(self.mcp_setting)
607
+ await self.mcp_client.register_mcp_tool()
608
+ self.log("INFO", "setup_mcp", "MCP 模块初始化成功")
609
+
380
610
  def run(
381
611
  self,
382
612
  query: str,
@@ -489,18 +719,18 @@ class LightAgent:
489
719
  fixed_args = function_call.arguments.replace('\\"', '"').replace('\\\\', '\\')
490
720
  self.log("DEBUG", "non_stream function_call", {"function_call": fixed_args})
491
721
 
492
- function_args = json.loads(fixed_args)
493
722
  # 解析函数参数
494
- # function_args = json.loads(function_call.arguments)
723
+ function_args = json.loads(fixed_args)
495
724
 
496
725
  # 调用工具并获取响应
497
- tool_response = dispatch_tool(function_call.name, function_args)
726
+ tool_response = asyncio.run(dispatch_tool(function_call.name, function_args))
498
727
  function_call_name = function_call.name
499
728
  combined_response = ""
729
+ single_tool_response = ""
500
730
 
501
731
  # 如果工具返回的是生成器(流式输出),则将所有 chunk 叠加
502
732
  if isinstance(tool_response, Generator):
503
- print(f"Streaming response from tool: {function_call.name}")
733
+ # print(f"Streaming response from tool: {function_call.name}")
504
734
  for chunk in tool_response:
505
735
  # print("Received chunk:", chunk) # 打印每个 chunk
506
736
  if function_call_name == 'finish':
@@ -520,8 +750,8 @@ class LightAgent:
520
750
  # 将 JSON 对象中的 Unicode 编码转换为中文字符
521
751
  if isinstance(combined_response, dict):
522
752
  combined_response = json.dumps(combined_response, ensure_ascii=False) # 确保中文字符不转义
753
+ single_tool_response = combined_response # 处理单个工具的方法
523
754
 
524
- tool_responses.append(combined_response) # 将叠加后的完整响应添加到列表
525
755
  else:
526
756
  # print(f"Non-streaming response from tool: {function_call.name}")
527
757
  combined_response = tool_response
@@ -534,22 +764,23 @@ class LightAgent:
534
764
  except json.JSONDecodeError:
535
765
  combined_response = tool_response
536
766
  pass # 如果不是 JSON 字符串,保持原样
537
- tool_responses.append(combined_response) # 直接添加普通响应
767
+ single_tool_response = combined_response # 处理单个工具的方法
538
768
 
539
- self.log("INFO", "non_stream tool_response", {"tool_response": combined_response})
769
+ self.log("INFO", "non_stream single_tool_response", {"single_tool_response": single_tool_response})
540
770
 
541
- # 将工具调用的结果添加到列表中
542
- tool_responses.append(combined_response)
771
+ # 将单个工具的响应结果添加到列表中
772
+ tool_responses.append(single_tool_response)
543
773
 
544
774
  # 将所有工具调用的结果合并为一个字符串
775
+ self.log("DEBUG", "non_stream tool_responses", {"tool_responses": tool_responses})
776
+
545
777
  combined_tool_response = "\n".join(tool_responses)
546
778
 
547
779
  # 将工具调用和响应添加到消息列表中
548
780
  params["messages"].append(
549
781
  {
550
782
  "role": "assistant",
551
- "content": json.dumps(
552
- [tool_call.function.model_dump() for tool_call in tool_calls]),
783
+ "content": f"使用工具: \n {json.dumps([tool_call.function.model_dump() for tool_call in tool_calls], ensure_ascii=False)}\n",
553
784
  }
554
785
  )
555
786
  params["messages"].append(
@@ -561,14 +792,14 @@ class LightAgent:
561
792
  else:
562
793
  # 返回最终回复
563
794
  reply = response.choices[0].message.content
564
- self.log("INFO", "final_reply", {"reply": reply})
795
+ self.log("INFO", "non_stream final_reply", {"reply": reply})
565
796
  return reply
566
797
 
567
798
  # 更新响应
568
799
  if function_call_name == 'finish':
569
800
  return # 如果最后调用了finish工具,则结束生成器
570
801
  # print("params:",params)
571
- self.log("DEBUG", "chat-completions params", {"params": params})
802
+ self.log("DEBUG", "non_stream chat-completions params", {"params": params})
572
803
 
573
804
  try:
574
805
  response = self.client.chat.completions.create(**params)
@@ -631,7 +862,7 @@ class LightAgent:
631
862
  chunk.choices[0].finish_reason == "stop" and any(
632
863
  tool_call["name"] for tool_call in tool_calls)):
633
864
  # 遍历所有工具调用
634
- self.log("DEBUG", "tool_calls", {"tool_calls": tool_calls})
865
+ self.log("DEBUG", "stream tool_calls", {"tool_calls": tool_calls})
635
866
  for tool_call in tool_calls:
636
867
  if tool_call["name"]: # 确保工具调用有名称
637
868
  function_call = {
@@ -639,7 +870,7 @@ class LightAgent:
639
870
  "title": _FUNCTION_INFO.get(tool_call["name"], {}).get('tool_title') or '',
640
871
  "arguments": tool_call["arguments"],
641
872
  }
642
- self.log("INFO", "tool_call", {"function_call": function_call})
873
+ self.log("INFO", "stream function_call", {"function_call": function_call})
643
874
  # 将工具的调用信息推送给开发者
644
875
  yield function_call
645
876
 
@@ -655,14 +886,20 @@ class LightAgent:
655
886
  # tool_responses.append(tool_response)
656
887
 
657
888
  for json_obj in json_objects:
658
- function_args = json.loads(json_obj)
659
- tool_response = dispatch_tool(function_call["name"], function_args)
889
+ # 尝试自动修复常见转义问题
890
+ fixed_args = json_obj.replace('\\"', '"').replace('\\\\', '\\')
891
+ self.log("DEBUG", "stream fixed_args", {"fixed_args": fixed_args})
892
+
893
+ function_args = json.loads(fixed_args)
894
+ # tool_response = dispatch_tool(function_call["name"], function_args)
895
+ tool_response = asyncio.run(dispatch_tool(function_call["name"], function_args))
660
896
  function_call_name = function_call["name"]
897
+ combined_response = ""
898
+ single_tool_response = ""
661
899
 
662
900
  # 如果工具返回的是生成器(流式输出),则将所有 chunk 叠加
663
901
  if isinstance(tool_response, Generator):
664
902
  # print(f"Streaming response from tool: {function_call['name']}")
665
- combined_response = ""
666
903
  for chunk in tool_response:
667
904
  # 将工具返回的数据继续流出
668
905
  if isinstance(chunk, ChatCompletionChunk):
@@ -674,7 +911,7 @@ class LightAgent:
674
911
  'tool_title') or '',
675
912
  "output": chunk,
676
913
  }
677
- self.log("INFO", "tool_call", {"tool_output": tool_output})
914
+ self.log("DEBUG", "stream tool_output", {"tool_output": tool_output})
678
915
  yield tool_output
679
916
  # 将工具的调用信息推送给开发者
680
917
  if function_call_name == 'finish':
@@ -682,13 +919,15 @@ class LightAgent:
682
919
  combined_response += content # 将每个 chunk 叠加
683
920
  else:
684
921
  combined_response += chunk # 将每个 chunk 叠加
685
- tool_responses.append(combined_response) # 将叠加后的完整响应添加到列表
922
+ single_tool_response = combined_response # 处理单个工具的方法
686
923
  else:
687
924
  # print(f"Non-streaming response from tool: {tool_response}")
688
925
  combined_response = tool_response
689
- tool_responses.append(combined_response) # 直接添加普通响应
690
- self.log("INFO", "stream tool_response", {"tool_response": combined_response})
691
-
926
+ single_tool_response = combined_response # 处理单个工具的方法
927
+ self.log("INFO", "stream single_tool_response",
928
+ {"single_tool_response": single_tool_response})
929
+ # 将单个工具的响应结果添加到列表中
930
+ tool_responses.append(single_tool_response)
692
931
 
693
932
  except json.JSONDecodeError as e:
694
933
  self.log("ERROR", "json_decode_error",
@@ -697,15 +936,15 @@ class LightAgent:
697
936
 
698
937
  # 将所有工具调用的结果合并为一个字符串
699
938
  combined_tool_response = "\n".join(tool_responses)
939
+ tool_str = json.dumps(
940
+ [{"name": tool_call["name"], "arguments": tool_call["arguments"]} for tool_call in tool_calls],
941
+ ensure_ascii=False)
700
942
 
701
943
  # 将工具调用和响应添加到消息列表中
702
944
  params["messages"].append(
703
945
  {
704
946
  "role": "assistant",
705
- "content": json.dumps(
706
- [{"name": tool_call["name"], "arguments": tool_call["arguments"]} for tool_call in
707
- tool_calls]
708
- ),
947
+ "content": f"使用工具: \n {tool_str}\n"
709
948
  }
710
949
  )
711
950
  params["messages"].append(
@@ -720,7 +959,7 @@ class LightAgent:
720
959
  # 更新响应
721
960
  if function_call_name == 'finish':
722
961
  return # 如果最后调用了finish工具,则结束生成器
723
- self.log("DEBUG", "chat-completions params", {"params": params})
962
+ self.log("DEBUG", "stream chat-completions params", {"params": params})
724
963
  response = self.client.chat.completions.create(**params)
725
964
 
726
965
  # 重试次数用尽
@@ -1024,6 +1263,7 @@ class LightAgent:
1024
1263
  # 在函数内部定义工具信息
1025
1264
  get_weather.tool_info = {
1026
1265
  "tool_name": "get_weather",
1266
+ "tool_title": "天气查询",
1027
1267
  "tool_description": "获取指定城市的当前天气信息",
1028
1268
  "tool_params": [
1029
1269
  {"name": "city_name", "description": "要查询的城市名称", "type": "string", "required": True},
@@ -1065,7 +1305,7 @@ get_weather.tool_info = {
1065
1305
  f.write(tool_code)
1066
1306
  self.log("INFO", "tool_created", {"tool_name": tool_name, "tool_path": tool_path})
1067
1307
 
1068
- # 加载新创建的工具
1308
+ # 自动加载新创建的工具
1069
1309
  self.load_tools([tool_name], tools_directory)
1070
1310
  except Exception as e:
1071
1311
  self.log("ERROR", "tool_creation_failed", {"error": str(e)})
@@ -1106,4 +1346,4 @@ class LightSwarm:
1106
1346
  if __name__ == "__main__":
1107
1347
  # Example of registering and using a tool
1108
1348
  print("This is LightAgent")
1109
- # print(dispatch_tool("example_tool", {"param1": "test"}))
1349
+ # print(dispatch_tool("example_tool", {"param1": "test"}))
@@ -1,20 +1,24 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: LightAgent
3
- Version: 0.2.85
3
+ Version: 0.2.95
4
4
  Summary: **LightAgent** is an extremely lightweight active Agentic Framework with memory (`mem0`), tools (`Tools`), and a Tree of Thought (`ToT`). It supports swarm-like multi-agent collaboration, automated tool generation, and agent assessment, with underlying model support for OpenAI, Zhipu ChatGLM, Baichuan Large Model, DeepSeek R1, Qwen series large models, and more. At the same time, LightAgent supports OpenAI streaming format API service output, seamlessly integrating with major mainstream chat frameworks. 🌟
5
5
  Home-page: https://github.com/wxai-space/LightAgent
6
6
  License: Apache 2.0
7
7
  Author: caiweige
8
8
  Author-email: caiweige@qq.com
9
- Requires-Python: >=3.10,<3.12
9
+ Requires-Python: >=3.10,<3.13
10
10
  Classifier: License :: Other/Proprietary License
11
11
  Classifier: Programming Language :: Python :: 3
12
12
  Classifier: Programming Language :: Python :: 3.10
13
13
  Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
14
15
  Requires-Dist: colorama (==0.4.6)
15
16
  Requires-Dist: httpx (==0.28.1)
17
+ Requires-Dist: httpx-sse (==0.4.0)
16
18
  Requires-Dist: loguru (==0.7.3)
19
+ Requires-Dist: mcp (==1.5.0)
17
20
  Requires-Dist: openai (==1.59.3)
21
+ Requires-Dist: pydantic-settings (==2.8.1)
18
22
  Requires-Dist: requests (==2.32.3)
19
23
  Project-URL: Repository, https://github.com/wxai-space/LightAgent
20
24
  Description-Content-Type: text/markdown
@@ -58,6 +62,8 @@ Description-Content-Type: text/markdown
58
62
 
59
63
  ---
60
64
 
65
+ ![lightswarm_demo_en.png](docs%2Fimages%2Flightswarm_demo_en.png)
66
+
61
67
  ## ✨ Features
62
68
 
63
69
  - **Lightweight and Efficient** 🚀: Minimalist design, quick deployment, suitable for various application scenarios. (No LangChain, No LlamaIndex) 100% Python implementation, no additional dependencies, core code is only 1000 lines, fully open source.
@@ -74,7 +80,8 @@ Description-Content-Type: text/markdown
74
80
 
75
81
  ---
76
82
  ## News
77
- - <img src="https://img.alicdn.com/imgextra/i3/O1CN01SFL0Gu26nrQBFKXFR_!!6000000007707-2-tps-500-500.png" alt="new" width="30" height="30"/>**[2025-02-19]** LightAgent v0.2.7 supports deepseek-r1 model for tot now.Significantly enhances the multi-tool planning capability for complex tasks.
83
+ - <img src="https://img.alicdn.com/imgextra/i3/O1CN01SFL0Gu26nrQBFKXFR_!!6000000007707-2-tps-500-500.png" alt="new" width="30" height="30"/>**[2025-02-19]** LightAgent v0.3.0 fully supports the MCP protocol, enabling collaborative work with multiple models and tools to achieve more efficient handling of complex tasks.<a href="mcp_release.zh-CN.md">View MCP release introduction.>></a>
84
+ - **[2025-02-19]** LightAgent v0.2.7 supports deepseek-r1 model for tot now.Significantly enhances the multi-tool planning capability for complex tasks.
78
85
  - **[2025-02-06]** LightAgent version 0.2.5 is released now.
79
86
  - **[2025-01-20]** LightAgent version 0.2.0 is released now.
80
87
  - **[2025-01-05]** LightAgent version 0.1.0 is released now.
@@ -776,7 +783,7 @@ We look forward to your feedback and work together to make LightAgent even stron
776
783
  **LightAgent** 是一个极其轻量的带记忆(`mem0`)、工具(`Tools`)、思维树(`ToT`)的主动式 Agentic Framework(自主性框架)。它支持比Openai Swarm更简单的多智能体协同,构建具备自我学习能力的agent,并支持Agent测评,底层模型支持 OpenAI、智谱 ChatGLM、DeepSeek、阶跃星辰、Qwen通义千问大模型等。同时,LightAgent 支持 OpenAI 流格式 API 服务输出,无缝接入各大主流 Chat 框架。🌟
777
784
 
778
785
  ---
779
-
786
+ ![lightswarm_demo_cn.png](docs%2Fimages%2Flightswarm_demo_cn.png)
780
787
  ## ✨ 特性
781
788
 
782
789
  - **轻量高效** 🚀:极简设计,快速部署,适合各种规模的应用场景。(No LangChain, No LlamaIndex)100% Python 实现,无需额外依赖,核心代码仅1000行,完全开源。
@@ -793,7 +800,8 @@ We look forward to your feedback and work together to make LightAgent even stron
793
800
 
794
801
  ---
795
802
  ## 新闻
796
- - <img src="https://img.alicdn.com/imgextra/i3/O1CN01SFL0Gu26nrQBFKXFR_!!6000000007707-2-tps-500-500.png" alt="new" width="30" height="30"/>**[2025-02-19]** LightAgent v0.2.7 支持单独采用 deepseek-r1 作为的agent推理规划ToT引擎,大幅度提升复杂任务的多工具Plan能力.
803
+ - <img src="https://img.alicdn.com/imgextra/i3/O1CN01SFL0Gu26nrQBFKXFR_!!6000000007707-2-tps-500-500.png" alt="new" width="30" height="30"/>**[2025-02-19]** LightAgent v0.3.0 全面支持MCP协议,支持多模型多工具的协同工作,实现更高效的复杂任务处理。<a href="mcp_release.zh-CN.md">查看MCP发布简介>></a>
804
+ - **[2025-02-19]** LightAgent v0.2.7 支持单独采用 deepseek-r1 作为的agent推理规划ToT引擎,大幅度提升复杂任务的多工具Plan能力.
797
805
  - **[2025-02-06]** LightAgent version 0.2.5 is released now.
798
806
  - **[2025-01-20]** LightAgent version 0.2.0 is released now.
799
807
  - **[2025-01-05]** LightAgent version 0.1.0 is released now.
@@ -37,6 +37,8 @@
37
37
 
38
38
  ---
39
39
 
40
+ ![lightswarm_demo_en.png](docs%2Fimages%2Flightswarm_demo_en.png)
41
+
40
42
  ## ✨ Features
41
43
 
42
44
  - **Lightweight and Efficient** 🚀: Minimalist design, quick deployment, suitable for various application scenarios. (No LangChain, No LlamaIndex) 100% Python implementation, no additional dependencies, core code is only 1000 lines, fully open source.
@@ -53,7 +55,8 @@
53
55
 
54
56
  ---
55
57
  ## News
56
- - <img src="https://img.alicdn.com/imgextra/i3/O1CN01SFL0Gu26nrQBFKXFR_!!6000000007707-2-tps-500-500.png" alt="new" width="30" height="30"/>**[2025-02-19]** LightAgent v0.2.7 supports deepseek-r1 model for tot now.Significantly enhances the multi-tool planning capability for complex tasks.
58
+ - <img src="https://img.alicdn.com/imgextra/i3/O1CN01SFL0Gu26nrQBFKXFR_!!6000000007707-2-tps-500-500.png" alt="new" width="30" height="30"/>**[2025-02-19]** LightAgent v0.3.0 fully supports the MCP protocol, enabling collaborative work with multiple models and tools to achieve more efficient handling of complex tasks.<a href="mcp_release.zh-CN.md">View MCP release introduction.>></a>
59
+ - **[2025-02-19]** LightAgent v0.2.7 supports deepseek-r1 model for tot now.Significantly enhances the multi-tool planning capability for complex tasks.
57
60
  - **[2025-02-06]** LightAgent version 0.2.5 is released now.
58
61
  - **[2025-01-20]** LightAgent version 0.2.0 is released now.
59
62
  - **[2025-01-05]** LightAgent version 0.1.0 is released now.
@@ -36,7 +36,7 @@
36
36
  **LightAgent** 是一个极其轻量的带记忆(`mem0`)、工具(`Tools`)、思维树(`ToT`)的主动式 Agentic Framework(自主性框架)。它支持比Openai Swarm更简单的多智能体协同,构建具备自我学习能力的agent,并支持Agent测评,底层模型支持 OpenAI、智谱 ChatGLM、DeepSeek、阶跃星辰、Qwen通义千问大模型等。同时,LightAgent 支持 OpenAI 流格式 API 服务输出,无缝接入各大主流 Chat 框架。🌟
37
37
 
38
38
  ---
39
-
39
+ ![lightswarm_demo_cn.png](docs%2Fimages%2Flightswarm_demo_cn.png)
40
40
  ## ✨ 特性
41
41
 
42
42
  - **轻量高效** 🚀:极简设计,快速部署,适合各种规模的应用场景。(No LangChain, No LlamaIndex)100% Python 实现,无需额外依赖,核心代码仅1000行,完全开源。
@@ -53,7 +53,8 @@
53
53
 
54
54
  ---
55
55
  ## 新闻
56
- - <img src="https://img.alicdn.com/imgextra/i3/O1CN01SFL0Gu26nrQBFKXFR_!!6000000007707-2-tps-500-500.png" alt="new" width="30" height="30"/>**[2025-02-19]** LightAgent v0.2.7 支持单独采用 deepseek-r1 作为的agent推理规划ToT引擎,大幅度提升复杂任务的多工具Plan能力.
56
+ - <img src="https://img.alicdn.com/imgextra/i3/O1CN01SFL0Gu26nrQBFKXFR_!!6000000007707-2-tps-500-500.png" alt="new" width="30" height="30"/>**[2025-02-19]** LightAgent v0.3.0 全面支持MCP协议,支持多模型多工具的协同工作,实现更高效的复杂任务处理。<a href="mcp_release.zh-CN.md">查看MCP发布简介>></a>
57
+ - **[2025-02-19]** LightAgent v0.2.7 支持单独采用 deepseek-r1 作为的agent推理规划ToT引擎,大幅度提升复杂任务的多工具Plan能力.
57
58
  - **[2025-02-06]** LightAgent version 0.2.5 is released now.
58
59
  - **[2025-01-20]** LightAgent version 0.2.0 is released now.
59
60
  - **[2025-01-05]** LightAgent version 0.1.0 is released now.
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "LightAgent"
3
- version = "0.2.85"
3
+ version = "0.2.95"
4
4
  description = "**LightAgent** is an extremely lightweight active Agentic Framework with memory (`mem0`), tools (`Tools`), and a Tree of Thought (`ToT`). It supports swarm-like multi-agent collaboration, automated tool generation, and agent assessment, with underlying model support for OpenAI, Zhipu ChatGLM, Baichuan Large Model, DeepSeek R1, Qwen series large models, and more. At the same time, LightAgent supports OpenAI streaming format API service output, seamlessly integrating with major mainstream chat frameworks. 🌟"
5
5
  authors = ["caiweige <caiweige@qq.com>"]
6
6
  license = "Apache 2.0"
@@ -22,12 +22,16 @@ packages = [
22
22
 
23
23
 
24
24
  [tool.poetry.dependencies]
25
- python = ">=3.10,<3.12,!=3.9.7"
25
+ python = ">=3.10,<3.13,!=3.9.7"
26
26
  loguru = "0.7.3"
27
27
  requests = "2.32.3"
28
28
  openai = "1.59.3"
29
29
  colorama = "0.4.6"
30
30
  httpx = "0.28.1"
31
+ httpx-sse = "0.4.0"
32
+ mcp = "1.5.0"
33
+ pydantic-settings = "2.8.1"
34
+
31
35
 
32
36
  [tool.ruff]
33
37
  extend-include = ["*.ipynb"]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes