xiaozhi-sdk 0.2.0__tar.gz → 0.2.1__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.
Potentially problematic release.
This version of xiaozhi-sdk might be problematic. Click here for more details.
- {xiaozhi_sdk-0.2.0/xiaozhi_sdk.egg-info → xiaozhi_sdk-0.2.1}/PKG-INFO +1 -1
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/tests/test_xiaozhi.py +2 -2
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/xiaozhi_sdk/__init__.py +1 -1
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/xiaozhi_sdk/mcp.py +1 -1
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/xiaozhi_sdk/utils/mcp_tool.py +2 -94
- xiaozhi_sdk-0.2.1/xiaozhi_sdk/utils/tool_func.py +92 -0
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1/xiaozhi_sdk.egg-info}/PKG-INFO +1 -1
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/xiaozhi_sdk.egg-info/SOURCES.txt +2 -1
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/LICENSE +0 -0
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/MANIFEST.in +0 -0
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/README.md +0 -0
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/file/audio/greet.wav +0 -0
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/file/audio/play_music.wav +0 -0
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/file/audio/say_hello.wav +0 -0
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/file/audio/take_photo.wav +0 -0
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/file/image/leijun.jpg +0 -0
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/file/opus/linux-arm64-libopus.so +0 -0
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/file/opus/linux-x64-libopus.so +0 -0
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/file/opus/macos-arm64-libopus.dylib +0 -0
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/file/opus/macos-x64-libopus.dylib +0 -0
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/file/opus/windows-opus.dll +0 -0
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/pyproject.toml +0 -0
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/setup.cfg +0 -0
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/tests/test_iot.py +0 -0
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/tests/test_pic.py +0 -0
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/tests/test_wake_word.py +0 -0
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/xiaozhi_sdk/__main__.py +0 -0
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/xiaozhi_sdk/cli.py +0 -0
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/xiaozhi_sdk/config.py +0 -0
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/xiaozhi_sdk/core.py +0 -0
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/xiaozhi_sdk/iot.py +0 -0
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/xiaozhi_sdk/opus.py +0 -0
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/xiaozhi_sdk/utils/__init__.py +0 -0
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/xiaozhi_sdk.egg-info/dependency_links.txt +0 -0
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/xiaozhi_sdk.egg-info/requires.txt +0 -0
- {xiaozhi_sdk-0.2.0 → xiaozhi_sdk-0.2.1}/xiaozhi_sdk.egg-info/top_level.txt +0 -0
|
@@ -54,13 +54,13 @@ def mcp_tool_func():
|
|
|
54
54
|
def mcp_set_volume(data) -> tuple[dict, bool]:
|
|
55
55
|
return {}, False
|
|
56
56
|
|
|
57
|
-
from xiaozhi_sdk.utils.mcp_tool import take_photo, get_device_status, set_volume
|
|
57
|
+
from xiaozhi_sdk.utils.mcp_tool import take_photo, get_device_status, set_volume, search_custom_music, play_custom_music
|
|
58
58
|
|
|
59
59
|
take_photo["tool_func"] = mcp_take_photo
|
|
60
60
|
get_device_status["tool_func"] = mcp_get_device_status
|
|
61
61
|
set_volume["tool_func"] = mcp_set_volume
|
|
62
62
|
|
|
63
|
-
return [take_photo, get_device_status, set_volume]
|
|
63
|
+
return [take_photo, get_device_status, set_volume, search_custom_music, play_custom_music]
|
|
64
64
|
|
|
65
65
|
|
|
66
66
|
async def message_handler_callback(message):
|
|
@@ -1,96 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import random
|
|
3
|
-
|
|
4
|
-
import aiohttp
|
|
5
|
-
import numpy as np
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
async def async_search_custom_music(data) -> tuple[dict, bool]:
|
|
9
|
-
search_url = f"https://music-api.gdstudio.xyz/api.php?types=search&name={data['music_name']}&count=100&pages=1"
|
|
10
|
-
|
|
11
|
-
# 为搜索请求设置 10 秒超时
|
|
12
|
-
timeout = aiohttp.ClientTimeout(total=10)
|
|
13
|
-
async with aiohttp.ClientSession(timeout=timeout) as session:
|
|
14
|
-
async with session.get(search_url) as response:
|
|
15
|
-
response_json = await response.json()
|
|
16
|
-
|
|
17
|
-
music_list = []
|
|
18
|
-
first_music_list = []
|
|
19
|
-
other_music_list1 = []
|
|
20
|
-
other_music_list2 = []
|
|
21
|
-
for line in response_json:
|
|
22
|
-
if data.get("author_name") and data["author_name"] in line["artist"][0]:
|
|
23
|
-
first_music_list.append(line)
|
|
24
|
-
elif data.get("author_name") and (data["author_name"] in line["artist"] or data["author_name"] in line["name"]):
|
|
25
|
-
other_music_list1.append(line)
|
|
26
|
-
else:
|
|
27
|
-
other_music_list2.append(line)
|
|
28
|
-
|
|
29
|
-
if len(first_music_list) <= 10:
|
|
30
|
-
music_list = first_music_list
|
|
31
|
-
random.shuffle(other_music_list2)
|
|
32
|
-
music_list = music_list + other_music_list1[: 20 - len(music_list)]
|
|
33
|
-
music_list = music_list + other_music_list2[: 20 - len(music_list)]
|
|
34
|
-
|
|
35
|
-
# print(data)
|
|
36
|
-
# print("找到音乐,数量:", len(first_music_list), len(music_list))
|
|
37
|
-
|
|
38
|
-
if not music_list:
|
|
39
|
-
return {}, False
|
|
40
|
-
return {"message": "已找到歌曲", "music_list": music_list}, False
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
async def _get_random_music_info(id_list: list) -> dict:
|
|
44
|
-
timeout = aiohttp.ClientTimeout(total=10)
|
|
45
|
-
async with aiohttp.ClientSession(timeout=timeout) as session:
|
|
46
|
-
random.shuffle(id_list)
|
|
47
|
-
|
|
48
|
-
for music_id in id_list:
|
|
49
|
-
url = f"https://music-api.gdstudio.xyz/api.php?types=url&id={music_id}&br=128"
|
|
50
|
-
async with session.get(url) as response:
|
|
51
|
-
res_json = await response.json()
|
|
52
|
-
if res_json.get("url"):
|
|
53
|
-
break
|
|
54
|
-
|
|
55
|
-
return res_json
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
async def async_mcp_play_music(data) -> tuple[list, bool]:
|
|
59
|
-
try:
|
|
60
|
-
from pydub import AudioSegment
|
|
61
|
-
except ImportError:
|
|
62
|
-
return [], True
|
|
63
|
-
|
|
64
|
-
id_list = data["id_list"]
|
|
65
|
-
res_json = await _get_random_music_info(id_list)
|
|
66
|
-
|
|
67
|
-
if not res_json:
|
|
68
|
-
return [], False
|
|
69
|
-
|
|
70
|
-
pcm_list = []
|
|
71
|
-
buffer = io.BytesIO()
|
|
72
|
-
# 为下载音乐文件设置 60 秒超时(音乐文件可能比较大)
|
|
73
|
-
download_timeout = aiohttp.ClientTimeout(total=60)
|
|
74
|
-
async with aiohttp.ClientSession(timeout=download_timeout) as session:
|
|
75
|
-
async with session.get(res_json["url"]) as resp:
|
|
76
|
-
async for chunk in resp.content.iter_chunked(1024):
|
|
77
|
-
buffer.write(chunk)
|
|
78
|
-
|
|
79
|
-
buffer.seek(0)
|
|
80
|
-
audio = AudioSegment.from_mp3(buffer)
|
|
81
|
-
audio = audio.set_frame_rate(16000).set_channels(1).set_sample_width(2) # 2 bytes = 16 bits
|
|
82
|
-
pcm_data = audio.raw_data
|
|
83
|
-
|
|
84
|
-
chunk_size = 960 * 2
|
|
85
|
-
for i in range(0, len(pcm_data), chunk_size):
|
|
86
|
-
chunk = pcm_data[i : i + chunk_size]
|
|
87
|
-
|
|
88
|
-
if chunk: # 确保不添加空块
|
|
89
|
-
chunk = np.frombuffer(chunk, dtype=np.int16)
|
|
90
|
-
pcm_list.extend(chunk)
|
|
91
|
-
|
|
92
|
-
return pcm_list, False
|
|
93
|
-
|
|
1
|
+
from xiaozhi_sdk.utils.tool_func import async_mcp_play_music, async_search_custom_music
|
|
94
2
|
|
|
95
3
|
search_custom_music = {
|
|
96
4
|
"name": "search_custom_music",
|
|
@@ -164,7 +72,7 @@ set_theme = {
|
|
|
164
72
|
|
|
165
73
|
take_photo = {
|
|
166
74
|
"name": "take_photo",
|
|
167
|
-
"description": "Use this tool when the user asks you to look at something, take a picture, or solve a problem based on what is captured.\nArgs:\n`question`: A clear question or task you want to ask about the captured photo (e.g., identify objects, read text, explain content, or solve a math/logic problem).\nReturn:\n A JSON object that provides the photo information, including answers, explanations, or problem-solving results if applicable.",
|
|
75
|
+
"description": "Have visual ability, Use this tool when the user asks you to look at something, take a picture, or solve a problem based on what is captured.\nArgs:\n`question`: A clear question or task you want to ask about the captured photo (e.g., identify objects, read text, explain content, or solve a math/logic problem).\nReturn:\n A JSON object that provides the photo information, including answers, explanations, or problem-solving results if applicable.",
|
|
168
76
|
"inputSchema": {
|
|
169
77
|
"type": "object",
|
|
170
78
|
"properties": {"question": {"type": "string"}},
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import io
|
|
2
|
+
import random
|
|
3
|
+
|
|
4
|
+
import aiohttp
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
async def async_search_custom_music(data) -> tuple[dict, bool]:
|
|
9
|
+
search_url = f"https://music-api.gdstudio.xyz/api.php?types=search&name={data['music_name']}&count=100&pages=1"
|
|
10
|
+
|
|
11
|
+
# 为搜索请求设置 10 秒超时
|
|
12
|
+
timeout = aiohttp.ClientTimeout(total=10)
|
|
13
|
+
async with aiohttp.ClientSession(timeout=timeout) as session:
|
|
14
|
+
async with session.get(search_url) as response:
|
|
15
|
+
response_json = await response.json()
|
|
16
|
+
|
|
17
|
+
music_list = []
|
|
18
|
+
first_music_list = []
|
|
19
|
+
other_music_list1 = []
|
|
20
|
+
other_music_list2 = []
|
|
21
|
+
for line in response_json:
|
|
22
|
+
if data.get("author_name") and data["author_name"] in line["artist"][0]:
|
|
23
|
+
first_music_list.append(line)
|
|
24
|
+
elif data.get("author_name") and (data["author_name"] in line["artist"] or data["author_name"] in line["name"]):
|
|
25
|
+
other_music_list1.append(line)
|
|
26
|
+
else:
|
|
27
|
+
other_music_list2.append(line)
|
|
28
|
+
|
|
29
|
+
if len(first_music_list) <= 10:
|
|
30
|
+
music_list = first_music_list
|
|
31
|
+
random.shuffle(other_music_list2)
|
|
32
|
+
music_list = music_list + other_music_list1[: 20 - len(music_list)]
|
|
33
|
+
music_list = music_list + other_music_list2[: 20 - len(music_list)]
|
|
34
|
+
|
|
35
|
+
# print(data)
|
|
36
|
+
# print("找到音乐,数量:", len(first_music_list), len(music_list))
|
|
37
|
+
|
|
38
|
+
if not music_list:
|
|
39
|
+
return {}, False
|
|
40
|
+
return {"message": "已找到歌曲", "music_list": music_list}, False
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
async def _get_random_music_info(id_list: list) -> dict:
|
|
44
|
+
timeout = aiohttp.ClientTimeout(total=10)
|
|
45
|
+
async with aiohttp.ClientSession(timeout=timeout) as session:
|
|
46
|
+
random.shuffle(id_list)
|
|
47
|
+
|
|
48
|
+
for music_id in id_list:
|
|
49
|
+
url = f"https://music-api.gdstudio.xyz/api.php?types=url&id={music_id}&br=128"
|
|
50
|
+
async with session.get(url) as response:
|
|
51
|
+
res_json = await response.json()
|
|
52
|
+
if res_json.get("url"):
|
|
53
|
+
break
|
|
54
|
+
|
|
55
|
+
return res_json
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
async def async_mcp_play_music(data) -> tuple[list, bool]:
|
|
59
|
+
try:
|
|
60
|
+
from pydub import AudioSegment
|
|
61
|
+
except ImportError:
|
|
62
|
+
return [], True
|
|
63
|
+
|
|
64
|
+
id_list = data["id_list"]
|
|
65
|
+
res_json = await _get_random_music_info(id_list)
|
|
66
|
+
|
|
67
|
+
if not res_json:
|
|
68
|
+
return [], False
|
|
69
|
+
|
|
70
|
+
pcm_list = []
|
|
71
|
+
buffer = io.BytesIO()
|
|
72
|
+
# 为下载音乐文件设置 60 秒超时(音乐文件可能比较大)
|
|
73
|
+
download_timeout = aiohttp.ClientTimeout(total=60)
|
|
74
|
+
async with aiohttp.ClientSession(timeout=download_timeout) as session:
|
|
75
|
+
async with session.get(res_json["url"]) as resp:
|
|
76
|
+
async for chunk in resp.content.iter_chunked(1024):
|
|
77
|
+
buffer.write(chunk)
|
|
78
|
+
|
|
79
|
+
buffer.seek(0)
|
|
80
|
+
audio = AudioSegment.from_mp3(buffer)
|
|
81
|
+
audio = audio.set_frame_rate(16000).set_channels(1).set_sample_width(2) # 2 bytes = 16 bits
|
|
82
|
+
pcm_data = audio.raw_data
|
|
83
|
+
|
|
84
|
+
chunk_size = 960 * 2
|
|
85
|
+
for i in range(0, len(pcm_data), chunk_size):
|
|
86
|
+
chunk = pcm_data[i : i + chunk_size]
|
|
87
|
+
|
|
88
|
+
if chunk: # 确保不添加空块
|
|
89
|
+
chunk = np.frombuffer(chunk, dtype=np.int16)
|
|
90
|
+
pcm_list.extend(chunk)
|
|
91
|
+
|
|
92
|
+
return pcm_list, False
|
|
@@ -40,4 +40,5 @@ xiaozhi_sdk/../file/opus/macos-arm64-libopus.dylib
|
|
|
40
40
|
xiaozhi_sdk/../file/opus/macos-x64-libopus.dylib
|
|
41
41
|
xiaozhi_sdk/../file/opus/windows-opus.dll
|
|
42
42
|
xiaozhi_sdk/utils/__init__.py
|
|
43
|
-
xiaozhi_sdk/utils/mcp_tool.py
|
|
43
|
+
xiaozhi_sdk/utils/mcp_tool.py
|
|
44
|
+
xiaozhi_sdk/utils/tool_func.py
|
|
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
|