feat(asr): 添加OpenAI和Groq语音识别支持

新增GPT语音识别模型供应器和配置,实现语音转文本功能
更新相关文档说明,包含API申请步骤和使用注意事项
This commit is contained in:
luruxian
2025-07-10 11:41:20 +08:00
parent 203ea89c6d
commit fbd4a22e3e
2 changed files with 113 additions and 0 deletions
@@ -0,0 +1,31 @@
-- OpenAI ASR模型供应器
delete from `ai_model_provider` where id = 'SYSTEM_ASR_GptASR';
INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
('SYSTEM_ASR_GptASR', 'ASR', 'gpt', 'Gpt语音识别', '[{"key": "base_url", "type": "string", "label": "基础URL"}, {"key": "model_name", "type": "string", "label": "模型名称"}, {"key": "api_key", "type": "string", "label": "API密钥"}, {"key": "output_dir", "type": "string", "label": "输出目录"}]', 5, 1, NOW(), 1, NOW());
-- OpenAI ASR模型配置
delete from `ai_model_config` where id = 'ASR_GptASR';
INSERT INTO `ai_model_config` VALUES ('ASR_GptASR', 'ASR', 'GptASR', 'GPT语音识别', 0, 1, '{\"type\": \"gpt\", \"api_key\": \"\", \"base_url\": \"https://api.openai.com/v1/audio/transcriptions\", \"model_name\": \"gpt-4o-mini-transcribe\", \"output_dir\": \"tmp/\"}', NULL, NULL, 3, NULL, NULL, NULL, NULL);
-- 更新OpenAI ASR配置说明
UPDATE `ai_model_config` SET
`doc_link` = 'https://platform.openai.com/docs/api-reference/audio/createTranscription',
`remark` = 'OpenAI ASR配置说明:
1. 需要在OpenAI开放平台创建组织并获取api_key
2. 支持中、英、日、韩等多种语音识别,具体参考文档https://platform.openai.com/docs/guides/speech-to-text
3. 需要网络连接
4. 输出文件保存在tmp/目录
5. 兼容groq语音识别。 groq识别速度更快。具体参考文档:https://console.groq.com/docs/speech-to-text
申请步骤:
**OpenAi ASR申请步骤:**
1.登录OpenAI Platform。https://auth.openai.com/log-in
2.创建api-key https://platform.openai.com/settings/organization/api-keys
3.模型可以选择gpt-4o-transcribe或GPT-4o mini Transcribe
**groq ASR申请步骤:**
1.登录groq Console。https://console.groq.com/home
2.创建api-key https://console.groq.com/keys
3.模型可以选择whisper-large-v3-turbo或whisper-large-v3distil-whisper-large-v3-en仅支持英语转录)
4.base url https://api.groq.com/openai/v1/audio/transcriptions
' WHERE `id` = 'ASR_GptASR';
@@ -0,0 +1,82 @@
import time
import os
from config.logger import setup_logging
from typing import Optional, Tuple, List
from core.providers.asr.dto.dto import InterfaceType
from core.providers.asr.base import ASRProviderBase
import requests
TAG = __name__
logger = setup_logging()
class ASRProvider(ASRProviderBase):
def __init__(self, config: dict, delete_audio_file: bool):
self.interface_type = InterfaceType.NON_STREAM
self.api_key = config.get("api_key")
self.api_url = config.get("base_url")
self.model = config.get("model_name")
self.output_dir = config.get("output_dir")
self.delete_audio_file = delete_audio_file
os.makedirs(self.output_dir, exist_ok=True)
async def speech_to_text(self, opus_data: List[bytes], session_id: str, audio_format="opus") -> Tuple[Optional[str], Optional[str]]:
file_path = None
try:
start_time = time.time()
if audio_format == "pcm":
pcm_data = opus_data
else:
pcm_data = self.decode_opus(opus_data)
file_path = self.save_audio_to_file(pcm_data, session_id)
logger.bind(tag=TAG).debug(
f"音频文件保存耗时: {time.time() - start_time:.3f}s | 路径: {file_path}"
)
logger.bind(tag=TAG).info(f"file path: {file_path}")
headers = {
"Authorization": f"Bearer {self.api_key}",
}
# 使用data参数传递模型名称
data = {
"model": self.model
}
with open(file_path, "rb") as audio_file: # 使用with语句确保文件关闭
files = {
"file": audio_file
}
start_time = time.time()
response = requests.post(
self.api_url,
files=files,
data=data,
headers=headers
)
logger.bind(tag=TAG).debug(
f"语音识别耗时: {time.time() - start_time:.3f}s | 结果: {response.text}"
)
if response.status_code == 200:
text = response.json().get("text", "")
return text, file_path
else:
raise Exception(f"API请求失败: {response.status_code} - {response.text}")
except Exception as e:
logger.bind(tag=TAG).error(f"语音识别失败: {e}")
return "", None
finally:
# 文件清理逻辑
if self.delete_audio_file and file_path and os.path.exists(file_path):
try:
os.remove(file_path)
logger.bind(tag=TAG).debug(f"已删除临时音频文件: {file_path}")
except Exception as e:
logger.bind(tag=TAG).error(f"文件删除失败: {file_path} | 错误: {e}")