first commit

This commit is contained in:
yumoqing 2025-07-16 14:32:28 +08:00
commit 7f50380e52
21 changed files with 283 additions and 0 deletions

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# asr_serve
a local deploy ASR models web server

0
app/README.md Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

40
app/asr.py Normal file
View File

@ -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)

76
app/asr_engine.py Normal file
View File

@ -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

View File

View File

@ -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"])

54
app/whisper_model.py Normal file
View File

@ -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)

86
conf/config.json Executable file
View File

@ -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"
}
}

0
files/README.md Normal file
View File

0
i18n/README.md Normal file
View File

0
logs/README.md Normal file
View File

0
plugins/README.md Normal file
View File

2
requirements.txt Normal file
View File

@ -0,0 +1,2 @@
openai-whisper

0
wwwroot/README.md Normal file
View File