From c795403c5bd82f25d6cc4bbcf20f3031adb4a0be Mon Sep 17 00:00:00 2001 From: yuhongxiao Date: Sat, 12 Jul 2025 16:52:00 +0800 Subject: [PATCH 01/12] =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=8F=98=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/integration_tests/test_audio.py | 1 + zhipuai/api_resource/audio/audio.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/tests/integration_tests/test_audio.py b/tests/integration_tests/test_audio.py index e4891b5..ded987a 100644 --- a/tests/integration_tests/test_audio.py +++ b/tests/integration_tests/test_audio.py @@ -15,6 +15,7 @@ def test_audio_speech(logging_conf): model='cogtts', input='你好呀,欢迎来到智谱开放平台', voice='female', + stream=False, response_format='wav', ) response.stream_to_file(speech_file_path) diff --git a/zhipuai/api_resource/audio/audio.py b/zhipuai/api_resource/audio/audio.py index aa987cb..fcf252a 100644 --- a/zhipuai/api_resource/audio/audio.py +++ b/zhipuai/api_resource/audio/audio.py @@ -46,6 +46,7 @@ def speech( sensitive_word_check: Optional[SensitiveWordCheckRequest] | NotGiven = NOT_GIVEN, request_id: str = None, user_id: str = None, + stream: bool = False, extra_headers: Headers | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, @@ -55,6 +56,7 @@ def speech( "model": model, "input": input, "voice": voice, + "stream": stream, "response_format": response_format, "sensitive_word_check": sensitive_word_check, "request_id": request_id, From 538fe6e58abe7e3fafbcd339bc90d9fbc0eac67a Mon Sep 17 00:00:00 2001 From: yuhongxiao Date: Mon, 14 Jul 2025 18:32:18 +0800 Subject: [PATCH 02/12] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E9=9F=B3=E9=A2=91?= =?UTF-8?q?=E5=90=88=E6=88=90=E6=B5=81=E5=BC=8F=E8=BF=94=E5=9B=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 4 +-- tests/integration_tests/test_audio.py | 23 +++++++++++++++- zhipuai/api_resource/audio/audio.py | 11 +++++--- zhipuai/types/audio/audio_speech_chunk.py | 32 +++++++++++++++++++++++ 4 files changed, 63 insertions(+), 7 deletions(-) create mode 100644 zhipuai/types/audio/audio_speech_chunk.py diff --git a/pyproject.toml b/pyproject.toml index a8b5077..33cd6e0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -84,7 +84,7 @@ build-backend = "poetry.core.masonry.api" # # https://github.com/tophat/syrupy # --snapshot-warn-unused Prints a warning on unused snapshots rather than fail the test suite. -addopts = "--strict-markers --strict-config --durations=5 --snapshot-warn-unused -svv" +addopts = "--strict-markers --strict-config --durations=5 -svv" # Registering custom markers. # https://docs.pytest.org/en/7.1.x/example/markers.html#registering-markers markers = [ @@ -92,7 +92,7 @@ markers = [ "scheduled: mark tests to run in scheduled testing", "compile: mark placeholder test used to compile integration tests without running them" ] -asyncio_mode = "auto" +# asyncio_mode = "auto" # https://python-poetry.org/docs/repositories/ diff --git a/tests/integration_tests/test_audio.py b/tests/integration_tests/test_audio.py index ded987a..63a62de 100644 --- a/tests/integration_tests/test_audio.py +++ b/tests/integration_tests/test_audio.py @@ -14,7 +14,7 @@ def test_audio_speech(logging_conf): response = client.audio.speech( model='cogtts', input='你好呀,欢迎来到智谱开放平台', - voice='female', + voice='tongtong', stream=False, response_format='wav', ) @@ -27,6 +27,27 @@ def test_audio_speech(logging_conf): except zhipuai.core._errors.APIStatusError as err: print(err) +def test_audio_speech_streaming(logging_conf): + logging.config.dictConfig(logging_conf) # type: ignore + client = ZhipuAI() # 填写您自己的APIKey + try: + response = client.audio.speech( + model='cogtts', + input='你好呀,欢迎来到智谱开放平台', + voice='tongtong', + stream=True, + response_format='wav', + ) + for item in response: + print(item) + + except zhipuai.core._errors.APIRequestFailedError as err: + print(err) + except zhipuai.core._errors.APIInternalError as err: + print(err) + except zhipuai.core._errors.APIStatusError as err: + print(err) + def test_audio_customization(logging_conf): logging.config.dictConfig(logging_conf) diff --git a/zhipuai/api_resource/audio/audio.py b/zhipuai/api_resource/audio/audio.py index fcf252a..d916048 100644 --- a/zhipuai/api_resource/audio/audio.py +++ b/zhipuai/api_resource/audio/audio.py @@ -9,7 +9,7 @@ from zhipuai.types.audio import AudioSpeechParams from ...types.audio import audio_customization_param -from zhipuai.core import BaseAPI, maybe_transform +from zhipuai.core import BaseAPI, maybe_transform, StreamResponse from zhipuai.core import NOT_GIVEN, Body, Headers, NotGiven, FileTypes from zhipuai.core import _legacy_response @@ -20,6 +20,7 @@ make_request_options, ) from zhipuai.core import deepcopy_minimal +from ...types.audio.audio_speech_chunk import AudioSpeechChunk if TYPE_CHECKING: from zhipuai._client import ZhipuAI @@ -50,7 +51,7 @@ def speech( extra_headers: Headers | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> _legacy_response.HttpxBinaryResponseContent: + ) -> _legacy_response.HttpxBinaryResponseContent | StreamResponse[AudioSpeechChunk]: body = deepcopy_minimal( { "model": model, @@ -65,11 +66,13 @@ def speech( ) return self._post( "/audio/speech", - body=maybe_transform(body, AudioSpeechParams), + body=body, options=make_request_options( extra_headers=extra_headers, extra_body=extra_body, timeout=timeout ), - cast_type=_legacy_response.HttpxBinaryResponseContent + cast_type=_legacy_response.HttpxBinaryResponseContent, + stream= stream or False, + stream_cls=StreamResponse[AudioSpeechChunk] ) def customization( diff --git a/zhipuai/types/audio/audio_speech_chunk.py b/zhipuai/types/audio/audio_speech_chunk.py new file mode 100644 index 0000000..7788d9d --- /dev/null +++ b/zhipuai/types/audio/audio_speech_chunk.py @@ -0,0 +1,32 @@ +from typing import List, Optional, Dict, Any + +from ...core import BaseModel + +__all__ = [ + "AudioSpeechChunk", + "AudioError", + "AudioSpeechChoice", + "AudioSpeechDelta" +] + + +class AudioSpeechDelta(BaseModel): + content: Optional[str] = None + role: Optional[str] = None + + +class AudioSpeechChoice(BaseModel): + delta: AudioSpeechDelta + finish_reason: Optional[str] = None + index: int + +class AudioError: + code: Optional[str] = None + message: Optional[str] = None + + +class AudioSpeechChunk(BaseModel): + choices: List[AudioSpeechChoice] + request_id: Optional[str] = None + created: Optional[int] = None + error: Optional[AudioError] = None \ No newline at end of file From f9e6c69108f7d6e65d4880793fa4f95c18e374a5 Mon Sep 17 00:00:00 2001 From: yuhongxiao Date: Mon, 14 Jul 2025 18:39:24 +0800 Subject: [PATCH 03/12] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B5=81=E5=A4=B1?= =?UTF-8?q?=E9=9F=B3=E9=A2=91=E5=90=88=E6=88=90=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 33cd6e0..a8b5077 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -84,7 +84,7 @@ build-backend = "poetry.core.masonry.api" # # https://github.com/tophat/syrupy # --snapshot-warn-unused Prints a warning on unused snapshots rather than fail the test suite. -addopts = "--strict-markers --strict-config --durations=5 -svv" +addopts = "--strict-markers --strict-config --durations=5 --snapshot-warn-unused -svv" # Registering custom markers. # https://docs.pytest.org/en/7.1.x/example/markers.html#registering-markers markers = [ @@ -92,7 +92,7 @@ markers = [ "scheduled: mark tests to run in scheduled testing", "compile: mark placeholder test used to compile integration tests without running them" ] -# asyncio_mode = "auto" +asyncio_mode = "auto" # https://python-poetry.org/docs/repositories/ From 7d79be9d30e3bac7fb51d671c01e35a44838bf6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=98=89=E7=90=A6?= Date: Tue, 15 Jul 2025 14:02:00 +0800 Subject: [PATCH 04/12] =?UTF-8?q?feat:=E8=B1=86=E7=A5=9E=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=EF=BC=8C=E6=B7=BB=E5=8A=A0=E9=9F=B3=E8=89=B2=EF=BC=8C=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E9=9F=B3=E8=89=B2=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/integration_tests/test_tts.py | 33 ++++++++++++++++++++++++++++ zhipuai/api_resource/tts/__init__.py | 1 + zhipuai/api_resource/tts/tts.py | 32 +++++++++++++++++++++++++++ zhipuai/types/tts/__init__.py | 1 + zhipuai/types/tts/voice.py | 24 ++++++++++++++++++++ 5 files changed, 91 insertions(+) create mode 100644 tests/integration_tests/test_tts.py create mode 100644 zhipuai/api_resource/tts/__init__.py create mode 100644 zhipuai/api_resource/tts/tts.py create mode 100644 zhipuai/types/tts/__init__.py create mode 100644 zhipuai/types/tts/voice.py diff --git a/tests/integration_tests/test_tts.py b/tests/integration_tests/test_tts.py new file mode 100644 index 0000000..41f260b --- /dev/null +++ b/tests/integration_tests/test_tts.py @@ -0,0 +1,33 @@ +import os +import pytest +from zhipuai.api_resource.tts import TTSApi + +# 请根据实际情况填写 +BASE_URL_LIST = "http://localhost:9203" +BASE_URL_ADD = "http://localhost:9203" +API_TOKEN = "eyJhbGciOiJIUzUxMiJ9.eyJ1c2VyX3R5cGUiOiJTRVJWSUNFIiwidXNlcl9pZCI6MywiYXBpX2tleSI6IjM0ZDhiM2ExNDQ2OTQ0MmY5MTJkNzg5MTJjZjZlMjU1IiwidXNlcl9rZXkiOiIyNzQyNjM0My1iYjI0LTQ4ZDktODVjNy01ODFmMjUwNzBiMzMiLCJjdXN0b21lcl9pZCI6IjEwMDAwMyIsInVzZXJuYW1lIjoiY3BjMTk4NiJ9.3MsaFjRAyArDp2WZsVbvKGPrVjkKvGB5EfBafprcFogbDeZv4s9VCQt4wUaR4FVon1tiL_pnqtMaI6qGGXs0Qg" +AUDIO_FILE_PATH = r"C:\Users\zjq18\Downloads\北京今天的天气.wav" + +@pytest.mark.integration +def test_list_voices(): + tts_api = TTSApi(base_url=BASE_URL_LIST, api_key=API_TOKEN) + resp = tts_api.list_voices() + assert resp.code == 200 + assert resp.success + assert isinstance(resp.data, list) + if resp.data: + assert hasattr(resp.data[0], "voiceId") + +@pytest.mark.integration +def test_add_voice(): + if not os.path.exists(AUDIO_FILE_PATH): + pytest.skip("音频文件不存在,跳过上传测试") + tts_api = TTSApi(base_url=BASE_URL_ADD, api_key=API_TOKEN) + resp = tts_api.add_voice( + voice_name="磁性嗓音123", + voice_text="今天北京的天气怎么样?", + file_path=AUDIO_FILE_PATH + ) + assert resp.code == 200 + assert resp.success + assert resp.data.voiceName == "磁性嗓音123" \ No newline at end of file diff --git a/zhipuai/api_resource/tts/__init__.py b/zhipuai/api_resource/tts/__init__.py new file mode 100644 index 0000000..00d8e99 --- /dev/null +++ b/zhipuai/api_resource/tts/__init__.py @@ -0,0 +1 @@ +from .tts import TTSApi \ No newline at end of file diff --git a/zhipuai/api_resource/tts/tts.py b/zhipuai/api_resource/tts/tts.py new file mode 100644 index 0000000..203758d --- /dev/null +++ b/zhipuai/api_resource/tts/tts.py @@ -0,0 +1,32 @@ +import requests +from typing import Optional +from zhipuai.types.tts.voice import VoiceListResponse, VoiceAddResponse + +class TTSApi: + def __init__(self, base_url: str, api_key: Optional[str] = None): + self.base_url = base_url.rstrip('/') + self.api_key = api_key + + def list_voices(self) -> VoiceListResponse: + url = f"{self.base_url}/v4/voice/list" + headers = {} + if self.api_key: + headers["Authorization"] = f"Bearer {self.api_key}" + + resp = requests.post(url,headers=headers) + resp.raise_for_status() + return VoiceListResponse.parse_obj(resp.json()) + + def add_voice(self, voice_name: str, voice_text: str, file_path: str) -> VoiceAddResponse: + url = f"{self.base_url}/v4/voice/add" + headers = {} + if self.api_key: + headers["Authorization"] = f"Bearer {self.api_key}" + files = { + "voiceName": (None, voice_name), + "voiceText": (None, voice_text), + "file": open(file_path, "rb"), + } + resp = requests.post(url, headers=headers, files=files) + resp.raise_for_status() + return VoiceAddResponse.parse_obj(resp.json()) \ No newline at end of file diff --git a/zhipuai/types/tts/__init__.py b/zhipuai/types/tts/__init__.py new file mode 100644 index 0000000..01b9eca --- /dev/null +++ b/zhipuai/types/tts/__init__.py @@ -0,0 +1 @@ +from .voice import Voice, VoiceListResponse, VoiceAddResponse \ No newline at end of file diff --git a/zhipuai/types/tts/voice.py b/zhipuai/types/tts/voice.py new file mode 100644 index 0000000..3ed5254 --- /dev/null +++ b/zhipuai/types/tts/voice.py @@ -0,0 +1,24 @@ +from typing import List +from pydantic import BaseModel + +class Voice(BaseModel): + id: int + voiceId: str + voiceName: str + voiceType: str + voiceStatus: str + voiceText: str + createTime: str + updateTime: str + +class VoiceListResponse(BaseModel): + code: int + msg: str + data: List[Voice] + success: bool + +class VoiceAddResponse(BaseModel): + code: int + msg: str + data: Voice + success: bool \ No newline at end of file From 616b6522f55ca7e0dec2c39f30e4f3a7bb661dd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=98=89=E7=90=A6?= Date: Tue, 15 Jul 2025 15:49:50 +0800 Subject: [PATCH 05/12] feat:APIKEY --- pyproject.toml | 6 +++--- tests/integration_tests/test_tts.py | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a8b5077..6c3e9ce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -84,15 +84,15 @@ build-backend = "poetry.core.masonry.api" # # https://github.com/tophat/syrupy # --snapshot-warn-unused Prints a warning on unused snapshots rather than fail the test suite. -addopts = "--strict-markers --strict-config --durations=5 --snapshot-warn-unused -svv" +addopts = "--strict-markers --strict-config --durations=5 -svv" # Registering custom markers. # https://docs.pytest.org/en/7.1.x/example/markers.html#registering-markers markers = [ "requires: mark tests as requiring a specific library", "scheduled: mark tests to run in scheduled testing", - "compile: mark placeholder test used to compile integration tests without running them" + "compile: mark placeholder test used to compile integration tests without running them", + "integration: mark tests as integration tests" ] -asyncio_mode = "auto" # https://python-poetry.org/docs/repositories/ diff --git a/tests/integration_tests/test_tts.py b/tests/integration_tests/test_tts.py index 41f260b..ec919aa 100644 --- a/tests/integration_tests/test_tts.py +++ b/tests/integration_tests/test_tts.py @@ -3,14 +3,14 @@ from zhipuai.api_resource.tts import TTSApi # 请根据实际情况填写 -BASE_URL_LIST = "http://localhost:9203" -BASE_URL_ADD = "http://localhost:9203" -API_TOKEN = "eyJhbGciOiJIUzUxMiJ9.eyJ1c2VyX3R5cGUiOiJTRVJWSUNFIiwidXNlcl9pZCI6MywiYXBpX2tleSI6IjM0ZDhiM2ExNDQ2OTQ0MmY5MTJkNzg5MTJjZjZlMjU1IiwidXNlcl9rZXkiOiIyNzQyNjM0My1iYjI0LTQ4ZDktODVjNy01ODFmMjUwNzBiMzMiLCJjdXN0b21lcl9pZCI6IjEwMDAwMyIsInVzZXJuYW1lIjoiY3BjMTk4NiJ9.3MsaFjRAyArDp2WZsVbvKGPrVjkKvGB5EfBafprcFogbDeZv4s9VCQt4wUaR4FVon1tiL_pnqtMaI6qGGXs0Qg" +BASE_URL_LIST = "https://dev.bigmodel.cn/stage-api/paas" +BASE_URL_ADD = "https://dev.bigmodel.cn/stage-api/paas" +API_KEY = "d91103df5c4a47bb808d4c84bcae9fcf.HCFkuDBhwKWLxliY" AUDIO_FILE_PATH = r"C:\Users\zjq18\Downloads\北京今天的天气.wav" @pytest.mark.integration def test_list_voices(): - tts_api = TTSApi(base_url=BASE_URL_LIST, api_key=API_TOKEN) + tts_api = TTSApi(base_url=BASE_URL_LIST, api_key=API_KEY) resp = tts_api.list_voices() assert resp.code == 200 assert resp.success @@ -22,7 +22,7 @@ def test_list_voices(): def test_add_voice(): if not os.path.exists(AUDIO_FILE_PATH): pytest.skip("音频文件不存在,跳过上传测试") - tts_api = TTSApi(base_url=BASE_URL_ADD, api_key=API_TOKEN) + tts_api = TTSApi(base_url=BASE_URL_ADD, api_key=API_KEY) resp = tts_api.add_voice( voice_name="磁性嗓音123", voice_text="今天北京的天气怎么样?", From 5775bc59164c72b7df3e7485229d2ffd6f7a66f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=98=89=E7=90=A6?= Date: Tue, 15 Jul 2025 16:02:54 +0800 Subject: [PATCH 06/12] feat:APIKEY --- tests/integration_tests/test_tts.py | 4 ++-- zhipuai/api_resource/tts/tts.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/integration_tests/test_tts.py b/tests/integration_tests/test_tts.py index ec919aa..5439b05 100644 --- a/tests/integration_tests/test_tts.py +++ b/tests/integration_tests/test_tts.py @@ -3,8 +3,8 @@ from zhipuai.api_resource.tts import TTSApi # 请根据实际情况填写 -BASE_URL_LIST = "https://dev.bigmodel.cn/stage-api/paas" -BASE_URL_ADD = "https://dev.bigmodel.cn/stage-api/paas" +BASE_URL_LIST = "https://dev.bigmodel.cn/stage-api/paas/v4" +BASE_URL_ADD = "https://dev.bigmodel.cn/stage-api/paas/v4" API_KEY = "d91103df5c4a47bb808d4c84bcae9fcf.HCFkuDBhwKWLxliY" AUDIO_FILE_PATH = r"C:\Users\zjq18\Downloads\北京今天的天气.wav" diff --git a/zhipuai/api_resource/tts/tts.py b/zhipuai/api_resource/tts/tts.py index 203758d..a9db744 100644 --- a/zhipuai/api_resource/tts/tts.py +++ b/zhipuai/api_resource/tts/tts.py @@ -8,7 +8,7 @@ def __init__(self, base_url: str, api_key: Optional[str] = None): self.api_key = api_key def list_voices(self) -> VoiceListResponse: - url = f"{self.base_url}/v4/voice/list" + url = f"{self.base_url}/voice/list" headers = {} if self.api_key: headers["Authorization"] = f"Bearer {self.api_key}" @@ -18,7 +18,7 @@ def list_voices(self) -> VoiceListResponse: return VoiceListResponse.parse_obj(resp.json()) def add_voice(self, voice_name: str, voice_text: str, file_path: str) -> VoiceAddResponse: - url = f"{self.base_url}/v4/voice/add" + url = f"{self.base_url}/voice/add" headers = {} if self.api_key: headers["Authorization"] = f"Bearer {self.api_key}" From 533624d1e0cd7936f4f26c2f575e4fbf9c90a956 Mon Sep 17 00:00:00 2001 From: yuhongxiao Date: Tue, 15 Jul 2025 19:19:25 +0800 Subject: [PATCH 07/12] Revert "feat:APIKEY" This reverts commit 5775bc59164c72b7df3e7485229d2ffd6f7a66f2. --- tests/integration_tests/test_tts.py | 4 ++-- zhipuai/api_resource/tts/tts.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/integration_tests/test_tts.py b/tests/integration_tests/test_tts.py index 5439b05..ec919aa 100644 --- a/tests/integration_tests/test_tts.py +++ b/tests/integration_tests/test_tts.py @@ -3,8 +3,8 @@ from zhipuai.api_resource.tts import TTSApi # 请根据实际情况填写 -BASE_URL_LIST = "https://dev.bigmodel.cn/stage-api/paas/v4" -BASE_URL_ADD = "https://dev.bigmodel.cn/stage-api/paas/v4" +BASE_URL_LIST = "https://dev.bigmodel.cn/stage-api/paas" +BASE_URL_ADD = "https://dev.bigmodel.cn/stage-api/paas" API_KEY = "d91103df5c4a47bb808d4c84bcae9fcf.HCFkuDBhwKWLxliY" AUDIO_FILE_PATH = r"C:\Users\zjq18\Downloads\北京今天的天气.wav" diff --git a/zhipuai/api_resource/tts/tts.py b/zhipuai/api_resource/tts/tts.py index a9db744..203758d 100644 --- a/zhipuai/api_resource/tts/tts.py +++ b/zhipuai/api_resource/tts/tts.py @@ -8,7 +8,7 @@ def __init__(self, base_url: str, api_key: Optional[str] = None): self.api_key = api_key def list_voices(self) -> VoiceListResponse: - url = f"{self.base_url}/voice/list" + url = f"{self.base_url}/v4/voice/list" headers = {} if self.api_key: headers["Authorization"] = f"Bearer {self.api_key}" @@ -18,7 +18,7 @@ def list_voices(self) -> VoiceListResponse: return VoiceListResponse.parse_obj(resp.json()) def add_voice(self, voice_name: str, voice_text: str, file_path: str) -> VoiceAddResponse: - url = f"{self.base_url}/voice/add" + url = f"{self.base_url}/v4/voice/add" headers = {} if self.api_key: headers["Authorization"] = f"Bearer {self.api_key}" From 0e32d341a25641c33353929319735912be20f4d0 Mon Sep 17 00:00:00 2001 From: yuhongxiao Date: Tue, 15 Jul 2025 19:19:32 +0800 Subject: [PATCH 08/12] Revert "feat:APIKEY" This reverts commit 616b6522f55ca7e0dec2c39f30e4f3a7bb661dd1. --- pyproject.toml | 6 +++--- tests/integration_tests/test_tts.py | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6c3e9ce..a8b5077 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -84,15 +84,15 @@ build-backend = "poetry.core.masonry.api" # # https://github.com/tophat/syrupy # --snapshot-warn-unused Prints a warning on unused snapshots rather than fail the test suite. -addopts = "--strict-markers --strict-config --durations=5 -svv" +addopts = "--strict-markers --strict-config --durations=5 --snapshot-warn-unused -svv" # Registering custom markers. # https://docs.pytest.org/en/7.1.x/example/markers.html#registering-markers markers = [ "requires: mark tests as requiring a specific library", "scheduled: mark tests to run in scheduled testing", - "compile: mark placeholder test used to compile integration tests without running them", - "integration: mark tests as integration tests" + "compile: mark placeholder test used to compile integration tests without running them" ] +asyncio_mode = "auto" # https://python-poetry.org/docs/repositories/ diff --git a/tests/integration_tests/test_tts.py b/tests/integration_tests/test_tts.py index ec919aa..41f260b 100644 --- a/tests/integration_tests/test_tts.py +++ b/tests/integration_tests/test_tts.py @@ -3,14 +3,14 @@ from zhipuai.api_resource.tts import TTSApi # 请根据实际情况填写 -BASE_URL_LIST = "https://dev.bigmodel.cn/stage-api/paas" -BASE_URL_ADD = "https://dev.bigmodel.cn/stage-api/paas" -API_KEY = "d91103df5c4a47bb808d4c84bcae9fcf.HCFkuDBhwKWLxliY" +BASE_URL_LIST = "http://localhost:9203" +BASE_URL_ADD = "http://localhost:9203" +API_TOKEN = "eyJhbGciOiJIUzUxMiJ9.eyJ1c2VyX3R5cGUiOiJTRVJWSUNFIiwidXNlcl9pZCI6MywiYXBpX2tleSI6IjM0ZDhiM2ExNDQ2OTQ0MmY5MTJkNzg5MTJjZjZlMjU1IiwidXNlcl9rZXkiOiIyNzQyNjM0My1iYjI0LTQ4ZDktODVjNy01ODFmMjUwNzBiMzMiLCJjdXN0b21lcl9pZCI6IjEwMDAwMyIsInVzZXJuYW1lIjoiY3BjMTk4NiJ9.3MsaFjRAyArDp2WZsVbvKGPrVjkKvGB5EfBafprcFogbDeZv4s9VCQt4wUaR4FVon1tiL_pnqtMaI6qGGXs0Qg" AUDIO_FILE_PATH = r"C:\Users\zjq18\Downloads\北京今天的天气.wav" @pytest.mark.integration def test_list_voices(): - tts_api = TTSApi(base_url=BASE_URL_LIST, api_key=API_KEY) + tts_api = TTSApi(base_url=BASE_URL_LIST, api_key=API_TOKEN) resp = tts_api.list_voices() assert resp.code == 200 assert resp.success @@ -22,7 +22,7 @@ def test_list_voices(): def test_add_voice(): if not os.path.exists(AUDIO_FILE_PATH): pytest.skip("音频文件不存在,跳过上传测试") - tts_api = TTSApi(base_url=BASE_URL_ADD, api_key=API_KEY) + tts_api = TTSApi(base_url=BASE_URL_ADD, api_key=API_TOKEN) resp = tts_api.add_voice( voice_name="磁性嗓音123", voice_text="今天北京的天气怎么样?", From f2aef580707737d85e22f98397b9073a227d06ad Mon Sep 17 00:00:00 2001 From: yuhongxiao Date: Tue, 15 Jul 2025 19:19:36 +0800 Subject: [PATCH 09/12] =?UTF-8?q?Revert=20"feat:=E8=B1=86=E7=A5=9E?= =?UTF-8?q?=E4=B8=80=E6=9C=9F=EF=BC=8C=E6=B7=BB=E5=8A=A0=E9=9F=B3=E8=89=B2?= =?UTF-8?q?=EF=BC=8C=E6=9F=A5=E8=AF=A2=E9=9F=B3=E8=89=B2=E5=88=97=E8=A1=A8?= =?UTF-8?q?"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 7d79be9d30e3bac7fb51d671c01e35a44838bf6c. --- tests/integration_tests/test_tts.py | 33 ---------------------------- zhipuai/api_resource/tts/__init__.py | 1 - zhipuai/api_resource/tts/tts.py | 32 --------------------------- zhipuai/types/tts/__init__.py | 1 - zhipuai/types/tts/voice.py | 24 -------------------- 5 files changed, 91 deletions(-) delete mode 100644 tests/integration_tests/test_tts.py delete mode 100644 zhipuai/api_resource/tts/__init__.py delete mode 100644 zhipuai/api_resource/tts/tts.py delete mode 100644 zhipuai/types/tts/__init__.py delete mode 100644 zhipuai/types/tts/voice.py diff --git a/tests/integration_tests/test_tts.py b/tests/integration_tests/test_tts.py deleted file mode 100644 index 41f260b..0000000 --- a/tests/integration_tests/test_tts.py +++ /dev/null @@ -1,33 +0,0 @@ -import os -import pytest -from zhipuai.api_resource.tts import TTSApi - -# 请根据实际情况填写 -BASE_URL_LIST = "http://localhost:9203" -BASE_URL_ADD = "http://localhost:9203" -API_TOKEN = "eyJhbGciOiJIUzUxMiJ9.eyJ1c2VyX3R5cGUiOiJTRVJWSUNFIiwidXNlcl9pZCI6MywiYXBpX2tleSI6IjM0ZDhiM2ExNDQ2OTQ0MmY5MTJkNzg5MTJjZjZlMjU1IiwidXNlcl9rZXkiOiIyNzQyNjM0My1iYjI0LTQ4ZDktODVjNy01ODFmMjUwNzBiMzMiLCJjdXN0b21lcl9pZCI6IjEwMDAwMyIsInVzZXJuYW1lIjoiY3BjMTk4NiJ9.3MsaFjRAyArDp2WZsVbvKGPrVjkKvGB5EfBafprcFogbDeZv4s9VCQt4wUaR4FVon1tiL_pnqtMaI6qGGXs0Qg" -AUDIO_FILE_PATH = r"C:\Users\zjq18\Downloads\北京今天的天气.wav" - -@pytest.mark.integration -def test_list_voices(): - tts_api = TTSApi(base_url=BASE_URL_LIST, api_key=API_TOKEN) - resp = tts_api.list_voices() - assert resp.code == 200 - assert resp.success - assert isinstance(resp.data, list) - if resp.data: - assert hasattr(resp.data[0], "voiceId") - -@pytest.mark.integration -def test_add_voice(): - if not os.path.exists(AUDIO_FILE_PATH): - pytest.skip("音频文件不存在,跳过上传测试") - tts_api = TTSApi(base_url=BASE_URL_ADD, api_key=API_TOKEN) - resp = tts_api.add_voice( - voice_name="磁性嗓音123", - voice_text="今天北京的天气怎么样?", - file_path=AUDIO_FILE_PATH - ) - assert resp.code == 200 - assert resp.success - assert resp.data.voiceName == "磁性嗓音123" \ No newline at end of file diff --git a/zhipuai/api_resource/tts/__init__.py b/zhipuai/api_resource/tts/__init__.py deleted file mode 100644 index 00d8e99..0000000 --- a/zhipuai/api_resource/tts/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .tts import TTSApi \ No newline at end of file diff --git a/zhipuai/api_resource/tts/tts.py b/zhipuai/api_resource/tts/tts.py deleted file mode 100644 index 203758d..0000000 --- a/zhipuai/api_resource/tts/tts.py +++ /dev/null @@ -1,32 +0,0 @@ -import requests -from typing import Optional -from zhipuai.types.tts.voice import VoiceListResponse, VoiceAddResponse - -class TTSApi: - def __init__(self, base_url: str, api_key: Optional[str] = None): - self.base_url = base_url.rstrip('/') - self.api_key = api_key - - def list_voices(self) -> VoiceListResponse: - url = f"{self.base_url}/v4/voice/list" - headers = {} - if self.api_key: - headers["Authorization"] = f"Bearer {self.api_key}" - - resp = requests.post(url,headers=headers) - resp.raise_for_status() - return VoiceListResponse.parse_obj(resp.json()) - - def add_voice(self, voice_name: str, voice_text: str, file_path: str) -> VoiceAddResponse: - url = f"{self.base_url}/v4/voice/add" - headers = {} - if self.api_key: - headers["Authorization"] = f"Bearer {self.api_key}" - files = { - "voiceName": (None, voice_name), - "voiceText": (None, voice_text), - "file": open(file_path, "rb"), - } - resp = requests.post(url, headers=headers, files=files) - resp.raise_for_status() - return VoiceAddResponse.parse_obj(resp.json()) \ No newline at end of file diff --git a/zhipuai/types/tts/__init__.py b/zhipuai/types/tts/__init__.py deleted file mode 100644 index 01b9eca..0000000 --- a/zhipuai/types/tts/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .voice import Voice, VoiceListResponse, VoiceAddResponse \ No newline at end of file diff --git a/zhipuai/types/tts/voice.py b/zhipuai/types/tts/voice.py deleted file mode 100644 index 3ed5254..0000000 --- a/zhipuai/types/tts/voice.py +++ /dev/null @@ -1,24 +0,0 @@ -from typing import List -from pydantic import BaseModel - -class Voice(BaseModel): - id: int - voiceId: str - voiceName: str - voiceType: str - voiceStatus: str - voiceText: str - createTime: str - updateTime: str - -class VoiceListResponse(BaseModel): - code: int - msg: str - data: List[Voice] - success: bool - -class VoiceAddResponse(BaseModel): - code: int - msg: str - data: Voice - success: bool \ No newline at end of file From ca91296b184ab8762e299695079d4927be36dc44 Mon Sep 17 00:00:00 2001 From: yuhongxiao Date: Tue, 15 Jul 2025 19:51:29 +0800 Subject: [PATCH 10/12] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/integration_tests/test_audio.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/integration_tests/test_audio.py b/tests/integration_tests/test_audio.py index 63a62de..b8cff6e 100644 --- a/tests/integration_tests/test_audio.py +++ b/tests/integration_tests/test_audio.py @@ -1,3 +1,5 @@ +import base64 +import json import logging import logging.config from pathlib import Path @@ -38,8 +40,14 @@ def test_audio_speech_streaming(logging_conf): stream=True, response_format='wav', ) - for item in response: - print(item) + with open("output.pcm", "wb") as f: + for item in response: + info_dict = json.loads(item) + index = info_dict.get('sequence') + is_finished = info_dict.get('is_finished') + audio_delta = info_dict.get('audio') + f.write(base64.b64decode(audio_delta)) + print(f"{index}.is_finished = {is_finished}, audio_delta = {len(audio_delta)}") except zhipuai.core._errors.APIRequestFailedError as err: print(err) From c5d0b2b16e7fe814ddab8dcc50eb19f93d1a8237 Mon Sep 17 00:00:00 2001 From: yuhongxiao Date: Wed, 16 Jul 2025 10:42:55 +0800 Subject: [PATCH 11/12] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/integration_tests/test_audio.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/integration_tests/test_audio.py b/tests/integration_tests/test_audio.py index b8cff6e..14fb8cb 100644 --- a/tests/integration_tests/test_audio.py +++ b/tests/integration_tests/test_audio.py @@ -42,12 +42,14 @@ def test_audio_speech_streaming(logging_conf): ) with open("output.pcm", "wb") as f: for item in response: - info_dict = json.loads(item) - index = info_dict.get('sequence') - is_finished = info_dict.get('is_finished') - audio_delta = info_dict.get('audio') + choice = item.choices[0] + index = choice.index + finish_reason = choice.finish_reason + audio_delta = item.choices[0].delta.content + if finish_reason is not None: + break f.write(base64.b64decode(audio_delta)) - print(f"{index}.is_finished = {is_finished}, audio_delta = {len(audio_delta)}") + print(f"{index}.finish_reason = {finish_reason}, audio_delta = {len(audio_delta)}") except zhipuai.core._errors.APIRequestFailedError as err: print(err) @@ -55,6 +57,8 @@ def test_audio_speech_streaming(logging_conf): print(err) except zhipuai.core._errors.APIStatusError as err: print(err) + except Exception as e: + print(e) def test_audio_customization(logging_conf): From 8f48be2af52c66c0285e38c4eee75e2dc535e735 Mon Sep 17 00:00:00 2001 From: yuhongxiao Date: Wed, 16 Jul 2025 10:49:54 +0800 Subject: [PATCH 12/12] =?UTF-8?q?=E5=8F=82=E6=95=B0=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/integration_tests/test_audio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration_tests/test_audio.py b/tests/integration_tests/test_audio.py index 14fb8cb..2ef05d6 100644 --- a/tests/integration_tests/test_audio.py +++ b/tests/integration_tests/test_audio.py @@ -45,7 +45,7 @@ def test_audio_speech_streaming(logging_conf): choice = item.choices[0] index = choice.index finish_reason = choice.finish_reason - audio_delta = item.choices[0].delta.content + audio_delta = choice.delta.content if finish_reason is not None: break f.write(base64.b64decode(audio_delta))