LightAgent 0.2.85__tar.gz → 0.3.0__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,14 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ 作者: [weego/WXAI-Team]
6
+ 版本: 0.3.0
7
+ 最后更新: 2025-03-31
8
+ """
9
+
1
10
  import re
2
- import traceback
3
- from typing import List, Dict, Any, Callable, Union, Iterable, Optional, Generator
11
+ from typing import List, Dict, Any, Callable, Union, Iterable, Optional, Generator, AsyncGenerator
4
12
  from copy import deepcopy
5
13
  import importlib.util
6
14
  import random
@@ -12,14 +20,24 @@ import os
12
20
  import httpx
13
21
  import importlib
14
22
  from openai.types.chat import ChatCompletionChunk
23
+ import inspect
24
+ import traceback
25
+ from mcp import ClientSession, StdioServerParameters, types
26
+ from contextlib import AsyncExitStack
27
+
28
+ from mcp.client.sse import sse_client
29
+ from mcp.client.stdio import stdio_client
30
+ import asyncio
31
+ from functools import partial
32
+
15
33
 
16
34
  # 全局工具注册表
17
35
  _FUNCTION_MAPPINGS = {} # 工具名称 -> 工具函数
18
- _FUNCTION_INFO = {} # 工具名称 -> 工具info信息
36
+ _FUNCTION_INFO = {} # 工具名称 -> 工具info信息
19
37
  _OPENAI_FUNCTION_SCHEMAS = [] # OpenAI 格式的工具描述
20
38
  _PROMPT_FUNCTION_SCHEMAS = [] # prompt 格式的工具描述
21
39
 
22
- __version__ = "0.2.85" # 你可以根据需要设置版本号
40
+ __version__ = "0.3.0" # 你可以根据需要设置版本号
23
41
 
24
42
 
25
43
  def register_tool_manually(tools: List[Union[str, Callable]]) -> bool:
@@ -88,35 +106,44 @@ def load_tool(tool_name: str, tools_directory: str = "tools"):
88
106
  raise AttributeError(f"Tool '{tool_name}' is not properly defined in {tool_path}")
89
107
 
90
108
 
91
- def dispatch_tool(tool_name: str, tool_params: Dict[str, Any]) -> Union[str, Generator[str, None, None]]:
109
+ async def dispatch_tool(tool_name: str, tool_params: Dict[str, Any]) -> Union[
110
+ str, Generator[str, None, None], AsyncGenerator[str, None]]:
92
111
  """
