commit 7f50380e52fa2a2c3f265e5a1e8a2cb800fe88aa Author: yumoqing Date: Wed Jul 16 14:32:28 2025 +0800 first commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..554ecf3 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# asr_serve + +a local deploy ASR models web server \ No newline at end of file diff --git a/app/README.md b/app/README.md new file mode 100644 index 0000000..e69de29 diff --git a/app/__pycache__/asyncproxy.cpython-310.pyc b/app/__pycache__/asyncproxy.cpython-310.pyc new file mode 100644 index 0000000..c7e6666 Binary files /dev/null and b/app/__pycache__/asyncproxy.cpython-310.pyc differ diff --git a/app/__pycache__/const.cpython-310.pyc b/app/__pycache__/const.cpython-310.pyc new file mode 100644 index 0000000..a7e7c96 Binary files /dev/null and b/app/__pycache__/const.cpython-310.pyc differ diff --git a/app/__pycache__/ext.cpython-310.pyc b/app/__pycache__/ext.cpython-310.pyc new file mode 100644 index 0000000..d1a089a Binary files /dev/null and b/app/__pycache__/ext.cpython-310.pyc differ diff --git a/app/__pycache__/llm_client.cpython-310.pyc b/app/__pycache__/llm_client.cpython-310.pyc new file mode 100644 index 0000000..5dd2ca4 Binary files /dev/null and b/app/__pycache__/llm_client.cpython-310.pyc differ diff --git a/app/__pycache__/rf.cpython-310.pyc b/app/__pycache__/rf.cpython-310.pyc new file mode 100644 index 0000000..54e3a5c Binary files /dev/null and b/app/__pycache__/rf.cpython-310.pyc differ diff --git a/app/__pycache__/streamproxy.cpython-310.pyc b/app/__pycache__/streamproxy.cpython-310.pyc new file mode 100644 index 0000000..bac737b Binary files /dev/null and b/app/__pycache__/streamproxy.cpython-310.pyc differ diff --git a/app/__pycache__/syncproxy.cpython-310.pyc b/app/__pycache__/syncproxy.cpython-310.pyc new file mode 100644 index 0000000..646052a Binary files /dev/null and b/app/__pycache__/syncproxy.cpython-310.pyc differ diff --git a/app/asr.py b/app/asr.py new file mode 100644 index 0000000..0bede44 --- /dev/null +++ b/app/asr.py @@ -0,0 +1,40 @@ +import os, sys +import argparse +from appPublic.log import MyLogger, info, debug, warning +from appPublic.folderUtils import ProgramPath +from appPublic.jsonConfig import getConfig +from appPublic.registerfunction import RegisterFunction +from ahserver.configuredServer import ConfiguredServer +from ahserver.serverenv import ServerEnv +from whisper_model import WhisperFile +from asr_engine import generate + +__version__ = '0.0.1' + +if __name__ == '__main__': + parser = argparse.ArgumentParser(prog="Sage") + parser.add_argument('-w', '--workdir') + parser.add_argument('-p', '--port') + args = parser.parse_args() + print(args) + workdir = args.workdir or os.getcwd() + p = ProgramPath() + config = getConfig(workdir, NS={'workdir':workdir, 'ProgramPath':p}) + if config.logger: + logger = MyLogger(config.logger.name or 'sage', + levelname=config.logger.levelname or 'debug', + logfile=config.logger.logfile or None) + else: + logger = MyLogger('sage', levelname='debug') + + info(f'========sage version={__version__}========') + # server = ConfiguredServer(auth_klass=MyAuthAPI, workdir=workdir) + server = ConfiguredServer(workdir=workdir) + rf = RegisterFunction() + rf.register('generate', generate) + g = ServerEnv() + g.whisper_engine = WhisperFile() + port = args.port or config.website.port or 8080 + port = int(port) + server.run(port=port) + diff --git a/app/asr_engine.py b/app/asr_engine.py new file mode 100644 index 0000000..dab5e12 --- /dev/null +++ b/app/asr_engine.py @@ -0,0 +1,76 @@ +import os +import time +from traceback import print_exc +import base64 +import wave +from appPublic.log import info, debug, warning, error, exception, critical +from appPublic.dictObject import DictObject +from appPublic.folderUtils import temp_file +from ahserver.serverenv import ServerEnv +from aiohttp.web import StreamResponse +from ahserver.globalEnv import realpath + +def save_base64_wav(base64_data, output_file,sample_rate=16000, num_channels=1): + # Decode the base64 data + wav_data = base64.b64decode(base64_data) + + # Open a new WAV file for writing + with wave.open(output_file, 'wb') as wf: + # Set the parameters of the WAV file + wf.setnchannels(num_channels) # Mono channel + wf.setsampwidth(2) # 16-bit sample width + wf.setframerate(sample_rate) # 44.1 kHz sample rate + # Write the decoded data to the WAV file + wf.writeframes(wav_data) + +async def generate(request, kw): + params_kw = kw.get('params_kw', DictObject()) + info(f'{params_kw=}') + model = params_kw.model + audio_file = params_kw.audio_file + if not audio_file: + audio = params_kw.audio + if audio is None: + return { + 'status':'error', + 'message':'audio is null' + } + audio_file = temp_file(suffix='.wav') + save_base64_wav(audio, audio_file) + else: + audio_file = realpath(audio_file) + + engine = None + g = ServerEnv() + if model=='whisper': + engine = g.whisper_engine + + if engine is None: + return { + 'status':'error', + 'message':f'model={model} is not defined' + } + try: + t1 = time.time() + dic = await engine.stt(audio_file) + t2 = time.time() + os.remove(audio_file) + ret = { + "status":"ok", + "time_cost":t2-t1, + "content":dic['text'], + "segments":dic['segments'], + "language":dic['language'] + } + info(f'{dic=}, {ret=}') + return ret + except Exception as e: + exception(f'{e}') + print_exc() + return { + 'status':'error', + 'message':f'{e}' + } + +g = ServerEnv() +g.generate = generate diff --git a/app/sensevoice/__init__.py b/app/sensevoice/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/sensevoice/load_model.py b/app/sensevoice/load_model.py new file mode 100644 index 0000000..e03c21f --- /dev/null +++ b/app/sensevoice/load_model.py @@ -0,0 +1,22 @@ +from ahserver.serverenv import ServerEnv +from appPublic.worker import awaitify +from appPublic.jsonConfig import getConfig, get_definition +from funasr import AutoModel +from funasr.utils.postprocess_utils import rich_transcription_postprocess + +class SenseVoiceBase: + def __init__(self): + model_dir = get_definition('sensevoice_path') + self.model = AutoModel(model=model_dir) + + def _stt(self, x): + pass + stt = awaitify(_stt) + +class SenseVoiceFile(SenseVoiceBase): + def _stt(self, filepath): + res = this.model.generate(input=filepath,language="auto", + use_itn=True, + batch_size_s=60 + ) + return rich_transcription_postprocess(res[0]["text"]) diff --git a/app/whisper_model.py b/app/whisper_model.py new file mode 100644 index 0000000..b212d94 --- /dev/null +++ b/app/whisper_model.py @@ -0,0 +1,54 @@ +from ahserver.serverenv import ServerEnv +from appPublic.worker import awaitify +from appPublic.log import info, debug, warning, error, exception, critical + +from ahserver.globalEnv import get_definition +import numpy as np +import base64 +import whisper + +# 编码 +def base64_encode(text): + text_bytes = text.encode('utf-8') + encoded_bytes = base64.b64encode(text_bytes) + encoded_text = encoded_bytes.decode('utf-8') + return encoded_text + +# 解码 +def base64_decode(encoded_text): + encoded_bytes = encoded_text.encode('utf-8') + decoded_bytes = base64.b64decode(encoded_bytes) + decoded_text = decoded_bytes.decode('utf-8') + return decoded_text + +class WhisperBase: + def __init__(self): + model_name = get_definition('whisper_model') + self.model = whisper.load_model(model_name) + + def _stt(self, filepath): + e = Exception(f'{filepath=} WhisperBase can not use') + exception(f'{e=}') + raise e + + +class WhisperFile(WhisperBase): + def _stt(self, filepath): + return self.model.transcribe(filepath) + + stt = awaitify(_stt) + +class WhisperBase64(WhisperBase): + def _stt(self, audio): + audiolist = audio.values() + nparr = np.array(audiolist, dtype=np.float32) + nparr=np.vstack(nparr).astype(np.float) + """ + + raw = base64_decode(audio_base64) + ndarr = np.frombuffer(raw, dtype=np.float32) + """ + info(f'ndarr={nparr}') + return self.model.transcribe(nparr) + + stt = awaitify(_stt) diff --git a/conf/config.json b/conf/config.json new file mode 100755 index 0000000..17f9254 --- /dev/null +++ b/conf/config.json @@ -0,0 +1,86 @@ +{ + "logger":{ + "name":"asr", + "levelname":"info", + "logfile":"$[workdir]$/logs/asr.log" + }, + "definitions":{ + "whisper_model":"/d/ymq/osc/models/whisper/large-v3.pt" + }, + "filesroot":"$[workdir]$/files", + "databases":{ + "sage":{ + "driver":"aiomysql", + "async_mode":true, + "coding":"utf8", + "maxconn":100, + "dbname":"sage", + "kwargs":{ + "user":"test", + "db":"sage", + "password":"QUZVcXg5V1p1STMybG5Ia6mX9D0v7+g=", + "host":"localhost" + } + } + }, + "website":{ + "paths":[ + ["$[workdir]$/wwwroot",""] + ], + "client_max_size":10000, + "host":"0.0.0.0", + "port":9091, + "coding":"utf-8", + "ssl_gg":{ + "crtfile":"$[workdir]$/conf/www.bsppo.com.pem", + "keyfile":"$[workdir]$/conf/www.bsppo.com.key" + }, + "indexes":[ + "index.html", + "index.tmpl", + "index.ui", + "index.dspy", + "index.md" + ], + "startswiths":[ + { + "leading":"/generate", + "registerfunction":"generate" + } + ], + "processors":[ + [".ws","ws"], + [".xterm","xterm"], + [".proxy","proxy"], + [".llm", "llm"], + [".llms", "llms"], + [".llma", "llma"], + [".xlsxds","xlsxds"], + [".sqlds","sqlds"], + [".tmpl.js","tmpl"], + [".tmpl.css","tmpl"], + [".html.tmpl","tmpl"], + [".bcrud", "bricks_crud"], + [".tmpl","tmpl"], + [".bui","bui"], + [".ui","bui"], + [".dspy","dspy"], + [".md","md"] + ], + "rsakey":{ + "privatekey":"$[workdir]$/conf/rsa_private_key.pem", + "publickey":"$[workdir]$/conf/rsa_public_key.pem" + }, + "session_max_time":3000, + "session_issue_time":2500, + "session_redis_oops":{ + "url":"redis://127.0.0.1:6379" + } + }, + "langMapping":{ + "zh-Hans-CN":"zh-cn", + "zh-CN":"zh-cn", + "en-us":"en", + "en-US":"en" + } +} diff --git a/files/README.md b/files/README.md new file mode 100644 index 0000000..e69de29 diff --git a/i18n/README.md b/i18n/README.md new file mode 100644 index 0000000..e69de29 diff --git a/logs/README.md b/logs/README.md new file mode 100644 index 0000000..e69de29 diff --git a/plugins/README.md b/plugins/README.md new file mode 100644 index 0000000..e69de29 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..4076923 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +openai-whisper + diff --git a/wwwroot/README.md b/wwwroot/README.md new file mode 100644 index 0000000..e69de29