LightAgent 0.2.8__tar.gz → 0.2.9__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.
@@ -11,6 +11,8 @@ import logging
11
11
  import os
12
12
  import httpx
13
13
  import importlib
14
+ from openai.types.chat import ChatCompletionChunk
15
+ import asyncio
14
16
 
15
17
  # 全局工具注册表
16
18
  _FUNCTION_MAPPINGS = {} # 工具名称 -> 工具函数
@@ -18,7 +20,7 @@ _FUNCTION_INFO = {} # 工具名称 -> 工具info信息
18
20
  _OPENAI_FUNCTION_SCHEMAS = [] # OpenAI 格式的工具描述
19
21
  _PROMPT_FUNCTION_SCHEMAS = [] # prompt 格式的工具描述
20
22
 
21
- __version__ = "0.2.8" # 你可以根据需要设置版本号
23
+ __version__ = "0.2.9" # 你可以根据需要设置版本号
22
24
 
23
25
 
24
26
  def register_tool_manually(tools: List[Union[str, Callable]]) -> bool:
@@ -86,36 +88,41 @@ def load_tool(tool_name: str, tools_directory: str = "tools"):
86
88
  return tool_func
87
89
  raise AttributeError(f"Tool '{tool_name}' is not properly defined in {tool_path}")
88
90
 
91
+ from typing import Dict, Any, Union, Generator, AsyncGenerator
92
+ import inspect
93
+ import traceback
89
94
 
90
- def dispatch_tool(tool_name: str, tool_params: Dict[str, Any]) -> Union[str, Generator[str, None, None]]:
95
+ async def dispatch_tool(tool_name: str, tool_params: Dict[str, Any]) -> Union[str, Generator[str, None, None], AsyncGenerator[str, None]]:
91
96
  """
92
- 调用工具执行,支持流式输出。
93
-
94
- :param tool_name: 工具名称
95
- :param tool_params: 工具参数
96
- :return: 如果工具是流式输出,返回生成器;否则返回字符串结果。
97
+ 调用工具执行,支持同步/异步工具及流式输出。
97
98
  """
98
99
  if tool_name not in _FUNCTION_MAPPINGS:
99
100
  return f"Tool `{tool_name}` not found."
100
101
 
101
102
  tool_call = _FUNCTION_MAPPINGS[tool_name]
102
103
  try:
103
- # print(f"Calling tool: {tool_name} with params: {tool_params}") # 调试信息
104
- result = tool_call(**tool_params)
104
+ # 区分同步/异步工具
105
+ if inspect.iscoroutinefunction(tool_call):
106
+ result = await tool_call(**tool_params)
107
+ else:
108
+ result = tool_call(**tool_params)
105
109
 
106
- # 如果工具返回的是可迭代对象(如生成器),则逐块处理并返回生成器
107
- if isinstance(result, Iterable) and not isinstance(result, (str, bytes)):
110
+ # 处理不同类型的流式输出
111
+ if inspect.isasyncgen(result):
112
+ return async_stream_generator(result)
113
+ elif inspect.isgenerator(result):
108
114
  return stream_generator(result)
109
- # 否则,返回字符串结果
110
115
  else:
111
116
  return str(result)
112
117
  except Exception as e:
113
- # print(f"Tool call failed: {e}") # 调试信息
114
118
  return traceback.format_exc()
115
119
 
120
+ async def async_stream_generator(async_gen: AsyncGenerator) -> AsyncGenerator[str, None]:
121
+ async for chunk in async_gen:
122
+ yield chunk
116
123
 
117
- def stream_generator(result):
118
- for chunk in result:
124
+ def stream_generator(sync_gen: Generator) -> Generator[str, None, None]:
125
+ for chunk in sync_gen:
119
126
  yield chunk
120
127
 
121
128
 
@@ -154,7 +161,7 @@ def get_tools_str() -> str:
154
161
 
155
162
 
156
163
  class LightAgent:
157
- __version__ = "0.2.1" # 将版本号放在类中
164
+ __version__ = "0.2.9" # 将版本号放在类中
158
165
 