93
- 调用工具执行,支持流式输出。
94
-
95
- :param tool_name: 工具名称
96
- :param tool_params: 工具参数
97
- :return: 如果工具是流式输出,返回生成器;否则返回字符串结果。
112
+ 调用工具执行,支持同步/异步工具及流式输出。
98
113
  """
99
114
  if tool_name not in _FUNCTION_MAPPINGS:
100
115
  return f"Tool `{tool_name}` not found."
101
116
 
102
117
  tool_call = _FUNCTION_MAPPINGS[tool_name]
103
118
  try:
104
- # print(f"Calling tool: {tool_name} with params: {tool_params}") # 调试信息
105
- result = tool_call(**tool_params)
119
+ # 处理不同类型的流式输出
120
+ # 区分同步/异步工具
121
+ if inspect.iscoroutinefunction(tool_call):
122
+ # result = await tool_call(**tool_params)
123
+ # 将参数以字典形式传递给包装器
124
+ result = await tool_call(**tool_params) if inspect.iscoroutinefunction(tool_call) else tool_call(
125
+ **tool_params)
126
+ else:
127
+ result = tool_call(**tool_params)
106
128
 
107
- # 如果工具返回的是可迭代对象(如生成器),则逐块处理并返回生成器
108
- if isinstance(result, Iterable) and not isinstance(result, (str, bytes)):
129
+ # 处理不同类型的流式输出
130
+ if inspect.isasyncgen(result):
131
+ return async_stream_generator(result)
132
+ elif inspect.isgenerator(result):
109
133
  return stream_generator(result)
110
- # 否则,返回字符串结果
111
134
  else:
112
135
  return str(result)
113
136
  except Exception as e:
114
- # print(f"Tool call failed: {e}") # 调试信息
115
137
  return traceback.format_exc()
116
138
 
117
139
 
118
- def stream_generator(result):
119
- for chunk in result:
140
+ async def async_stream_generator(async_gen: AsyncGenerator) -> AsyncGenerator[str, None]:
141
+ async for chunk in async_gen:
142
+ yield chunk
143
+
144
+
145
+ def stream_generator(sync_gen: Generator) -> Generator[str, None, None]:
146
+ for chunk in sync_gen:
120
147
  yield chunk
121
148
 
122
149
 
@@ -154,26 +181,223 @@ def get_tools_str() -> str:
154
181
  return tools_str
155
182
 
156
183
 
184
+ class MCPClientManager:
185
+ """增强版MCP客户端管理器"""
186
+
187
+ def __init__(self, config: dict):
188
+ self.config = config
189
+ self.session: Optional[ClientSession] = None
190
+ self.exit_stack = AsyncExitStack()
191
+ self._streams_context = None
192
+ self._session_context = None
193
+ self.server_sessions = {} # 存储不同服务器的会话
194
+
195
+ async def _call_tool_wrapper(self, tool_name: str, target_server: str, **kwargs):
196
+ """参数转换适配器"""
197
+ return await self.call_tool(
198
+ tool_name=tool_name,
199
+ arguments=kwargs,
200
+ target_server=target_server
201
+ )
202
+
203
+ async def _create_session(self, server_name: str, config: dict):
204
+ """创建并管理会话上下文"""
205
+ if 'url' in config:
206
+ # SSE 服务器连接
207
+ self._streams_context = sse_client(
208
+ url=config['url'],
209
+ headers=config.get('headers', {})
210
+ )
211
+ streams = await self.exit_stack.enter_async_context(self._streams_context)
212
+ self._session_context = ClientSession(*streams)
213
+ self.session = await self.exit_stack.enter_async_context(self._session_context)
214
+ else:
215
+ # 标准输入输出服务器连接
216
+ server_params = StdioServerParameters(
217
+ command=config["command"],
218
+ args=config["args"],
219
+ env=config.get("env")
220
+ )
221
+ transport = await self.exit_stack.enter_async_context(stdio_client(server_params))
222
+ stdio, write = transport
223
+ self._session_context = ClientSession(stdio, write)
224
+ self.session = await self.exit_stack.enter_async_context(self._session_context)
225
+
226
+ await self.session.initialize()
227
+ self.server_sessions[server_name] = self.session
228
+
229
+ async def cleanup(self):
230
+ """清理所有会话资源"""
231
+ await self.exit_stack.__aexit__(None, None, None)
232
+ self.server_sessions.clear()
233
+
234
+ async def register_mcp_tool(self) -> bool:
235
+ """
236
+ 自动注册所有MCP服务的工具到全局字典
237
+ :param config: MCP服务配置(与call_tool使用的相同配置)
238
+ :return: 是否至少成功注册一个工具
239
+ """
240
+ registered_count = 0
241
+ enabled_servers = [
242
+ (name, config)
243
+ for name, config in self.config["mcpServers"].items()
244
+ if not config["disabled"]
245
+ ]
246
+
247
+ for server_name, config in enabled_servers:
248
+ try:
249
+ # 创建会话连接
250
+ # print(server_name,config)
251
+ await self._create_session(server_name, config)
252
+
253
+ # 获取工具列表
254
+ tools_response = await self.session.list_tools()
255
+ print(f"🔍 Registering tools for server : {server_name} ...")
256
+
257
+ # 注册工具处理逻辑
258
+ for tool in tools_response.tools:
259
+ try:
260
+ # 构建工具元数据
261
+ tool_info = {
262
+ "tool_name": tool.name,
263
+ "tool_description": tool.description,
264
+ "tool_params": []
265
+ }
266
+
267
+ # 解析参数模式
268
+ properties = tool.inputSchema.get("properties", {})
269
+ required_fields = tool.inputSchema.get("required", [])
270
+
271
+ for param_name, param_schema in properties.items():
272
+ tool_info["tool_params"].append({
273
+ "name": param_name,
274
+ "type": param_schema.get("type", "string"),
275
+ "description": param_schema.get("title", ""),
276
+ "required": param_name in required_fields
277
+ })
278
+
279
+ # 注册到全局字典
280
+ _FUNCTION_INFO[tool.name] = tool_info
281
+ _FUNCTION_MAPPINGS[tool.name] = partial(
282
+ self._call_tool_wrapper,
283
+ tool_name=tool.name,
284
+ target_server=server_name
285
+ )
286
+
287
+ # 构建OpenAI格式
288
+ openai_schema = {
289
+ "type": "function",
290
+ "function": {
291
+ "name": tool.name,
292
+ "description": tool.description,
293
+ "parameters": {
294
+ "type": "object",
295
+ "properties": {
296
+ k: {"type": v["type"], "description": v.get("title", "")}
297
+ for k, v in properties.items()
298
+ },
299
+ "required": required_fields
300
+ }
301
+ }
302
+ }
303
+ _OPENAI_FUNCTION_SCHEMAS.append(openai_schema)
304
+
305
+ registered_count += 1
306
+ print(f"✅ The registered tool : {tool.name}")
307
+
308
+ except Exception as e:
309
+ print(f"⚠️ 工具 {tool.name} 注册失败: {str(e)}")
310
+ continue
311
+ print(f"🟢 {registered_count} tools have been registered")
312
+
313
+ except Exception as e:
314
+ print(f"🔴 服务器 {server_name} 连接失败: {str(e)}")
315
+ continue
316
+ # 清理
317
+ await self.cleanup()
318
+ return registered_count > 0
319
+
320
+ async def call_tool(self, tool_name: str, arguments: dict, target_server: str = None):
321
+ """
322
+ 通用工具调用方法
323
+ :param tool_name: 要调用的工具名称
324
+ :param arguments: 工具参数字典
325
+ :param target_server: 指定服务器名称(可选)
326
+ :return: 工具调用结果
327
+ """
328
+ enabled_servers = [
329
+ (name, config)
330
+ for name, config in self.config["mcpServers"].items()
331
+ if not config["disabled"]
332
+ ]
333
+
334
+ if target_server:
335
+ enabled_servers = [s for s in enabled_servers if s[0] == target_server]
336
+
337
+ for server_name, config in enabled_servers:
338
+ try:
339
+ # 复用已建立的会话
340
+ session = self.server_sessions.get(server_name)
341
+ # print(111,server_name,session)
342
+ # print(222,server_name,config)
343
+ if not session:
344
+ await self._create_session(server_name, config)
345
+ session = self.session
346
+
347
+ # 获取工具列表
348
+ tools = await session.list_tools()
349
+ available_tools = {t.name: t for t in tools.tools}
350
+
351
+ if tool_name in available_tools:
352
+ # 验证参数类型
353
+ schema = available_tools[tool_name].inputSchema
354
+ self._validate_arguments(arguments, schema)
355
+
356
+ # 执行调用
357
+ result = await session.call_tool(tool_name, arguments)
358
+ # print(f"mcp工具运行结果: {result.content[0].text}")
359
+ # 调用完成清理session
360
+ await self.cleanup()
361
+ return {
362
+ "server": server_name,
363
+ "tool": tool_name,
364
+ "result": result.content[0].text
365
+ }
366
+
367
+ except Exception as e:
368
+ print(f"调用服务器 {server_name} 失败: {str(e)}")
369
+ continue
370
+
371
+ raise ValueError(f"工具 {tool_name} 在可用服务器中未找到")
372
+
373
+ def _validate_arguments(self, arguments: dict, schema: dict):
374
+ """简单参数校验(可选)"""
375
+ required_fields = schema.get("required", [])
376
+ for field in required_fields:
377
+ if field not in arguments:
378
+ raise ValueError(f"缺少必要参数: {field}")
379
+
380
+
157
381
  class LightAgent:
158
- __version__ = "0.2.85" # 将版本号放在类中
382
+ __version__ = "0.3.0" # 将版本号放在类中
159
383
 
160
384
  def __init__(
161
385
  self,
162
386
  *,
163
387
  name: Optional[str] = None, # 代理名称
164
388
  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,
389
+ role: Optional[str] = None, # 代理角色
390
+ model: str, # agent模型名称
391
+ api_key: str | None = None, # 模型 api key
392
+ base_url: str | httpx.URL | None = None, # 模型 base url
393
+ websocket_base_url: str | httpx.URL | None = None, # 模型 websocket base url
170
394
  memory=None, # 支持外部传入记忆模块
171
395
  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,
396
+ tot_model: str | None = None, # 链式思考模型
397
+ tot_api_key: str | None = None, # 链式思考模型API密钥
398
+ tot_base_url: str | httpx.URL | None = None, # 链式思考模型base_url
175
399
  self_learning: bool = False, # 是否启用agent自我学习
176
- tools: List[Union[str, Callable]] = None, # 支持混合输入
400
+ tools: List[Union[str, Callable]] = None, # 支持工具混合输入
177
401
  debug: bool = False, # 是否启用调试模式
178
402
  log_level: str = "INFO", # 日志级别(INFO, DEBUG, ERROR)
179
403
  log_file: Optional[str] = None # 日志文件路径
@@ -198,6 +422,8 @@ class LightAgent:
198
422
  :param log_level: 日志级别(INFO, DEBUG, ERROR)。
199
423
  :param log_file: 日志文件路径。
200
424
  """
