[文档、验收与归档]:完成真实运行说明和OpenSpec归档,包含README、最终门禁和主规范更新
This commit is contained in:
+23
-36
@@ -1,12 +1,12 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import io
|
||||
import base64
|
||||
import json
|
||||
import re
|
||||
import socket
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
import uuid
|
||||
import wave
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
@@ -84,19 +84,28 @@ class CloudAsrSttProvider:
|
||||
"newapi-asr",
|
||||
"stt",
|
||||
)
|
||||
wav_bytes = _segment_to_wav_bytes(segment)
|
||||
boundary = "owner-voice-pet-" + uuid.uuid4().hex
|
||||
body = _multipart_form_data(
|
||||
boundary,
|
||||
fields={"model": self.config.asr_model, "response_format": "json"},
|
||||
files={"file": ("utterance.wav", "audio/wav", wav_bytes)},
|
||||
)
|
||||
audio_data = base64.b64encode(_segment_to_wav_bytes(segment)).decode("ascii")
|
||||
payload = {
|
||||
"model": self.config.asr_model,
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "input_audio",
|
||||
"input_audio": {"data": f"data:audio/wav;base64,{audio_data}"},
|
||||
}
|
||||
],
|
||||
}
|
||||
],
|
||||
"asr_options": {"language": "auto"},
|
||||
}
|
||||
request = urllib.request.Request(
|
||||
self.config.api_url("/v1/audio/transcriptions"),
|
||||
data=body,
|
||||
self.config.api_url("/v1/chat/completions"),
|
||||
data=json.dumps(payload).encode("utf-8"),
|
||||
headers={
|
||||
"Authorization": f"Bearer {self.config.llm_api_key}",
|
||||
"Content-Type": f"multipart/form-data; boundary={boundary}",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
method="POST",
|
||||
)
|
||||
@@ -119,7 +128,9 @@ class CloudAsrSttProvider:
|
||||
"newapi-asr",
|
||||
"stt",
|
||||
) from exc
|
||||
text = str(payload.get("text") or "").strip()
|
||||
choices = payload.get("choices") or []
|
||||
message = (choices[0].get("message") if choices else {}) or {}
|
||||
text = str(message.get("content") or payload.get("text") or "").strip()
|
||||
if not is_valid_transcript_text(text):
|
||||
raise ProviderError(
|
||||
ErrorCode.STT_EMPTY_TRANSCRIPT,
|
||||
@@ -261,27 +272,3 @@ def _segment_to_wav_bytes(segment: AudioSegment) -> bytes:
|
||||
wav.setframerate(segment.sample_rate)
|
||||
wav.writeframes(segment.pcm)
|
||||
return buffer.getvalue()
|
||||
|
||||
|
||||
def _multipart_form_data(
|
||||
boundary: str,
|
||||
*,
|
||||
fields: dict[str, str],
|
||||
files: dict[str, tuple[str, str, bytes]],
|
||||
) -> bytes:
|
||||
body = bytearray()
|
||||
for name, value in fields.items():
|
||||
body.extend(f"--{boundary}\r\n".encode())
|
||||
body.extend(f'Content-Disposition: form-data; name="{name}"\r\n\r\n'.encode())
|
||||
body.extend(value.encode("utf-8"))
|
||||
body.extend(b"\r\n")
|
||||
for name, (filename, content_type, data) in files.items():
|
||||
body.extend(f"--{boundary}\r\n".encode())
|
||||
body.extend(
|
||||
f'Content-Disposition: form-data; name="{name}"; filename="{filename}"\r\n'.encode()
|
||||
)
|
||||
body.extend(f"Content-Type: {content_type}\r\n\r\n".encode())
|
||||
body.extend(data)
|
||||
body.extend(b"\r\n")
|
||||
body.extend(f"--{boundary}--\r\n".encode())
|
||||
return bytes(body)
|
||||
|
||||
Reference in New Issue
Block a user