aient 1.0.42__tar.gz → 1.0.43__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.
Files changed (72) hide show
  1. {aient-1.0.42/src/aient.egg-info → aient-1.0.43}/PKG-INFO +1 -1
  2. {aient-1.0.42 → aient-1.0.43}/setup.py +1 -1
  3. {aient-1.0.42 → aient-1.0.43}/src/aient/models/chatgpt.py +89 -48
  4. {aient-1.0.42 → aient-1.0.43}/src/aient/utils/scripts.py +188 -78
  5. {aient-1.0.42 → aient-1.0.43/src/aient.egg-info}/PKG-INFO +1 -1
  6. {aient-1.0.42 → aient-1.0.43}/LICENSE +0 -0
  7. {aient-1.0.42 → aient-1.0.43}/MANIFEST.in +0 -0
  8. {aient-1.0.42 → aient-1.0.43}/README.md +0 -0
  9. {aient-1.0.42 → aient-1.0.43}/setup.cfg +0 -0
  10. {aient-1.0.42 → aient-1.0.43}/src/aient/__init__.py +0 -0
  11. {aient-1.0.42 → aient-1.0.43}/src/aient/core/.git +0 -0
  12. {aient-1.0.42 → aient-1.0.43}/src/aient/core/__init__.py +0 -0
  13. {aient-1.0.42 → aient-1.0.43}/src/aient/core/log_config.py +0 -0
  14. {aient-1.0.42 → aient-1.0.43}/src/aient/core/models.py +0 -0
  15. {aient-1.0.42 → aient-1.0.43}/src/aient/core/request.py +0 -0
  16. {aient-1.0.42 → aient-1.0.43}/src/aient/core/response.py +0 -0
  17. {aient-1.0.42 → aient-1.0.43}/src/aient/core/test/test_base_api.py +0 -0
  18. {aient-1.0.42 → aient-1.0.43}/src/aient/core/test/test_image.py +0 -0
  19. {aient-1.0.42 → aient-1.0.43}/src/aient/core/test/test_payload.py +0 -0
  20. {aient-1.0.42 → aient-1.0.43}/src/aient/core/utils.py +0 -0
  21. {aient-1.0.42 → aient-1.0.43}/src/aient/models/__init__.py +0 -0
  22. {aient-1.0.42 → aient-1.0.43}/src/aient/models/audio.py +0 -0
  23. {aient-1.0.42 → aient-1.0.43}/src/aient/models/base.py +0 -0
  24. {aient-1.0.42 → aient-1.0.43}/src/aient/models/claude.py +0 -0
  25. {aient-1.0.42 → aient-1.0.43}/src/aient/models/duckduckgo.py +0 -0
  26. {aient-1.0.42 → aient-1.0.43}/src/aient/models/gemini.py +0 -0
  27. {aient-1.0.42 → aient-1.0.43}/src/aient/models/groq.py +0 -0
  28. {aient-1.0.42 → aient-1.0.43}/src/aient/models/vertex.py +0 -0
  29. {aient-1.0.42 → aient-1.0.43}/src/aient/plugins/__init__.py +0 -0
  30. {aient-1.0.42 → aient-1.0.43}/src/aient/plugins/arXiv.py +0 -0
  31. {aient-1.0.42 → aient-1.0.43}/src/aient/plugins/config.py +0 -0
  32. {aient-1.0.42 → aient-1.0.43}/src/aient/plugins/image.py +0 -0
  33. {aient-1.0.42 → aient-1.0.43}/src/aient/plugins/registry.py +0 -0
  34. {aient-1.0.42 → aient-1.0.43}/src/aient/plugins/run_python.py +0 -0
  35. {aient-1.0.42 → aient-1.0.43}/src/aient/plugins/today.py +0 -0
  36. {aient-1.0.42 → aient-1.0.43}/src/aient/plugins/websearch.py +0 -0
  37. {aient-1.0.42 → aient-1.0.43}/src/aient/utils/__init__.py +0 -0
  38. {aient-1.0.42 → aient-1.0.43}/src/aient/utils/prompt.py +0 -0
  39. {aient-1.0.42 → aient-1.0.43}/src/aient.egg-info/SOURCES.txt +0 -0
  40. {aient-1.0.42 → aient-1.0.43}/src/aient.egg-info/dependency_links.txt +0 -0
  41. {aient-1.0.42 → aient-1.0.43}/src/aient.egg-info/requires.txt +0 -0
  42. {aient-1.0.42 → aient-1.0.43}/src/aient.egg-info/top_level.txt +0 -0
  43. {aient-1.0.42 → aient-1.0.43}/test/test.py +0 -0
  44. {aient-1.0.42 → aient-1.0.43}/test/test_API.py +0 -0
  45. {aient-1.0.42 → aient-1.0.43}/test/test_Deepbricks.py +0 -0
  46. {aient-1.0.42 → aient-1.0.43}/test/test_Web_crawler.py +0 -0
  47. {aient-1.0.42 → aient-1.0.43}/test/test_aiwaves.py +0 -0
  48. {aient-1.0.42 → aient-1.0.43}/test/test_aiwaves_arxiv.py +0 -0
  49. {aient-1.0.42 → aient-1.0.43}/test/test_ask_gemini.py +0 -0
  50. {aient-1.0.42 → aient-1.0.43}/test/test_class.py +0 -0
  51. {aient-1.0.42 → aient-1.0.43}/test/test_claude.py +0 -0
  52. {aient-1.0.42 → aient-1.0.43}/test/test_claude_zh_char.py +0 -0
  53. {aient-1.0.42 → aient-1.0.43}/test/test_ddg_search.py +0 -0
  54. {aient-1.0.42 → aient-1.0.43}/test/test_download_pdf.py +0 -0
  55. {aient-1.0.42 → aient-1.0.43}/test/test_gemini.py +0 -0
  56. {aient-1.0.42 → aient-1.0.43}/test/test_get_token_dict.py +0 -0
  57. {aient-1.0.42 → aient-1.0.43}/test/test_google_search.py +0 -0
  58. {aient-1.0.42 → aient-1.0.43}/test/test_jieba.py +0 -0
  59. {aient-1.0.42 → aient-1.0.43}/test/test_json.py +0 -0
  60. {aient-1.0.42 → aient-1.0.43}/test/test_langchain_search_old.py +0 -0
  61. {aient-1.0.42 → aient-1.0.43}/test/test_logging.py +0 -0
  62. {aient-1.0.42 → aient-1.0.43}/test/test_ollama.py +0 -0
  63. {aient-1.0.42 → aient-1.0.43}/test/test_plugin.py +0 -0
  64. {aient-1.0.42 → aient-1.0.43}/test/test_py_run.py +0 -0
  65. {aient-1.0.42 → aient-1.0.43}/test/test_requests.py +0 -0
  66. {aient-1.0.42 → aient-1.0.43}/test/test_search.py +0 -0
  67. {aient-1.0.42 → aient-1.0.43}/test/test_tikitoken.py +0 -0
  68. {aient-1.0.42 → aient-1.0.43}/test/test_token.py +0 -0
  69. {aient-1.0.42 → aient-1.0.43}/test/test_url.py +0 -0
  70. {aient-1.0.42 → aient-1.0.43}/test/test_whisper.py +0 -0
  71. {aient-1.0.42 → aient-1.0.43}/test/test_wildcard.py +0 -0
  72. {aient-1.0.42 → aient-1.0.43}/test/test_yjh.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aient
3
- Version: 1.0.42
3
+ Version: 1.0.43
4
4
  Summary: Aient: The Awakening of Agent.
5
5
  Description-Content-Type: text/markdown
6
6
  License-File: LICENSE
@@ -4,7 +4,7 @@ from setuptools import setup, find_packages
4
4
 
5
5
  setup(
6
6
  name="aient",
7
- version="1.0.42",
7
+ version="1.0.43",
8
8
  description="Aient: The Awakening of Agent.",
9
9
  long_description=Path.open(Path("README.md"), encoding="utf-8").read(),
10
10
  long_description_content_type="text/markdown",
@@ -6,13 +6,13 @@ import httpx
6
6
  import asyncio
7
7
  import requests
8
8
  from typing import Set
9
- from typing import Union, Optional, Callable
9
+ from typing import Union, Optional, Callable, List, Dict, Any
10
10
  from pathlib import Path
11
11
 
12
12
 
13
13
  from .base import BaseLLM
14
14
  from ..plugins import PLUGINS, get_tools_result_async, function_call_list, update_tools_config
15
- from ..utils.scripts import check_json, safe_get, async_generator_to_sync, parse_function_xml
15
+ from ..utils.scripts import safe_get, async_generator_to_sync, parse_function_xml, parse_continuous_json
16
16
  from ..core.request import prepare_request_payload
17
17
  from ..core.response import fetch_response_stream
18
18
 
@@ -148,8 +148,8 @@ class chatgpt(BaseLLM):
148
148
  })