425
+ self.mcp_setting = None
426
+ self.mcp_client = None
201
427
  if not model:
202
428
  model = "gpt-4o-mini" # 默认模型
203
429
  if not api_key:
@@ -206,7 +432,7 @@ class LightAgent:
206
432
  base_url = os.environ.get("OPENAI_BASE_URL")
207
433
  self.loaded_tools = {} # 用于存储已加载的工具函数
208
434
  if not name:
209
- random_suffix = random.randint(10000000, 99999999) # 生成一个8位随机数
435
+ random_suffix = random.randint(10000000, 99999999) # 生成一个8位随机数作为agent编号
210
436
  name = f"LightAgent{random_suffix}"
211
437
  self.name = name
212
438
  if not instructions:
@@ -236,6 +462,8 @@ class LightAgent:
236
462
  # 初始化工具列表
237
463
  self.load_tools(tools)
238
464
  # register_tool_manually(tools)
465
+
466
+
239
467
  if api_key is None:
240
468
  raise ValueError(
241
469
  "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 +475,8 @@ class LightAgent:
247
475
  base_url = f"https://api.openai.com/v1"
248
476
 
249
477
  self.client = OpenAI(
250
- base_url = base_url,
251
- api_key = self.api_key
478
+ base_url=base_url,
479
+ api_key=self.api_key
252
480
  )
253
481
  if self.tree_of_thought:
254
482
  if tot_api_key is None:
@@ -259,8 +487,8 @@ class LightAgent:
259
487
  tot_model = "deepseek-r1" # 默认思维推理模型为deepseek-r1
260
488
  self.tot_model = tot_model
261
489
  self.tot_client = OpenAI(
262
- base_url = tot_base_url,
263
- api_key = tot_api_key
490
+ base_url=tot_base_url,
491
+ api_key=tot_api_key
264
492
  )
265
493
 
266
494
  def get_tool(self, tool_name: str) -> Callable:
@@ -278,7 +506,6 @@ class LightAgent:
278
506
  用于外部可以获取已加载的工具函数列表
279
507
  :return: 工具函数
280
508
  """
281
-
282
509
  return list(_FUNCTION_MAPPINGS.keys())
283
510
 
284
511
  def load_tools(self, tool_names: List[Union[str, Callable]], tools_directory: str = "tools"):
@@ -377,6 +604,18 @@ class LightAgent:
377
604
  elif level == "ERROR":
378
605
  self.logger.error(log_message)
379
606
 
607
+ async def setup_mcp(
608
+ self,
609
+ mcp_setting: dict | None = None, # mcp 设置
610
+ ):
611
+ if mcp_setting:
612
+ self.mcp_setting = mcp_setting
613
+ """单独初始化 MCP 模块"""
614
+ if self.mcp_setting and not self.mcp_client:
615
+ self.mcp_client = MCPClientManager(self.mcp_setting)
616
+ await self.mcp_client.register_mcp_tool()
617
+ self.log("INFO", "setup_mcp", "MCP 模块初始化成功")
618
+
380
619
  def run(
381
620
  self,
382
621
  query: str,
@@ -489,18 +728,18 @@ class LightAgent:
489
728
  fixed_args = function_call.arguments.replace('\\"', '"').replace('\\\\', '\\')
490
729
  self.log("DEBUG", "non_stream function_call", {"function_call": fixed_args})
491
730
 
492
- function_args = json.loads(fixed_args)
493
731
  # 解析函数参数
494
- # function_args = json.loads(function_call.arguments)
732
+ function_args = json.loads(fixed_args)
495
733
 
496
734
  # 调用工具并获取响应
497
- tool_response = dispatch_tool(function_call.name, function_args)
735
+ tool_response = asyncio.run(dispatch_tool(function_call.name, function_args))
498
736
  function_call_name = function_call.name
499
737
  combined_response = ""
738
+ single_tool_response = ""
500
739
 
501
740
  # 如果工具返回的是生成器(流式输出),则将所有 chunk 叠加
502
741
  if isinstance(tool_response, Generator):
503
- print(f"Streaming response from tool: {function_call.name}")
742
+ # print(f"Streaming response from tool: {function_call.name}")
504
743
  for chunk in tool_response:
505
744
  # print("Received chunk:", chunk) # 打印每个 chunk
506
745
  if function_call_name == 'finish':
@@ -520,8 +759,8 @@ class LightAgent:
520
759
  # 将 JSON 对象中的 Unicode 编码转换为中文字符
521
760
  if isinstance(combined_response, dict):
522
761
  combined_response = json.dumps(combined_response, ensure_ascii=False) # 确保中文字符不转义
762
+ single_tool_response = combined_response # 处理单个工具的方法
523
763
 
524
- tool_responses.append(combined_response) # 将叠加后的完整响应添加到列表
525
764
  else:
526
765
  # print(f"Non-streaming response from tool: {function_call.name}")
527
766
  combined_response = tool_response
@@ -534,22 +773,23 @@ class LightAgent:
534
773
  except json.JSONDecodeError:
535
774
  combined_response = tool_response
536
775
  pass # 如果不是 JSON 字符串,保持原样
537
- tool_responses.append(combined_response) # 直接添加普通响应
776
+ single_tool_response = combined_response # 处理单个工具的方法
538
777
 
539
- self.log("INFO", "non_stream tool_response", {"tool_response": combined_response})
778
+ self.log("INFO", "non_stream single_tool_response", {"single_tool_response": single_tool_response})
540
779
 
541
- # 将工具调用的结果添加到列表中
542
- tool_responses.append(combined_response)
780
+ # 将单个工具的响应结果添加到列表中
781
+ tool_responses.append(single_tool_response)
543
782
 
544
783
  # 将所有工具调用的结果合并为一个字符串
784
+ self.log("DEBUG", "non_stream tool_responses", {"tool_responses": tool_responses})
785
+
545
786
  combined_tool_response = "\n".join(tool_responses)
546
787
 
547
788
  # 将工具调用和响应添加到消息列表中
548
789
  params["messages"].append(
549
790
  {
550
791
  "role": "assistant",
551
- "content": json.dumps(
552
- [tool_call.function.model_dump() for tool_call in tool_calls]),
792
+ "content": f"使用工具: \n {json.dumps([tool_call.function.model_dump() for tool_call in tool_calls], ensure_ascii=False)}\n",
553
793
  }
554
794
  )
555
795
  params["messages"].append(
@@ -561,14 +801,14 @@ class LightAgent:
561
801
  else:
562
802
  # 返回最终回复
563
803
  reply = response.choices[0].message.content
564
- self.log("INFO", "final_reply", {"reply": reply})
804
+ self.log("INFO", "non_stream final_reply", {"reply": reply})
565
805
  return reply
566
806
 
567
807
  # 更新响应
568
808
  if function_call_name == 'finish':
569
809
  return # 如果最后调用了finish工具,则结束生成器
570
810
  # print("params:",params)
571
- self.log("DEBUG", "chat-completions params", {"params": params})
811
+ self.log("DEBUG", "non_stream chat-completions params", {"params": params})
572
812
 
573
813
  try:
574
814
  response = self.client.chat.completions.create(**params)
@@ -631,7 +871,7 @@ class LightAgent:
631
871
  chunk.choices[0].finish_reason == "stop" and any(
632
872
  tool_call["name"] for tool_call in tool_calls)):
633
873
  # 遍历所有工具调用
634
- self.log("DEBUG", "tool_calls", {"tool_calls": tool_calls})
874
+ self.log("DEBUG", "stream tool_calls", {"tool_calls": tool_calls})
635
875
  for tool_call in tool_calls:
636
876
  if tool_call["name"]: # 确保工具调用有名称
637
877
  function_call = {
@@ -639,7 +879,7 @@ class LightAgent:
639
879
  "title": _FUNCTION_INFO.get(tool_call["name"], {}).get('tool_title') or '',
640
880
  "arguments": tool_call["arguments"],
641
881
  }
642
- self.log("INFO", "tool_call", {"function_call": function_call})
882
+ self.log("INFO", "stream function_call", {"function_call": function_call})
643
883
  # 将工具的调用信息推送给开发者
644
884
  yield function_call
645
885
 
@@ -655,14 +895,20 @@ class LightAgent:
655
895
  # tool_responses.append(tool_response)
656
896
 
657
897
  for json_obj in json_objects:
658
- function_args = json.loads(json_obj)
659
- tool_response = dispatch_tool(function_call["name"], function_args)
898
+ # 尝试自动修复常见转义问题
899
+ fixed_args = json_obj.replace('\\"', '"').replace('\\\\', '\\')
900
+ self.log("DEBUG", "stream fixed_args", {"fixed_args": fixed_args})
901
+
902
+ function_args = json.loads(fixed_args)
903
+ # tool_response = dispatch_tool(function_call["name"], function_args)
904
+ tool_response = asyncio.run(dispatch_tool(function_call["name"], function_args))
660
905
  function_call_name = function_call["name"]
906
+ combined_response = ""
907
+ single_tool_response = ""
661
908
 
662
909
  # 如果工具返回的是生成器(流式输出),则将所有 chunk 叠加
663
910
  if isinstance(tool_response, Generator):
664
911
  # print(f"Streaming response from tool: {function_call['name']}")
665
- combined_response = ""
666
912
  for chunk in tool_response:
667
913
  # 将工具返回的数据继续流出
668
914
  if isinstance(chunk, ChatCompletionChunk):
@@ -674,7 +920,7 @@ class LightAgent:
674
920
  'tool_title') or '',
675
921
  "output": chunk,
676
922
  }
677
- self.log("INFO", "tool_call", {"tool_output": tool_output})
923
+ self.log("DEBUG", "stream tool_output", {"tool_output": tool_output})
678
924
  yield tool_output
679
925
  # 将工具的调用信息推送给开发者
680
926
  if function_call_name == 'finish':
@@ -682,13 +928,15 @@ class LightAgent:
682
928
  combined_response += content # 将每个 chunk 叠加
683
929
  else:
684
930
  combined_response += chunk # 将每个 chunk 叠加
685
- tool_responses.append(combined_response) # 将叠加后的完整响应添加到列表
931
+ single_tool_response = combined_response # 处理单个工具的方法
686
932
  else:
687
933
  # print(f"Non-streaming response from tool: {tool_response}")
688
934
  combined_response = tool_response
689
- tool_responses.append(combined_response) # 直接添加普通响应
690
- self.log("INFO", "stream tool_response", {"tool_response": combined_response})
691
-
935
+ single_tool_response = combined_response # 处理单个工具的方法
936
+ self.log("INFO", "stream single_tool_response",
937
+ {"single_tool_response": single_tool_response})
938
+ # 将单个工具的响应结果添加到列表中
939
+ tool_responses.append(single_tool_response)
692
940
 
693
941
  except json.JSONDecodeError as e:
694
942
  self.log("ERROR", "json_decode_error",
@@ -697,15 +945,15 @@ class LightAgent:
697
945
 
698
946
  # 将所有工具调用的结果合并为一个字符串
699
947
  combined_tool_response = "\n".join(tool_responses)
948
+ tool_str = json.dumps(
949
+ [{"name": tool_call["name"], "arguments": tool_call["arguments"]} for tool_call in tool_calls],
950
+ ensure_ascii=False)
700
951
 
701
952
  # 将工具调用和响应添加到消息列表中
702
953
  params["messages"].append(
703
954
  {
704
955
  "role": "assistant",
705
- "content": json.dumps(
706
- [{"name": tool_call["name"], "arguments": tool_call["arguments"]} for tool_call in
707
- tool_calls]
708
- ),
956
+ "content": f"使用工具: \n {tool_str}\n"
709
957
  }
710
958
  )
711
959
  params["messages"].append(
@@ -720,7 +968,7 @@ class LightAgent:
720
968
  # 更新响应
721
969
  if function_call_name == 'finish':
722
970
  return # 如果最后调用了finish工具,则结束生成器
723
- self.log("DEBUG", "chat-completions params", {"params": params})
971
+ self.log("DEBUG", "stream chat-completions params", {"params": params})
724
972
  response = self.client.chat.completions.create(**params)
725
973
 
726
974
  # 重试次数用尽
@@ -1024,6 +1272,7 @@ class LightAgent:
1024
1272
  # 在函数内部定义工具信息
1025
1273
  get_weather.tool_info = {
1026
1274
  "tool_name": "get_weather",
1275
+ "tool_title": "天气查询",
1027
1276
  "tool_description": "获取指定城市的当前天气信息",
1028
1277
  "tool_params": [
1029
1278
  {"name": "city_name", "description": "要查询的城市名称", "type": "string", "required": True},
@@ -1065,7 +1314,7 @@ get_weather.tool_info = {
1065
1314
  f.write(tool_code)
1066
1315
  self.log("INFO", "tool_created", {"tool_name": tool_name, "tool_path": tool_path})
1067
1316
 
1068
- # 加载新创建的工具
1317
+ # 自动加载新创建的工具
1069
1318
  self.load_tools([tool_name], tools_directory)
1070
1319
  except Exception as e:
1071
1320
  self.log("ERROR", "tool_creation_failed", {"error": str(e)})
@@ -1106,4 +1355,4 @@ class LightSwarm:
1106
1355
  if __name__ == "__main__":
1107
1356
  # Example of registering and using a tool
1108
1357
  print("This is LightAgent")
1109
- # print(dispatch_tool("example_tool", {"param1": "test"}))
1358
+ # 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.3.0
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.3.0"
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