159
166
  def __init__(
160
167
  self,
@@ -478,20 +485,25 @@ class LightAgent:
478
485
  output = ""
479
486
  function_call_name = ""
480
487
  tool_calls = response.choices[0].message.tool_calls
481
- self.log("DEBUG", "tool_calls", {"tool_calls": tool_calls})
488
+ self.log("DEBUG", "non_stream tool_calls", {"tool_calls": tool_calls})
482
489
 
483
490
  # 遍历所有工具调用
484
491
  for tool_call in tool_calls:
485
492
  function_call = tool_call.function
486
- self.log("DEBUG", "function_call", {"function_call": function_call.model_dump()})
487
493
 
494
+ # 尝试自动修复常见转义问题
495
+ fixed_args = function_call.arguments.replace('\\"', '"').replace('\\\\', '\\')
496
+ self.log("DEBUG", "non_stream function_call", {"function_call": fixed_args})
497
+
498
+ function_args = json.loads(fixed_args)
488
499
  # 解析函数参数
489
- function_args = json.loads(function_call.arguments)
500
+ # function_args = json.loads(function_call.arguments)
490
501
 
491
502
  # 调用工具并获取响应
492
- tool_response = dispatch_tool(function_call.name, function_args)
503
+ tool_response = asyncio.run(dispatch_tool(function_call.name, function_args))
493
504
  function_call_name = function_call.name
494
505
  combined_response = ""
506
+ single_tool_response = ""
495
507
 
496
508
  # 如果工具返回的是生成器(流式输出),则将所有 chunk 叠加
497
509
  if isinstance(tool_response, Generator):
@@ -515,34 +527,38 @@ class LightAgent:
515
527
  # 将 JSON 对象中的 Unicode 编码转换为中文字符
516
528
  if isinstance(combined_response, dict):
517
529
  combined_response = json.dumps(combined_response, ensure_ascii=False) # 确保中文字符不转义
530
+ single_tool_response = combined_response # 处理单个工具的方法
518
531
 
519
- tool_responses.append(combined_response) # 将叠加后的完整响应添加到列表
520
532
  else:
521
533
  # print(f"Non-streaming response from tool: {function_call.name}")
522
534
  combined_response = tool_response
535
+ # print("tool_response type:",type(combined_response))
523
536
  # 如果是 JSON 字符串,解析并转换为中文
524
537
  if isinstance(combined_response, str):
525
538
  try:
526
539
  combined_response = json.loads(combined_response) # 解析 JSON
527
540
  combined_response = json.dumps(combined_response, ensure_ascii=False) # 转换为中文
528
541
  except json.JSONDecodeError:
542
+ combined_response = tool_response
529
543
  pass # 如果不是 JSON 字符串,保持原样
530
- tool_responses.append(combined_response) # 直接添加普通响应
544
+ single_tool_response = combined_response # 处理单个工具的方法
531
545
 
532
- self.log("INFO", "tool_response", {"tool_response": combined_response})
533
546
 
534
- # 将工具调用的结果添加到列表中
535
- tool_responses.append(combined_response)
547
+ self.log("DEBUG", "non_stream single_tool_response", {"single_tool_response": single_tool_response})
548
+
549
+ # 将单个工具的响应结果添加到列表中
550
+ tool_responses.append(single_tool_response)
536
551
 
537
552
  # 将所有工具调用的结果合并为一个字符串
553
+ self.log("DEBUG", "non_stream tool_responses", {"tool_responses": tool_responses})
554
+
538
555
  combined_tool_response = "\n".join(tool_responses)
539
556
 
540
557
  # 将工具调用和响应添加到消息列表中
541
558
  params["messages"].append(
542
559
  {
543
560
  "role": "assistant",
544
- "content": json.dumps(
545
- [tool_call.function.model_dump() for tool_call in tool_calls]),
561
+ "content": f"使用工具: \n {json.dumps([tool_call.function.model_dump() for tool_call in tool_calls],ensure_ascii=False)}\n",
546
562
  }
547
563
  )
548
564
  params["messages"].append(
@@ -560,8 +576,13 @@ class LightAgent:
560
576
  # 更新响应
561
577
  if function_call_name == 'finish':
562
578
  return # 如果最后调用了finish工具,则结束生成器
563
- # print(params)
564
- response = self.client.chat.completions.create(**params)
579
+ # print("params:",params)
580
+ self.log("DEBUG", "chat-completions params", {"params": params})
581
+
582
+ try:
583
+ response = self.client.chat.completions.create(**params)
584
+ except Exception as e:
585
+ print(f"An error occurred: {e}")
565
586
 
566
587
  # 重试次数用尽
567
588
  self.log("ERROR", "max_retry_reached", {"message": "Failed to generate a valid response."})
@@ -644,33 +665,42 @@ class LightAgent:
644
665
 
645
666
  for json_obj in json_objects:
646
667
  function_args = json.loads(json_obj)
647
- tool_response = dispatch_tool(function_call["name"], function_args)
668
+ # tool_response = dispatch_tool(function_call["name"], function_args)
669
+ tool_response = asyncio.run(dispatch_tool(function_call["name"], function_args))
648
670
  function_call_name = function_call["name"]
671
+ combined_response = ""
672
+ single_tool_response = ""
649
673
 
650
674
  # 如果工具返回的是生成器(流式输出),则将所有 chunk 叠加
651
675
  if isinstance(tool_response, Generator):
652
676
  # print(f"Streaming response from tool: {function_call['name']}")
653
- combined_response = ""
654
677
  for chunk in tool_response:
655
678
  # 将工具返回的数据继续流出
656
- tool_output = {
657
- "name": tool_call["name"],
658
- "title": _FUNCTION_INFO.get(tool_call["name"], {}).get(
659
- 'tool_title') or '',
660
- "output": chunk,
661
- }
662
- self.log("INFO", "tool_call", {"tool_output": tool_output})
663
- yield tool_output
679
+ if isinstance(chunk, ChatCompletionChunk):
680
+ yield chunk
681
+ else:
682
+ tool_output = {
683
+ "name": tool_call["name"],
684
+ "title": _FUNCTION_INFO.get(tool_call["name"], {}).get(
685
+ 'tool_title') or '',
686
+ "output": chunk,
687
+ }
688
+ self.log("INFO", "tool_call", {"tool_output": tool_output})
689
+ yield tool_output
664
690
  # 将工具的调用信息推送给开发者
665
691
  if function_call_name == 'finish':
666
692
  content = chunk.choices[0].delta.content or ""
667
693
  combined_response += content # 将每个 chunk 叠加
668
694
  else:
669
695
  combined_response += chunk # 将每个 chunk 叠加
670
- tool_responses.append(combined_response) # 将叠加后的完整响应添加到列表
696
+ single_tool_response = combined_response # 处理单个工具的方法
671
697
  else:
672
- # print(f"Non-streaming response from tool: {function_call['name']}")
673
- tool_responses.append(tool_response) # 直接添加普通响应
698
+ # print(f"Non-streaming response from tool: {tool_response}")
699
+ combined_response = tool_response
700
+ single_tool_response = combined_response # 处理单个工具的方法
701
+ self.log("INFO", "stream single_tool_response", {"single_tool_response": single_tool_response})
702
+ # 将单个工具的响应结果添加到列表中
703
+ tool_responses.append(single_tool_response)
674
704
 
675
705
  except json.JSONDecodeError as e:
676
706
  self.log("ERROR", "json_decode_error",
@@ -702,6 +732,7 @@ class LightAgent:
702
732
  # 更新响应
703
733
  if function_call_name == 'finish':
704
734
  return # 如果最后调用了finish工具,则结束生成器
735
+ self.log("DEBUG", "chat-completions params", {"params": params})
705
736
  response = self.client.chat.completions.create(**params)
706
737
 
707
738
  # 重试次数用尽
@@ -1005,6 +1036,7 @@ class LightAgent:
1005
1036
  # 在函数内部定义工具信息
1006
1037
  get_weather.tool_info = {
1007
1038
  "tool_name": "get_weather",
1039
+ "tool_title": "天气查询",
1008
1040
  "tool_description": "获取指定城市的当前天气信息",
1009
1041
  "tool_params": [
1010
1042
  {"name": "city_name", "description": "要查询的城市名称", "type": "string", "required": True},
@@ -1087,4 +1119,4 @@ class LightSwarm:
1087
1119
  if __name__ == "__main__":
1088
1120
  # Example of registering and using a tool
1089
1121
  print("This is LightAgent")
1090
- # print(dispatch_tool("example_tool", {"param1": "test"}))
1122
+ # print(dispatch_tool("example_tool", {"param1": "test"}))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: LightAgent
3
- Version: 0.2.8
3
+ Version: 0.2.9
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
@@ -58,6 +58,8 @@ Description-Content-Type: text/markdown
58
58
 
59
59
  ---
60
60
 
61
+ ![lightswarm_demo_en.png](docs%2Fimages%2Flightswarm_demo_en.png)
62
+
61
63
  ## ✨ Features
62
64
 
63
65
  - **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.
@@ -356,6 +358,7 @@ def get_weather(
356
358
  # Define tool information inside the function
357
359
  get_weather.tool_info = {
358
360
  "tool_name": "get_weather",
361
+ "tool_title": "get weather",
359
362
  "tool_description": "Get current weather information for the specified city.",
360
363
  "tool_params": [
361
364
  {"name": "city_name", "description": "The name of the city to query", "type": "string", "required": True},
@@ -378,6 +381,7 @@ def search_news(
378
381
  # Define tool information inside the function
379
382
  search_news.tool_info = {
380
383
  "tool_name": "search_news",
384
+ "tool_title": "search news",
381
385
  "tool_description": "Search news based on keywords.",
382
386
  "tool_params": [
383
387
  {"name": "keyword", "description": "Search keyword", "type": "string", "required": True},
@@ -774,7 +778,7 @@ We look forward to your feedback and work together to make LightAgent even stron
774
778
  **LightAgent** 是一个极其轻量的带记忆(`mem0`)、工具(`Tools`)、思维树(`ToT`)的主动式 Agentic Framework(自主性框架)。它支持比Openai Swarm更简单的多智能体协同,构建具备自我学习能力的agent,并支持Agent测评,底层模型支持 OpenAI、智谱 ChatGLM、DeepSeek、阶跃星辰、Qwen通义千问大模型等。同时,LightAgent 支持 OpenAI 流格式 API 服务输出,无缝接入各大主流 Chat 框架。🌟
775
779
 
776
780
  ---
777
-
781
+ ![lightswarm_demo_cn.png](docs%2Fimages%2Flightswarm_demo_cn.png)
778
782
  ## ✨ 特性
779
783
 
780
784
  - **轻量高效** 🚀:极简设计,快速部署,适合各种规模的应用场景。(No LangChain, No LlamaIndex)100% Python 实现,无需额外依赖,核心代码仅1000行,完全开源。
@@ -1077,6 +1081,7 @@ def get_weather(
1077
1081
  # 在函数内部定义工具信息
1078
1082
  get_weather.tool_info = {
1079
1083
  "tool_name": "get_weather",
1084
+ "tool_name": "获取天气",
1080
1085
  "tool_description": "获取指定城市的当前天气信息",
1081
1086
  "tool_params": [
1082
1087
  {"name": "city_name", "description": "要查询的城市名称", "type": "string", "required": True},
@@ -1099,6 +1104,7 @@ def search_news(
1099
1104
  # 在函数内部定义工具信息
1100
1105
  search_news.tool_info = {
1101
1106
  "tool_name": "search_news",
1107
+ "tool_name": "联网搜索",
1102
1108
  "tool_description": "根据关键词搜索新闻",
1103
1109
  "tool_params": [
1104
1110
  {"name": "keyword", "description": "搜索关键词", "type": "string", "required": True},
@@ -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.
@@ -335,6 +337,7 @@ def get_weather(
335
337
  # Define tool information inside the function
336
338
  get_weather.tool_info = {
337
339
  "tool_name": "get_weather",
340
+ "tool_title": "get weather",
338
341
  "tool_description": "Get current weather information for the specified city.",
339
342
  "tool_params": [
340
343
  {"name": "city_name", "description": "The name of the city to query", "type": "string", "required": True},
@@ -357,6 +360,7 @@ def search_news(
357
360
  # Define tool information inside the function
358
361
  search_news.tool_info = {
359
362
  "tool_name": "search_news",
363
+ "tool_title": "search news",
360
364
  "tool_description": "Search news based on keywords.",
361
365
  "tool_params": [
362
366
  {"name": "keyword", "description": "Search keyword", "type": "string", "required": True},
@@ -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行,完全开源。
@@ -339,6 +339,7 @@ def get_weather(
339
339
  # 在函数内部定义工具信息
340
340
  get_weather.tool_info = {
341
341
  "tool_name": "get_weather",
342
+ "tool_name": "获取天气",
342
343
  "tool_description": "获取指定城市的当前天气信息",
343
344
  "tool_params": [
344
345
  {"name": "city_name", "description": "要查询的城市名称", "type": "string", "required": True},
@@ -361,6 +362,7 @@ def search_news(
361
362
  # 在函数内部定义工具信息
362
363
  search_news.tool_info = {
363
364
  "tool_name": "search_news",
365
+ "tool_name": "联网搜索",
364
366
  "tool_description": "根据关键词搜索新闻",
365
367
  "tool_params": [
366
368
  {"name": "keyword", "description": "搜索关键词", "type": "string", "required": True},
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "LightAgent"
3
- version = "0.2.8"
3
+ version = "0.2.9"
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"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes