[文档、验收与归档]:完成真实运行说明和OpenSpec归档,包含README、最终门禁和主规范更新

This commit is contained in:
mkbk
2026-06-17 20:17:04 +08:00
parent 4b7cd18a0f
commit 445eebeaec
15 changed files with 320 additions and 143 deletions
+23 -36
View File
@@ -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)