149
149
  self.conversation[convo_id].append({"role": role, "tool_call_id": function_call_id, "content": message})
150
150
  else:
151
- self.conversation[convo_id].append({"role": "assistant", "content": "I will use tool: " + function_name + ". tool arguments:" + function_arguments + ". I will get the tool call result in the next user response."})
152
- self.conversation[convo_id].append({"role": "user", "content": f"[{function_name} Result]\n\n" + message})
151
+ self.conversation[convo_id].append({"role": "assistant", "content": "I will use tool: " + function_arguments + ". I will get the tool call result in the next user response."})
152
+ self.conversation[convo_id].append({"role": "user", "content": message})
153
153
 
154
154
  else:
155
155
  print('\033[31m')
@@ -176,10 +176,6 @@ class chatgpt(BaseLLM):
176
176
  if type(self.conversation[convo_id][message_index]["content"]) == dict \
177
177
  and type(self.conversation[convo_id][message_index + 1]["content"]) == list:
178
178
  self.conversation[convo_id][message_index]["content"] = [self.conversation[convo_id][message_index]["content"]]
179
- if type(self.conversation[convo_id][message_index]["content"]) == dict \
180
- and type(self.conversation[convo_id][message_index + 1]["content"]) == dict:
181
- self.conversation[convo_id][message_index]["content"] = [self.conversation[convo_id][message_index]["content"]]
182
- self.conversation[convo_id][message_index + 1]["content"] = [self.conversation[convo_id][message_index + 1]["content"]]
183
179
  self.conversation[convo_id][message_index]["content"] += self.conversation[convo_id][message_index + 1]["content"]
184
180
  self.conversation[convo_id].pop(message_index + 1)
185
181
  conversation_len = conversation_len - 1
@@ -407,62 +403,107 @@ class chatgpt(BaseLLM):
407
403
  response_role = "assistant"
408
404
 
409
405
  function_parameter = parse_function_xml(full_response)
410
- if function_parameter['function_name']:
406
+ if function_parameter:
411
407
  need_function_call = True
412
- function_call_name = function_parameter['function_name']
413
- function_full_response = json.dumps(function_parameter['parameter'])
414
- function_call_id = function_parameter['function_name'] + "_tool_call"
415
408
 
416
409
  # 处理函数调用
417
410
  if need_function_call:
418
411
  if self.print_log:
419
412
  print("function_full_response", function_full_response)
420
- function_full_response = check_json(function_full_response)
421
- function_response = ""
422
413
 
423
- if not self.function_calls_counter.get(function_call_name):
424
- self.function_calls_counter[function_call_name] = 1
425
- else:
426
- self.function_calls_counter[function_call_name] += 1
427
-
428
- has_args = safe_get(self.function_call_list, function_call_name, "parameters", "required", default=False)
429
- if self.function_calls_counter[function_call_name] <= self.function_call_max_loop and (function_full_response != "{}" or not has_args):
430
- function_call_max_tokens = self.truncate_limit - 1000
431
- if function_call_max_tokens <= 0:
432
- function_call_max_tokens = int(self.truncate_limit / 2)
433
- if self.print_log:
434
- print("\033[32m function_call", function_call_name, "max token:", function_call_max_tokens, "\033[0m")
414
+ function_response = ""
415
+ # 定义处理单个工具调用的辅助函数
416
+ async def process_single_tool_call(tool_name, tool_args, tool_id):
417
+ nonlocal function_response
435
418
 
