返回 JoyAI-Echo
transcription.py
1 """Voice transcription providers (Groq and OpenAI Whisper)."""
2
3 import os
4 from pathlib import Path
5
6 import httpx
7 from loguru import logger
8
9
10 class OpenAITranscriptionProvider:
11 """Voice transcription provider using OpenAI's Whisper API."""
12
13 def __init__(
14 self,
15 api_key: str | None = None,
16 api_base: str | None = None,
17 language: str | None = None,
18 ):
19 self.api_key = api_key or os.environ.get("OPENAI_API_KEY")
20 self.api_url = (
21 api_base
22 or os.environ.get("OPENAI_TRANSCRIPTION_BASE_URL")
23 or "https://api.openai.com/v1/audio/transcriptions"
24 )
25 self.language = language or None
26
27 async def transcribe(self, file_path: str | Path) -> str:
28 if not self.api_key:
29 logger.warning("OpenAI API key not configured for transcription")
30 return ""
31 path = Path(file_path)
32 if not path.exists():
33 logger.error("Audio file not found: {}", file_path)
34 return ""
35 try:
36 async with httpx.AsyncClient() as client:
37 with open(path, "rb") as f:
38 files = {"file": (path.name, f), "model": (None, "whisper-1")}
39 if self.language:
40 files["language"] = (None, self.language)
41 headers = {"Authorization": f"Bearer {self.api_key}"}
42 response = await client.post(
43 self.api_url, headers=headers, files=files, timeout=60.0,
44 )
45 response.raise_for_status()
46 return response.json().get("text", "")
47 except Exception as e:
48 logger.error("OpenAI transcription error: {}", e)
49 return ""
50
51
52 class GroqTranscriptionProvider:
53 """
54 Voice transcription provider using Groq's Whisper API.
55
56 Groq offers extremely fast transcription with a generous free tier.
57 """
58
59 def __init__(
60 self,
61 api_key: str | None = None,
62 api_base: str | None = None,
63 language: str | None = None,
64 ):
65 self.api_key = api_key or os.environ.get("GROQ_API_KEY")
66 self.api_url = api_base or os.environ.get("GROQ_BASE_URL") or "https://api.groq.com/openai/v1/audio/transcriptions"
67 self.language = language or None
68
69 async def transcribe(self, file_path: str | Path) -> str:
70 """
71 Transcribe an audio file using Groq.
72
73 Args:
74 file_path: Path to the audio file.
75
76 Returns:
77 Transcribed text.
78 """
79 if not self.api_key:
80 logger.warning("Groq API key not configured for transcription")
81 return ""
82
83 path = Path(file_path)
84 if not path.exists():
85 logger.error("Audio file not found: {}", file_path)
86 return ""
87
88 try:
89 async with httpx.AsyncClient() as client:
90 with open(path, "rb") as f:
91 files = {
92 "file": (path.name, f),
93 "model": (None, "whisper-large-v3"),
94 }
95 if self.language:
96 files["language"] = (None, self.language)
97 headers = {
98 "Authorization": f"Bearer {self.api_key}",
99 }
100
101 response = await client.post(
102 self.api_url,
103 headers=headers,
104 files=files,
105 timeout=60.0
106 )
107
108 response.raise_for_status()
109 data = response.json()
110 return data.get("text", "")
111
112 except Exception as e:
113 logger.error("Groq transcription error: {}", e)
114 return ""
115
115 lines PYTHON