Files

141 lines
5.1 KiB
Python
Raw Permalink Normal View History

2025-04-14 11:03:56 +08:00
import time
2025-02-21 19:46:06 +08:00
import asyncio
2025-04-14 11:03:56 +08:00
import weakref
2025-02-21 19:46:06 +08:00
import base64
import logging
2025-04-14 11:03:56 +08:00
from .base_model_backend import BaseModelBackend
from client.asr_client import ASRClient
2025-02-21 19:46:06 +08:00
from concurrent.futures import ThreadPoolExecutor
2025-04-14 11:03:56 +08:00
from services.memory_check import MemoryChecker
2025-02-21 19:46:06 +08:00
class ASRClientBackend(BaseModelBackend):
def __init__(self, model_config):
super().__init__(model_config)
2025-04-14 11:03:56 +08:00
self._client_pool = []
self._active_clients = {}
self._pool_lock = asyncio.Lock()
self.logger = logging.getLogger("api.asr")
self.POOL_SIZE = 1
self._inference_executor = ThreadPoolExecutor(max_workers=self.POOL_SIZE)
self._active_tasks = weakref.WeakSet()
self.memory_checker = MemoryChecker(
host=self.config["host"],
port=self.config["port"]
)
2025-02-21 19:46:06 +08:00
2025-04-17 18:28:17 +08:00
async def _get_client(self, language: str):
2025-02-21 19:46:06 +08:00
try:
2025-04-14 11:03:56 +08:00
await asyncio.wait_for(self._pool_lock.acquire(), timeout=30.0)
start_time = time.time()
timeout = 30.0
retry_interval = 3
2025-02-24 16:13:29 +08:00
2025-04-14 11:03:56 +08:00
while True:
if self._client_pool:
client = self._client_pool.pop()
self.logger.debug(f"Reusing client from pool | ID:{id(client)}")
2025-04-14 11:03:56 +08:00
return client
if len(self._active_clients) < self.POOL_SIZE:
break
2025-04-14 11:03:56 +08:00
for task in self._active_tasks:
task.cancel()
self._pool_lock.release()
await asyncio.sleep(retry_interval)
await asyncio.wait_for(self._pool_lock.acquire(), timeout=timeout - (time.time() - start_time))
if "memory_required" in self.config:
await self.memory_checker.check_memory(self.config["memory_required"])
2026-02-24 09:42:30 +08:00
self.logger.debug("Creating new ASR client")
client = ASRClient(
host=self.config["host"],
port=self.config["port"]
)
self._active_clients[id(client)] = client
2025-04-14 11:03:56 +08:00
loop = asyncio.get_event_loop()
await loop.run_in_executor(
2025-12-26 16:56:23 +08:00
self._inference_executor,
lambda: client.setup(
self.config["object"],
{
"model": self.config["model_name"],
"response_format": "asr.utf-8",
2025-12-26 16:56:23 +08:00
"input": "whisper.asr.wav.stream.base64",
2025-04-17 18:28:17 +08:00
"language": language,
"enoutput": True
2025-12-26 16:56:23 +08:00
}
)
)
return client
2025-04-14 11:03:56 +08:00
except asyncio.TimeoutError:
raise RuntimeError("Server busy, please try again later.")
finally:
if self._pool_lock.locked():
self._pool_lock.release()
async def _release_client(self, client):
async with self._pool_lock:
self._client_pool.append(client)
self.logger.debug(f"Returned client to pool | ID:{id(client)}")
2025-04-14 11:03:56 +08:00
async def close(self):
for task in self._active_tasks:
task.cancel()
if self._active_tasks:
await asyncio.wait(self._active_tasks, timeout=2)
for client in self._client_pool:
client.exit()
self._client_pool.clear()
self._active_clients.clear()
self._inference_executor.shutdown(wait=False)
2025-04-14 11:03:56 +08:00
async def _inference(self, client, audio_b64: str):
2025-02-24 16:13:29 +08:00
loop = asyncio.get_event_loop()
for chunk in await loop.run_in_executor(
2025-04-14 11:03:56 +08:00
self._inference_executor,
client.inference,
2025-02-24 16:13:29 +08:00
audio_b64,
"asr.base64"
):
2025-04-14 11:03:56 +08:00
full_result = chunk
return full_result
2025-02-24 16:13:29 +08:00
2025-04-14 11:03:56 +08:00
async def create_transcription(self, audio_data: bytes, language: str = "zh", prompt: str = "") -> str:
2025-04-17 18:28:17 +08:00
client = await self._get_client(language)
2025-04-14 11:03:56 +08:00
task = asyncio.current_task()
self._active_tasks.add(task)
try:
audio_b64 = base64.b64encode(audio_data).decode('utf-8')
chunk_size = 4096
audio_chunks = [
audio_b64[i:i + chunk_size] for i in range(0, len(audio_b64), chunk_size)
]
transcription = ""
for index, chunk in enumerate(audio_chunks):
finish = index == len(audio_chunks) - 1
responses = list(client.inference_stream(
delta=chunk,
index=index,
finish=finish,
2025-04-15 19:38:36 +08:00
object_type="asr.wav.stream.base64"
))
transcription += "".join(responses)
await asyncio.sleep(0.002)
return transcription
2025-04-14 11:03:56 +08:00
except asyncio.CancelledError:
self.logger.warning("Inference task cancelled, stopping...")
client.stop_inference()
raise
except Exception as e:
self.logger.error(f"Inference error: {str(e)}")
raise RuntimeError(f"[ERROR: {str(e)}")
finally:
self._active_tasks.discard(task)
await self._release_client(client)