436
- # 处理函数调用结果
437
- if is_async:
438
- async for chunk in get_tools_result_async(
439
- function_call_name, function_full_response, function_call_max_tokens,
440
- model or self.engine, chatgpt, kwargs.get('api_key', self.api_key),
441
- self.api_url, use_plugins=False, model=model or self.engine,
442
- add_message=self.add_to_conversation, convo_id=convo_id, language=language
443
- ):
444
- if "function_response:" in chunk:
445
- function_response = chunk.replace("function_response:", "")
446
- else:
447
- yield chunk
419
+ if not self.function_calls_counter.get(tool_name):
420
+ self.function_calls_counter[tool_name] = 1
448
421
  else:
449
- async def run_async():
450
- nonlocal function_response
422
+ self.function_calls_counter[tool_name] += 1
423
+
424
+ tool_response = ""
425
+ has_args = safe_get(self.function_call_list, tool_name, "parameters", "required", default=False)
426
+ if self.function_calls_counter[tool_name] <= self.function_call_max_loop and (tool_args != "{}" or not has_args):
427
+ function_call_max_tokens = self.truncate_limit - 1000
428
+ if function_call_max_tokens <= 0:
429
+ function_call_max_tokens = int(self.truncate_limit / 2)
430
+ if self.print_log:
431
+ print(f"\033[32m function_call {tool_name}, max token: {function_call_max_tokens} \033[0m")
432
+
433
+ # 处理函数调用结果
434
+ if is_async:
451
435
  async for chunk in get_tools_result_async(
452
- function_call_name, function_full_response, function_call_max_tokens,
436
+ tool_name, tool_args, function_call_max_tokens,
453
437
  model or self.engine, chatgpt, kwargs.get('api_key', self.api_key),
454
438
  self.api_url, use_plugins=False, model=model or self.engine,
455
439
  add_message=self.add_to_conversation, convo_id=convo_id, language=language
456
440
  ):
457
- if "function_response:" in chunk:
458
- function_response = chunk.replace("function_response:", "")
459
- else:
441
+ yield chunk
442
+ else:
443
+ async def run_async():
444
+ async for chunk in get_tools_result_async(
445
+ tool_name, tool_args, function_call_max_tokens,
446
+ model or self.engine, chatgpt, kwargs.get('api_key', self.api_key),
447
+ self.api_url, use_plugins=False, model=model or self.engine,
448
+ add_message=self.add_to_conversation, convo_id=convo_id, language=language
449
+ ):
460
450
  yield chunk
461
451
 
