aient 1.0.41__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.
- {aient-1.0.41/src/aient.egg-info → aient-1.0.43}/PKG-INFO +1 -1
- {aient-1.0.41 → aient-1.0.43}/setup.py +1 -1
- {aient-1.0.41 → aient-1.0.43}/src/aient/models/chatgpt.py +89 -44
- {aient-1.0.41 → aient-1.0.43}/src/aient/utils/scripts.py +188 -78
- {aient-1.0.41 → aient-1.0.43/src/aient.egg-info}/PKG-INFO +1 -1
- {aient-1.0.41 → aient-1.0.43}/LICENSE +0 -0
- {aient-1.0.41 → aient-1.0.43}/MANIFEST.in +0 -0
- {aient-1.0.41 → aient-1.0.43}/README.md +0 -0
- {aient-1.0.41 → aient-1.0.43}/setup.cfg +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient/__init__.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient/core/.git +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient/core/__init__.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient/core/log_config.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient/core/models.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient/core/request.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient/core/response.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient/core/test/test_base_api.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient/core/test/test_image.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient/core/test/test_payload.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient/core/utils.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient/models/__init__.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient/models/audio.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient/models/base.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient/models/claude.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient/models/duckduckgo.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient/models/gemini.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient/models/groq.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient/models/vertex.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient/plugins/__init__.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient/plugins/arXiv.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient/plugins/config.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient/plugins/image.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient/plugins/registry.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient/plugins/run_python.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient/plugins/today.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient/plugins/websearch.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient/utils/__init__.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient/utils/prompt.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient.egg-info/SOURCES.txt +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient.egg-info/dependency_links.txt +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient.egg-info/requires.txt +0 -0
- {aient-1.0.41 → aient-1.0.43}/src/aient.egg-info/top_level.txt +0 -0
- {aient-1.0.41 → aient-1.0.43}/test/test.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/test/test_API.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/test/test_Deepbricks.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/test/test_Web_crawler.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/test/test_aiwaves.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/test/test_aiwaves_arxiv.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/test/test_ask_gemini.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/test/test_class.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/test/test_claude.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/test/test_claude_zh_char.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/test/test_ddg_search.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/test/test_download_pdf.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/test/test_gemini.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/test/test_get_token_dict.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/test/test_google_search.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/test/test_jieba.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/test/test_json.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/test/test_langchain_search_old.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/test/test_logging.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/test/test_ollama.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/test/test_plugin.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/test/test_py_run.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/test/test_requests.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/test/test_search.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/test/test_tikitoken.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/test/test_token.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/test/test_url.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/test/test_whisper.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/test/test_wildcard.py +0 -0
- {aient-1.0.41 → aient-1.0.43}/test/test_yjh.py +0 -0
@@ -4,7 +4,7 @@ from setuptools import setup, find_packages
|
|
4
4
|
|
5
5
|
setup(
|
6
6
|
name="aient",
|
7
|
-
version="1.0.
|
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
|
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: " +
|
152
|
-
self.conversation[convo_id].append({"role": "user", "content":
|
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')
|
@@ -403,62 +403,107 @@ class chatgpt(BaseLLM):
|
|
403
403
|
response_role = "assistant"
|
404
404
|
|
405
405
|
function_parameter = parse_function_xml(full_response)
|
406
|
-
if function_parameter
|
406
|
+
if function_parameter:
|
407
407
|
need_function_call = True
|
408
|
-
function_call_name = function_parameter['function_name']
|
409
|
-
function_full_response = json.dumps(function_parameter['parameter'])
|
410
|
-
function_call_id = function_parameter['function_name'] + "_tool_call"
|
411
408
|
|
412
409
|
# 处理函数调用
|
413
410
|
if need_function_call:
|
414
411
|
if self.print_log:
|
415
412
|
print("function_full_response", function_full_response)
|
416
|
-
function_full_response = check_json(function_full_response)
|
417
|
-
function_response = ""
|
418
|
-
|
419
|
-
if not self.function_calls_counter.get(function_call_name):
|
420
|
-
self.function_calls_counter[function_call_name] = 1
|
421
|
-
else:
|
422
|
-
self.function_calls_counter[function_call_name] += 1
|
423
413
|
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
function_call_max_tokens = int(self.truncate_limit / 2)
|
429
|
-
if self.print_log:
|
430
|
-
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
|
431
418
|
|
432
|
-
|
433
|
-
|
434
|
-
async for chunk in get_tools_result_async(
|
435
|
-
function_call_name, function_full_response, function_call_max_tokens,
|
436
|
-
model or self.engine, chatgpt, kwargs.get('api_key', self.api_key),
|
437
|
-
self.api_url, use_plugins=False, model=model or self.engine,
|
438
|
-
add_message=self.add_to_conversation, convo_id=convo_id, language=language
|
439
|
-
):
|
440
|
-
if "function_response:" in chunk:
|
441
|
-
function_response = chunk.replace("function_response:", "")
|
442
|
-
else:
|
443
|
-
yield chunk
|
419
|
+
if not self.function_calls_counter.get(tool_name):
|
420
|
+
self.function_calls_counter[tool_name] = 1
|
444
421
|
else:
|
445
|
-
|
446
|
-
|
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:
|
447
435
|
async for chunk in get_tools_result_async(
|
448
|
-
|
436
|
+
tool_name, tool_args, function_call_max_tokens,
|
449
437
|
model or self.engine, chatgpt, kwargs.get('api_key', self.api_key),
|
450
438
|
self.api_url, use_plugins=False, model=model or self.engine,
|
451
439
|
add_message=self.add_to_conversation, convo_id=convo_id, language=language
|
452
440
|
):
|
453
|
-
|
454
|
-
|
455
|
-
|
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
|
+
):
|
456
450
|
yield chunk
|
457
451
|
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
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")
|
462
507
|
|
463
508
|
response_role = "tool"
|
464
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:
|
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
|
-
|
476
|
-
|
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
|
483
|
-
#
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
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
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
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")
|
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
|
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
|