462
- for chunk in async_generator_to_sync(run_async()):
463
- yield chunk
464
- else:
465
- function_response = "无法找到相关信息,停止使用 tools"
452
+ for chunk in async_generator_to_sync(run_async()):
453
+ yield chunk
454
+ else:
455
+ tool_response = f"无法找到相关信息,停止使用工具 {tool_name}"
456
+
457
+ yield tool_response
458
+
459
+ # 使用统一的JSON解析逻辑
460
+ try:
461
+ if function_full_response:
462
+ function_parameter = parse_continuous_json(function_full_response, function_call_name)
463
+ except Exception as e:
464
+ print(f"解析JSON失败: {e}")
465
+ # 保持原始工具调用
466
+ tool_calls = [{
467
+ 'function_name': function_call_name,
468
+ 'parameter': function_full_response,
469
+ 'function_call_id': function_call_id
470
+ }]
471
+
472
+ # 统一处理逻辑,将所有情况转换为列表处理
473
+ if isinstance(function_parameter, list) and function_parameter:
474
+ # 多个工具调用
475
+ tool_calls = function_parameter
476
+
477
+ # 处理所有工具调用
478
+ all_responses = []
479
+
480
+ for tool_info in tool_calls:
481
+ tool_name = tool_info['function_name']
482
+ tool_args = json.dumps(tool_info['parameter']) if not isinstance(tool_info['parameter'], str) else tool_info['parameter']
483
+ tool_id = tool_info.get('function_call_id', tool_name + "_tool_call")
484
+
485
+ tool_response = ""
486
+ if is_async:
487
+ async for chunk in process_single_tool_call(tool_name, tool_args, tool_id):
488
+ if isinstance(chunk, str) and "function_response:" in chunk:
489
+ tool_response = chunk.replace("function_response:", "")
490
+ else:
491
+ yield chunk
492
+ else:
493
+ for chunk in async_generator_to_sync(process_single_tool_call(tool_name, tool_args, tool_id)):
494
+ if isinstance(chunk, str) and "function_response:" in chunk:
495
+ tool_response = chunk.replace("function_response:", "")
496
+ else:
497
+ yield chunk
498
+ all_responses.append(f"[{tool_name}({tool_args}) Result]:\n\n{tool_response}")
499
+
500
+ # 合并所有工具响应
501
+ function_response = "\n\n".join(all_responses)
502
+
503
+ # 使用第一个工具的名称和参数作为历史记录
504
+ function_call_name = tool_calls[0]['function_name']
505
+ function_full_response = function_full_response or json.dumps(tool_calls) if not isinstance(tool_calls[0]['parameter'], str) else tool_calls
506
+ function_call_id = tool_calls[0].get('function_call_id', function_call_name + "_tool_call")
466
507
 
467
508
  response_role = "tool"
468
509
 
@@ -457,95 +457,205 @@ class XmlMatcher(Generic[R]):
457
457
  self._update(chunk)
458
458
  return self._pop()
459
459
 
460
- def parse_function_xml(xml_content: str) -> Dict[str, Any]:
460
+ def parse_function_xml(xml_content: str) -> List[Dict[str, Any]]:
461
461
  """
462
- 解析XML格式的函数调用信息,转换为字典格式
462
+ 解析XML格式的函数调用信息,转换为字典数组格式
463
463
 
464
464
  参数:
465
- xml_content: 包含函数调用的XML字符串
465
+ xml_content: 包含一个或多个函数调用的XML字符串
466
466
 
467
467
  返回:
468
- 包含函数名和参数的字典
468
+ 包含所有函数调用信息的字典数组,每个字典包含函数名和参数
469
469
  """
470
- # 首先,找出根标签(函数名)
471
- # function_matcher = XmlMatcher[XmlMatcherResult]("", position=0)
472
- # results = function_matcher.final(xml_content)
470
+ result_functions = []
473
471
 
474
- # 找到第一个匹配的标签
475
- function_name = ""
476
- function_content = ""
472
+ position = 0
473
+ while position < len(xml_content):
474
+ # 寻找下一个开始标签
475
+ tag_start = xml_content.find("<", position)
476
+ if tag_start == -1:
477
+ break # 没有找到更多的标签
478
+
479
+ # 检查是否是XML标签的开始(不是闭合标签)
480
+ if tag_start + 1 < len(xml_content) and xml_content[tag_start + 1] == '/':
481
+ # 这是一个结束标签,跳过
482
+ position = tag_start + 1
483
+ continue
477
484
 
478
- # 寻找第一个开始标签
479
- tag_start = xml_content.find("<")
480
- if tag_start != -1:
481
485
  tag_end = xml_content.find(">", tag_start)
482
- if tag_end != -1:
483
- # 提取标签名(函数名)
484
- tag_content = xml_content[tag_start+1:tag_end].strip()
485
- # 处理可能有属性的情况
486
- function_name = tag_content.split()[0] if " " in tag_content else tag_content
487
-
488
- # 使用XmlMatcher提取该函数标签内的内容
489
- content_matcher = XmlMatcher[XmlMatcherResult](function_name)
490
- match_results = content_matcher.final(xml_content)
491
-
492
- for result in match_results:
493
- if result.matched:
494
- function_content = result.data
495
- break
496
-
497
- # 如果没有找到函数名或内容,返回空结果
498
- if not function_name or not function_content:
499
- return {'function_name': '', 'parameter': {}}
500
-
501
- # 解析参数
502
- parameters = {}
503
- lines = function_content.strip().split('\n')
504
- current_param = None
505
- current_value = []
506
-
507
- for line in lines:
508
- line = line.strip()
509
- if line.startswith('<') and '>' in line:
510
- # 新参数开始
486
+ if tag_end == -1:
487
+ break # 标签未正确关闭
488
+
489
+ # 提取标签名(函数名)
490
+ tag_content = xml_content[tag_start+1:tag_end].strip()
491
+ # 处理可能有属性的情况
492
+ function_name = tag_content.split()[0] if " " in tag_content else tag_content
493
+
494
+ if not function_name:
495
+ position = tag_end + 1
496
+ continue # 空标签名,跳过
497
+
498
+ # 查找整个函数调用的起止范围
499
+ full_start_tag = f"<{function_name}"
500
+ full_end_tag = f"</{function_name}>"
501
+
502
+ # 从当前位置找到开始标签
503
+ start_pos = xml_content.find(full_start_tag, position)
504
+ if start_pos == -1:
505
+ position = tag_end + 1
506
+ continue
507
+
508
+ # 找到对应的结束标签
509
+ end_pos = xml_content.find(full_end_tag, start_pos)
510
+ if end_pos == -1:
511
+ # 没有找到结束标签,可能是未闭合标签
512
+ position = tag_end + 1
513
+ continue
514
+
515
+ # 计算整个函数标签内容,包括开始和结束标签
516
+ end_pos_complete = end_pos + len(full_end_tag)
517
+ full_tag_content = xml_content[start_pos:end_pos_complete]
518
+
519
+ # 使用XmlMatcher提取该函数标签内的内容
520
+ content_matcher = XmlMatcher[XmlMatcherResult](function_name)
521
+ match_results = content_matcher.final(full_tag_content)
522
+
523
+ function_content = ""
524
+ for result in match_results:
525
+ if result.matched:
526
+ function_content = result.data
527
+ break
528
+
529
+ # 解析参数
530
+ parameters = {}
531
+ if function_content:
532
+ lines = function_content.strip().split('\n')
533
+ current_param = None
534
+ current_value = []
535
+
536
+ for line in lines:
537
+ line = line.strip()
538
+ if line.startswith('<') and '>' in line and not line.startswith('</'):
539
+ # 新参数开始
540
+ if current_param and current_value:
541
+ # 保存之前的参数
542
+ parameters[current_param] = '\n'.join(current_value).strip()
543
+ current_value = []
544
+
545
+ # 提取参数名
546
+ param_start = line.find('<') + 1
547
+ param_end = line.find('>', param_start)
548
+ if param_end != -1:
549
+ param = line[param_start:param_end]
550
+ # 检查是否是闭合标签
551
+ if not param.startswith('/'):
552
+ current_param = param
553
+ # 检查是否在同一行有值
554
+ rest = line[param_end+1:]
555
+ if rest and not rest.startswith('</'):
556
+ current_value.append(rest)
557
+ elif line.startswith('</') and '>' in line:
558
+ # 参数结束
559
+ if current_param and current_value:
560
+ param_end_tag = f"</{current_param}>"
561
+ if line.strip() == param_end_tag:
562
+ parameters[current_param] = '\n'.join(current_value).strip()
563
+ current_param = None
564
+ current_value = []
565
+ elif current_param:
566
+ # 继续收集当前参数的值
567
+ current_value.append(line)
568
+
569
+ # 处理最后一个参数
511
570
  if current_param and current_value:
512
- # 保存之前的参数
513
571
  parameters[current_param] = '\n'.join(current_value).strip()
514
- current_value = []
515
-
516
- # 提取参数名
517
- param_start = line.find('<') + 1
518
- param_end = line.find('>', param_start)
519
- if param_end != -1:
520
- param = line[param_start:param_end]
521
- # 检查是否是闭合标签
522
- if param.startswith('/'):
523
- if param[1:] == current_param:
524
- current_param = None
525
- else:
526
- current_param = param
527
- # 检查是否在同一行有值
528
- rest = line[param_end+1:]
529
- if rest and not rest.startswith('</'):
530
- current_value.append(rest)
531
- elif current_param:
532
- # 继续收集当前参数的值
533
- current_value.append(line)
534
-
535
- # 处理最后一个参数
536
- if current_param and current_value:
537
- parameters[current_param] = '\n'.join(current_value).strip()
538
-
539
- # 清理参数值中可能的结束标签
540
- for param, value in parameters.items():
541
- end_tag = f'</{param}>'
542
- if value.endswith(end_tag):
543
- parameters[param] = value[:-len(end_tag)].strip()
544
-
545
- return {
546
- 'function_name': function_name,
547
- 'parameter': parameters
548
- }
572
+
573
+ # 清理参数值中可能的结束标签
574
+ for param, value in parameters.items():
575
+ end_tag = f'</{param}>'
576
+ if value.endswith(end_tag):
577
+ parameters[param] = value[:-len(end_tag)].strip()
578
+
579
+ # 将解析的函数添加到结果数组
580
+ result_functions.append({
581
+ 'function_name': function_name,
582
+ 'parameter': parameters
583
+ })
584
+
585
+ # 更新位置到当前标签之后,继续查找下一个函数
586
+ position = end_pos_complete
587
+
588
+ return result_functions
589
+
590
+ def parse_continuous_json(json_str: str, function_name: str = "") -> List[Dict[str, Any]]:
591
+ """
592
+ 解析JSON字符串,无论是单个JSON对象还是多个连续的JSON对象
593
+ 都能正确解析并转换为结构化的函数调用格式列表
594
+
595
+ Args:
596
+ json_str: JSON字符串,可能是单个JSON对象或多个连续JSON对象
597
+ function_name: 函数名称,默认为空字符串
598
+
599
+ Returns:
600
+ 包含函数调用信息的字典列表
601
+ """
602
+ if not json_str or not json_str.strip():
603
+ return []
604
+
605
+ # 尝试直接解析为单个JSON
606
+ try:
607
+ json_obj = json.loads(json_str)
608
+ tool_id = function_name + "_single" if function_name else "tool_single"
609
+ return [{
610
+ 'function_name': function_name or "default_function",
611
+ 'parameter': json_obj,
612
+ 'function_call_id': tool_id
613
+ }]
614
+ except json.JSONDecodeError:
615
+ # 如果不是单个JSON,尝试解析为连续JSON
616
+ pass
617
+
618
+ result = []
619
+ idx = 0
620
+ length = len(json_str)
621
+
622
+ while idx < length:
623
+ # 找到JSON对象的开始
624
+ if json_str[idx] != '{':
625
+ idx += 1
626
+ continue
627
+
628
+ # 跟踪括号的平衡
629
+ balance = 1
630
+ start = idx
631
+ idx += 1
632
+
633
+ # 寻找匹配的右括号
634
+ while idx < length and balance > 0:
635
+ if json_str[idx] == '{':
636
+ balance += 1
637
+ elif json_str[idx] == '}':
638
+ balance -= 1
639
+ idx += 1
640
+
641
+ if balance == 0:
642
+ # 提取出一个完整的JSON对象
643
+ json_obj_str = json_str[start:idx]
644
+ try:
645
+ # 解析JSON对象
646
+ json_obj = json.loads(json_obj_str)
647
+ # 构造函数调用信息
648
+ tool_id = function_name + "_" + str(len(result)) if function_name else "tool_" + str(len(result))
649
+ result.append({
650
+ 'function_name': function_name or "default_function",
651
+ 'parameter': json_obj,
652
+ 'function_call_id': tool_id
653
+ })
654
+ except json.JSONDecodeError:
655
+ # 忽略解析错误
656
+ pass
657
+
658
+ return result
549
659
 
550
660
  if __name__ == "__main__":
551
661
  os.system("clear")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aient
3
- Version: 1.0.42
3
+ Version: 1.0.43
4
4
  Summary: Aient: The Awakening of Agent.
5
5
  Description-Content-Type: text/markdown
6
6
  License-File: LICENSE
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes