|
|
|
|
|
""" |
|
|
Extra gRPC server for HuggingFace AutoModel models. |
|
|
""" |
|
|
from concurrent import futures |
|
|
|
|
|
import argparse |
|
|
import signal |
|
|
import sys |
|
|
import os |
|
|
from threading import Thread |
|
|
import asyncio |
|
|
|
|
|
import time |
|
|
import backend_pb2 |
|
|
import backend_pb2_grpc |
|
|
|
|
|
import grpc |
|
|
import torch |
|
|
import torch.cuda |
|
|
|
|
|
|
|
|
XPU=os.environ.get("XPU", "0") == "1" |
|
|
from transformers import AutoTokenizer, AutoModel, set_seed, TextIteratorStreamer, StoppingCriteriaList, StopStringCriteria, MambaConfig, MambaForCausalLM |
|
|
from transformers import AutoProcessor, MusicgenForConditionalGeneration, DiaForConditionalGeneration |
|
|
from scipy.io import wavfile |
|
|
import outetts |
|
|
from sentence_transformers import SentenceTransformer |
|
|
|
|
|
|
|
|
_ONE_DAY_IN_SECONDS = 60 * 60 * 24 |
|
|
|
|
|
|
|
|
MAX_WORKERS = int(os.environ.get('PYTHON_GRPC_MAX_WORKERS', '1')) |
|
|
|
|
|
|
|
|
def mean_pooling(model_output, attention_mask): |
|
|
""" |
|
|
Mean pooling to get sentence embeddings. See: |
|
|
https://huggingface.co/sentence-transformers/paraphrase-distilroberta-base-v1 |
|
|
""" |
|
|
token_embeddings = model_output[0] |
|
|
input_mask_expanded = attention_mask.unsqueeze(-1).expand(token_embeddings.size()).float() |
|
|
sum_embeddings = torch.sum(token_embeddings * input_mask_expanded, 1) |
|
|
sum_mask = torch.clamp(input_mask_expanded.sum(1), min=1e-9) |
|
|
return sum_embeddings / sum_mask |
|
|
|
|
|
|
|
|
class BackendServicer(backend_pb2_grpc.BackendServicer): |
|
|
""" |
|
|
A gRPC servicer for the backend service. |
|
|
|
|
|
This class implements the gRPC methods for the backend service, including Health, LoadModel, and Embedding. |
|
|
""" |
|
|
def Health(self, request, context): |
|
|
""" |
|
|
A gRPC method that returns the health status of the backend service. |
|
|
|
|
|
Args: |
|
|
request: A HealthRequest object that contains the request parameters. |
|
|
context: A grpc.ServicerContext object that provides information about the RPC. |
|
|
|
|
|
Returns: |
|
|
A Reply object that contains the health status of the backend service. |
|
|
""" |
|
|
return backend_pb2.Reply(message=bytes("OK", 'utf-8')) |
|
|
|
|
|
def LoadModel(self, request, context): |
|
|
""" |
|
|
A gRPC method that loads a model into memory. |
|
|
|
|
|
Args: |
|
|
request: A LoadModelRequest object that contains the request parameters. |
|
|
context: A grpc.ServicerContext object that provides information about the RPC. |
|
|
|
|
|
Returns: |
|
|
A Result object that contains the result of the LoadModel operation. |
|
|
""" |
|
|
|
|
|
model_name = request.Model |
|
|
|
|
|
|
|
|
if os.path.exists(request.ModelFile): |
|
|
model_name = request.ModelFile |
|
|
|
|
|
compute = torch.float16 |
|
|
if request.F16Memory == True: |
|
|
compute=torch.bfloat16 |
|
|
|
|
|
self.CUDA = torch.cuda.is_available() |
|
|
self.OV=False |
|
|
self.OuteTTS=False |
|
|
self.DiaTTS=False |
|
|
self.SentenceTransformer = False |
|
|
|
|
|
device_map="cpu" |
|
|
mps_available = hasattr(torch.backends, "mps") and torch.backends.mps.is_available() |
|
|
if mps_available: |
|
|
device_map = "mps" |
|
|
quantization = None |
|
|
autoTokenizer = True |
|
|
|
|
|
|
|
|
self.options = {} |
|
|
options = request.Options |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for opt in options: |
|
|
if ":" not in opt: |
|
|
continue |
|
|
key, value = opt.split(":", 1) |
|
|
|
|
|
try: |
|
|
if "." in value: |
|
|
value = float(value) |
|
|
else: |
|
|
value = int(value) |
|
|
except ValueError: |
|
|
|
|
|
pass |
|
|
self.options[key] = value |
|
|
|
|
|
print(f"Parsed options: {self.options}", file=sys.stderr) |
|
|
|
|
|
if self.CUDA: |
|
|
from transformers import BitsAndBytesConfig, AutoModelForCausalLM |
|
|
if request.MainGPU: |
|
|
device_map=request.MainGPU |
|
|
else: |
|
|
device_map="cuda:0" |
|
|
if request.Quantization == "bnb_4bit": |
|
|
quantization = BitsAndBytesConfig( |
|
|
load_in_4bit = True, |
|
|
bnb_4bit_compute_dtype = compute, |
|
|
bnb_4bit_quant_type = "nf4", |
|
|
bnb_4bit_use_double_quant = True, |
|
|
load_in_8bit = False, |
|
|
) |
|
|
elif request.Quantization == "bnb_8bit": |
|
|
quantization = BitsAndBytesConfig( |
|
|
load_in_4bit=False, |
|
|
bnb_4bit_compute_dtype = None, |
|
|
load_in_8bit=True, |
|
|
) |
|
|
|
|
|
try: |
|
|
if request.Type == "AutoModelForCausalLM": |
|
|
if XPU: |
|
|
import intel_extension_for_pytorch as ipex |
|
|
from intel_extension_for_transformers.transformers.modeling import AutoModelForCausalLM |
|
|
|
|
|
device_map="xpu" |
|
|
compute=torch.float16 |
|
|
if request.Quantization == "xpu_4bit": |
|
|
xpu_4bit = True |
|
|
xpu_8bit = False |
|
|
elif request.Quantization == "xpu_8bit": |
|
|
xpu_4bit = False |
|
|
xpu_8bit = True |
|
|
else: |
|
|
xpu_4bit = False |
|
|
xpu_8bit = False |
|
|
self.model = AutoModelForCausalLM.from_pretrained(model_name, |
|
|
trust_remote_code=request.TrustRemoteCode, |
|
|
use_safetensors=True, |
|
|
device_map=device_map, |
|
|
load_in_4bit=xpu_4bit, |
|
|
load_in_8bit=xpu_8bit, |
|
|
torch_dtype=compute) |
|
|
else: |
|
|
self.model = AutoModelForCausalLM.from_pretrained(model_name, |
|
|
trust_remote_code=request.TrustRemoteCode, |
|
|
use_safetensors=True, |
|
|
quantization_config=quantization, |
|
|
device_map=device_map, |
|
|
torch_dtype=compute) |
|
|
elif request.Type == "OVModelForCausalLM": |
|
|
from optimum.intel.openvino import OVModelForCausalLM |
|
|
from openvino.runtime import Core |
|
|
|
|
|
if request.MainGPU: |
|
|
device_map=request.MainGPU |
|
|
else: |
|
|
device_map="AUTO" |
|
|
devices = Core().available_devices |
|
|
if "GPU" in " ".join(devices): |
|
|
device_map="AUTO:GPU" |
|
|
|
|
|
|
|
|
if "CPU" or "NPU" in device_map: |
|
|
if "-CPU" or "-NPU" not in device_map: |
|
|
ovconfig={"PERFORMANCE_HINT": "CUMULATIVE_THROUGHPUT"} |
|
|
else: |
|
|
ovconfig={"PERFORMANCE_HINT": "CUMULATIVE_THROUGHPUT","GPU_DISABLE_WINOGRAD_CONVOLUTION": "YES"} |
|
|
self.model = OVModelForCausalLM.from_pretrained(model_name, |
|
|
compile=True, |
|
|
trust_remote_code=request.TrustRemoteCode, |
|
|
ov_config=ovconfig, |
|
|
device=device_map) |
|
|
self.OV = True |
|
|
elif request.Type == "OVModelForFeatureExtraction": |
|
|
from optimum.intel.openvino import OVModelForFeatureExtraction |
|
|
from openvino.runtime import Core |
|
|
|
|
|
if request.MainGPU: |
|
|
device_map=request.MainGPU |
|
|
else: |
|
|
device_map="AUTO" |
|
|
devices = Core().available_devices |
|
|
if "GPU" in " ".join(devices): |
|
|
device_map="AUTO:GPU" |
|
|
|
|
|
|
|
|
if "CPU" or "NPU" in device_map: |
|
|
if "-CPU" or "-NPU" not in device_map: |
|
|
ovconfig={"PERFORMANCE_HINT": "CUMULATIVE_THROUGHPUT"} |
|
|
else: |
|
|
ovconfig={"PERFORMANCE_HINT": "CUMULATIVE_THROUGHPUT","GPU_DISABLE_WINOGRAD_CONVOLUTION": "YES"} |
|
|
self.model = OVModelForFeatureExtraction.from_pretrained(model_name, |
|
|
compile=True, |
|
|
trust_remote_code=request.TrustRemoteCode, |
|
|
ov_config=ovconfig, |
|
|
export=True, |
|
|
device=device_map) |
|
|
self.OV = True |
|
|
elif request.Type == "MusicgenForConditionalGeneration": |
|
|
autoTokenizer = False |
|
|
self.processor = AutoProcessor.from_pretrained(model_name) |
|
|
self.model = MusicgenForConditionalGeneration.from_pretrained(model_name) |
|
|
elif request.Type == "DiaForConditionalGeneration": |
|
|
autoTokenizer = False |
|
|
print("DiaForConditionalGeneration", file=sys.stderr) |
|
|
self.processor = AutoProcessor.from_pretrained(model_name) |
|
|
self.model = DiaForConditionalGeneration.from_pretrained(model_name) |
|
|
if self.CUDA: |
|
|
self.model = self.model.to("cuda") |
|
|
self.processor = self.processor.to("cuda") |
|
|
print("DiaForConditionalGeneration loaded", file=sys.stderr) |
|
|
self.DiaTTS = True |
|
|
elif request.Type == "OuteTTS": |
|
|
autoTokenizer = False |
|
|
options = request.Options |
|
|
MODELNAME = "OuteAI/OuteTTS-0.3-1B" |
|
|
TOKENIZER = "OuteAI/OuteTTS-0.3-1B" |
|
|
VERSION = "0.3" |
|
|
SPEAKER = "en_male_1" |
|
|
for opt in options: |
|
|
if opt.startswith("tokenizer:"): |
|
|
TOKENIZER = opt.split(":")[1] |
|
|
break |
|
|
if opt.startswith("version:"): |
|
|
VERSION = opt.split(":")[1] |
|
|
break |
|
|
if opt.startswith("speaker:"): |
|
|
SPEAKER = opt.split(":")[1] |
|
|
break |
|
|
|
|
|
if model_name != "": |
|
|
MODELNAME = model_name |
|
|
|
|
|
|
|
|
model_config = outetts.HFModelConfig_v2( |
|
|
model_path=MODELNAME, |
|
|
tokenizer_path=TOKENIZER |
|
|
) |
|
|
|
|
|
self.interface = outetts.InterfaceHF(model_version=VERSION, cfg=model_config) |
|
|
self.OuteTTS = True |
|
|
|
|
|
self.interface.print_default_speakers() |
|
|
if request.AudioPath: |
|
|
if os.path.isabs(request.AudioPath): |
|
|
self.AudioPath = request.AudioPath |
|
|
else: |
|
|
self.AudioPath = os.path.join(request.ModelPath, request.AudioPath) |
|
|
self.speaker = self.interface.create_speaker(audio_path=self.AudioPath) |
|
|
else: |
|
|
self.speaker = self.interface.load_default_speaker(name=SPEAKER) |
|
|
elif request.Type == "SentenceTransformer": |
|
|
autoTokenizer = False |
|
|
self.model = SentenceTransformer(model_name, trust_remote_code=request.TrustRemoteCode) |
|
|
self.SentenceTransformer = True |
|
|
elif request.Type == "Mamba": |
|
|
autoTokenizer = False |
|
|
self.tokenizer = AutoTokenizer.from_pretrained(model_name) |
|
|
self.model = MambaForCausalLM.from_pretrained(model_name) |
|
|
else: |
|
|
print("Automodel", file=sys.stderr) |
|
|
self.model = AutoModel.from_pretrained(model_name, |
|
|
trust_remote_code=request.TrustRemoteCode, |
|
|
use_safetensors=True, |
|
|
quantization_config=quantization, |
|
|
device_map=device_map, |
|
|
torch_dtype=compute) |
|
|
if request.ContextSize > 0: |
|
|
self.max_tokens = request.ContextSize |
|
|
elif hasattr(self.model, 'config') and hasattr(self.model.config, 'max_position_embeddings'): |
|
|
self.max_tokens = self.model.config.max_position_embeddings |
|
|
else: |
|
|
self.max_tokens = self.options.get("max_new_tokens", 512) |
|
|
|
|
|
if autoTokenizer: |
|
|
self.tokenizer = AutoTokenizer.from_pretrained(model_name, use_safetensors=True) |
|
|
self.XPU = False |
|
|
|
|
|
if XPU and self.OV == False: |
|
|
self.XPU = True |
|
|
try: |
|
|
print("Optimizing model", model_name, "to XPU.", file=sys.stderr) |
|
|
self.model = ipex.optimize_transformers(self.model, inplace=True, dtype=torch.float16, device="xpu") |
|
|
except Exception as err: |
|
|
print("Not using XPU:", err, file=sys.stderr) |
|
|
|
|
|
except Exception as err: |
|
|
print("Error:", err, file=sys.stderr) |
|
|
return backend_pb2.Result(success=False, message=f"Unexpected {err=}, {type(err)=}") |
|
|
|
|
|
|
|
|
return backend_pb2.Result(message="Model loaded successfully", success=True) |
|
|
|
|
|
def Embedding(self, request, context): |
|
|
""" |
|
|
A gRPC method that calculates embeddings for a given sentence. |
|
|
|
|
|
Args: |
|
|
request: An EmbeddingRequest object that contains the request parameters. |
|
|
context: A grpc.ServicerContext object that provides information about the RPC. |
|
|
|
|
|
Returns: |
|
|
An EmbeddingResult object that contains the calculated embeddings. |
|
|
""" |
|
|
|
|
|
set_seed(request.Seed) |
|
|
|
|
|
max_length = 512 |
|
|
if request.Tokens != 0: |
|
|
max_length = request.Tokens |
|
|
|
|
|
embeds = None |
|
|
|
|
|
if self.SentenceTransformer: |
|
|
print("Calculated embeddings for: " + request.Embeddings, file=sys.stderr) |
|
|
embeds = self.model.encode(request.Embeddings) |
|
|
else: |
|
|
encoded_input = self.tokenizer(request.Embeddings, padding=True, truncation=True, max_length=max_length, return_tensors="pt") |
|
|
|
|
|
|
|
|
if self.CUDA: |
|
|
encoded_input = encoded_input.to("cuda") |
|
|
|
|
|
with torch.no_grad(): |
|
|
model_output = self.model(**encoded_input) |
|
|
|
|
|
|
|
|
sentence_embeddings = mean_pooling(model_output, encoded_input['attention_mask']) |
|
|
embeds = sentence_embeddings[0] |
|
|
return backend_pb2.EmbeddingResult(embeddings=embeds) |
|
|
|
|
|
async def _predict(self, request, context, streaming=False): |
|
|
set_seed(request.Seed) |
|
|
if request.TopP < 0 or request.TopP > 1: |
|
|
request.TopP = 1 |
|
|
|
|
|
if request.TopK <= 0: |
|
|
request.TopK = 50 |
|
|
|
|
|
if request.Temperature > 0 : |
|
|
sample=True |
|
|
else: |
|
|
sample=False |
|
|
request.TopP == None |
|
|
request.TopK == None |
|
|
request.Temperature == None |
|
|
|
|
|
prompt = request.Prompt |
|
|
if not request.Prompt and request.UseTokenizerTemplate and request.Messages: |
|
|
prompt = self.tokenizer.apply_chat_template(request.Messages, tokenize=False, add_generation_prompt=True) |
|
|
|
|
|
inputs = self.tokenizer(prompt, return_tensors="pt") |
|
|
|
|
|
if request.Tokens > 0: |
|
|
max_tokens = request.Tokens |
|
|
else: |
|
|
max_tokens = self.max_tokens - inputs["input_ids"].size()[inputs["input_ids"].dim()-1] |
|
|
|
|
|
if self.CUDA: |
|
|
inputs = inputs.to("cuda") |
|
|
if XPU and self.OV == False: |
|
|
inputs = inputs.to("xpu") |
|
|
streaming = False |
|
|
|
|
|
criteria=[] |
|
|
if request.StopPrompts: |
|
|
criteria = StoppingCriteriaList( |
|
|
[ |
|
|
StopStringCriteria(tokenizer=self.tokenizer, stop_strings=request.StopPrompts), |
|
|
] |
|
|
) |
|
|
|
|
|
if streaming: |
|
|
streamer=TextIteratorStreamer(self.tokenizer, |
|
|
skip_prompt=True, |
|
|
skip_special_tokens=True) |
|
|
config=dict(inputs, |
|
|
max_new_tokens=max_tokens, |
|
|
temperature=request.Temperature, |
|
|
top_p=request.TopP, |
|
|
top_k=request.TopK, |
|
|
do_sample=sample, |
|
|
attention_mask=inputs["attention_mask"], |
|
|
eos_token_id=self.tokenizer.eos_token_id, |
|
|
pad_token_id=self.tokenizer.eos_token_id, |
|
|
streamer=streamer, |
|
|
stopping_criteria=criteria, |
|
|
use_cache=True, |
|
|
) |
|
|
thread=Thread(target=self.model.generate, kwargs=config) |
|
|
thread.start() |
|
|
generated_text = "" |
|
|
try: |
|
|
for new_text in streamer: |
|
|
generated_text += new_text |
|
|
yield backend_pb2.Reply(message=bytes(new_text, encoding='utf-8')) |
|
|
finally: |
|
|
thread.join() |
|
|
else: |
|
|
if XPU and self.OV == False: |
|
|
outputs = self.model.generate(inputs["input_ids"], |
|
|
max_new_tokens=max_tokens, |
|
|
temperature=request.Temperature, |
|
|
top_p=request.TopP, |
|
|
top_k=request.TopK, |
|
|
do_sample=sample, |
|
|
pad_token=self.tokenizer.eos_token_id) |
|
|
else: |
|
|
outputs = self.model.generate(**inputs, |
|
|
max_new_tokens=max_tokens, |
|
|
temperature=request.Temperature, |
|
|
top_p=request.TopP, |
|
|
top_k=request.TopK, |
|
|
do_sample=sample, |
|
|
eos_token_id=self.tokenizer.eos_token_id, |
|
|
pad_token_id=self.tokenizer.eos_token_id, |
|
|
stopping_criteria=criteria, |
|
|
use_cache=True, |
|
|
) |
|
|
generated_text = self.tokenizer.batch_decode(outputs[:, inputs["input_ids"].shape[1]:], skip_special_tokens=True)[0] |
|
|
|
|
|
if streaming: |
|
|
return |
|
|
|
|
|
yield backend_pb2.Reply(message=bytes(generated_text, encoding='utf-8')) |
|
|
|
|
|
async def Predict(self, request, context): |
|
|
""" |
|
|
Generates text based on the given prompt and sampling parameters. |
|
|
|
|
|
Args: |
|
|
request: The predict request. |
|
|
context: The gRPC context. |
|
|
|
|
|
Returns: |
|
|
backend_pb2.Reply: The predict result. |
|
|
""" |
|
|
gen = self._predict(request, context, streaming=False) |
|
|
res = await gen.__anext__() |
|
|
return res |
|
|
|
|
|
async def PredictStream(self, request, context): |
|
|
""" |
|
|
Generates text based on the given prompt and sampling parameters, and streams the results. |
|
|
|
|
|
Args: |
|
|
request: The predict stream request. |
|
|
context: The gRPC context. |
|
|
|
|
|
Returns: |
|
|
backend_pb2.Result: The predict stream result. |
|
|
""" |
|
|
iterations = self._predict(request, context, streaming=True) |
|
|
try: |
|
|
async for iteration in iterations: |
|
|
yield iteration |
|
|
finally: |
|
|
await iterations.aclose() |
|
|
|
|
|
def SoundGeneration(self, request, context): |
|
|
model_name = request.model |
|
|
try: |
|
|
if self.processor is None: |
|
|
if model_name == "": |
|
|
return backend_pb2.Result(success=False, message="request.model is required") |
|
|
self.processor = AutoProcessor.from_pretrained(model_name) |
|
|
if self.model is None: |
|
|
if model_name == "": |
|
|
return backend_pb2.Result(success=False, message="request.model is required") |
|
|
self.model = MusicgenForConditionalGeneration.from_pretrained(model_name) |
|
|
inputs = None |
|
|
if request.text == "": |
|
|
inputs = self.model.get_unconditional_inputs(num_samples=1) |
|
|
elif request.HasField('src'): |
|
|
|
|
|
|
|
|
sample_rate, wsamples = wavfile.read('path_to_your_file.wav') |
|
|
|
|
|
if request.HasField('src_divisor'): |
|
|
wsamples = wsamples[: len(wsamples) // request.src_divisor] |
|
|
|
|
|
inputs = self.processor( |
|
|
audio=wsamples, |
|
|
sampling_rate=sample_rate, |
|
|
text=[request.text], |
|
|
padding=True, |
|
|
return_tensors="pt", |
|
|
) |
|
|
else: |
|
|
inputs = self.processor( |
|
|
text=[request.text], |
|
|
padding=True, |
|
|
return_tensors="pt", |
|
|
) |
|
|
|
|
|
if request.HasField('duration'): |
|
|
tokens = int(request.duration * 51.2) |
|
|
guidance = self.options.get("guidance_scale", 3.0) |
|
|
if request.HasField('temperature'): |
|
|
guidance = request.temperature |
|
|
dosample = self.options.get("do_sample", True) |
|
|
if request.HasField('sample'): |
|
|
dosample = request.sample |
|
|
audio_values = self.model.generate(**inputs, do_sample=dosample, guidance_scale=guidance, max_new_tokens=self.max_tokens) |
|
|
print("[transformers-musicgen] SoundGeneration generated!", file=sys.stderr) |
|
|
sampling_rate = self.model.config.audio_encoder.sampling_rate |
|
|
wavfile.write(request.dst, rate=sampling_rate, data=audio_values[0, 0].numpy()) |
|
|
print("[transformers-musicgen] SoundGeneration saved to", request.dst, file=sys.stderr) |
|
|
print("[transformers-musicgen] SoundGeneration for", file=sys.stderr) |
|
|
print("[transformers-musicgen] SoundGeneration requested tokens", tokens, file=sys.stderr) |
|
|
print(request, file=sys.stderr) |
|
|
except Exception as err: |
|
|
return backend_pb2.Result(success=False, message=f"Unexpected {err=}, {type(err)=}") |
|
|
return backend_pb2.Result(success=True) |
|
|
|
|
|
|
|
|
def CallDiaTTS(self, request, context): |
|
|
""" |
|
|
Generates dialogue audio using the Dia model. |
|
|
|
|
|
Args: |
|
|
request: A TTSRequest containing text dialogue and generation parameters |
|
|
context: The gRPC context |
|
|
|
|
|
Returns: |
|
|
A Result object indicating success or failure |
|
|
""" |
|
|
try: |
|
|
print("[DiaTTS] generating dialogue audio", file=sys.stderr) |
|
|
|
|
|
|
|
|
text = [request.text] |
|
|
|
|
|
|
|
|
inputs = self.processor(text=text, padding=True, return_tensors="pt") |
|
|
|
|
|
|
|
|
generation_params = { |
|
|
**inputs, |
|
|
"max_new_tokens": self.max_tokens, |
|
|
"guidance_scale": self.options.get("guidance_scale", 3.0), |
|
|
"temperature": self.options.get("temperature", 1.8), |
|
|
"top_p": self.options.get("top_p", 0.90), |
|
|
"top_k": self.options.get("top_k", 45) |
|
|
} |
|
|
|
|
|
outputs = self.model.generate(**generation_params) |
|
|
|
|
|
|
|
|
outputs = self.processor.batch_decode(outputs) |
|
|
self.processor.save_audio(outputs, request.dst) |
|
|
|
|
|
print("[DiaTTS] Generated dialogue audio", file=sys.stderr) |
|
|
print("[DiaTTS] Audio saved to", request.dst, file=sys.stderr) |
|
|
print("[DiaTTS] Dialogue generation done", file=sys.stderr) |
|
|
|
|
|
except Exception as err: |
|
|
return backend_pb2.Result(success=False, message=f"Unexpected {err=}, {type(err)=}") |
|
|
return backend_pb2.Result(success=True) |
|
|
|
|
|
|
|
|
def CallOuteTTS(self, request, context): |
|
|
try: |
|
|
print("[OuteTTS] generating TTS", file=sys.stderr) |
|
|
gen_cfg = outetts.GenerationConfig( |
|
|
text="Speech synthesis is the artificial production of human speech.", |
|
|
temperature=self.options.get("temperature", 0.1), |
|
|
repetition_penalty=self.options.get("repetition_penalty", 1.1), |
|
|
max_length=self.max_tokens, |
|
|
speaker=self.speaker, |
|
|
|
|
|
) |
|
|
output = self.interface.generate(config=gen_cfg) |
|
|
print("[OuteTTS] Generated TTS", file=sys.stderr) |
|
|
output.save(request.dst) |
|
|
print("[OuteTTS] TTS done", file=sys.stderr) |
|
|
except Exception as err: |
|
|
return backend_pb2.Result(success=False, message=f"Unexpected {err=}, {type(err)=}") |
|
|
return backend_pb2.Result(success=True) |
|
|
|
|
|
|
|
|
def TTS(self, request, context): |
|
|
if self.OuteTTS: |
|
|
return self.CallOuteTTS(request, context) |
|
|
|
|
|
if self.DiaTTS: |
|
|
print("DiaTTS", file=sys.stderr) |
|
|
return self.CallDiaTTS(request, context) |
|
|
|
|
|
model_name = request.model |
|
|
try: |
|
|
if self.processor is None: |
|
|
if model_name == "": |
|
|
return backend_pb2.Result(success=False, message="request.model is required") |
|
|
self.processor = AutoProcessor.from_pretrained(model_name) |
|
|
if self.model is None: |
|
|
if model_name == "": |
|
|
return backend_pb2.Result(success=False, message="request.model is required") |
|
|
self.model = MusicgenForConditionalGeneration.from_pretrained(model_name) |
|
|
inputs = self.processor( |
|
|
text=[request.text], |
|
|
padding=True, |
|
|
return_tensors="pt", |
|
|
) |
|
|
tokens = self.max_tokens |
|
|
audio_values = self.model.generate(**inputs, max_new_tokens=tokens) |
|
|
print("[transformers-musicgen] TTS generated!", file=sys.stderr) |
|
|
sampling_rate = self.model.config.audio_encoder.sampling_rate |
|
|
wavfile.write(request.dst, rate=sampling_rate, data=audio_values[0, 0].numpy()) |
|
|
print("[transformers-musicgen] TTS saved to", request.dst, file=sys.stderr) |
|
|
print("[transformers-musicgen] TTS for", file=sys.stderr) |
|
|
print(request, file=sys.stderr) |
|
|
except Exception as err: |
|
|
return backend_pb2.Result(success=False, message=f"Unexpected {err=}, {type(err)=}") |
|
|
return backend_pb2.Result(success=True) |
|
|
|
|
|
async def serve(address): |
|
|
|
|
|
server = grpc.aio.server(migration_thread_pool=futures.ThreadPoolExecutor(max_workers=MAX_WORKERS), |
|
|
options=[ |
|
|
('grpc.max_message_length', 50 * 1024 * 1024), |
|
|
('grpc.max_send_message_length', 50 * 1024 * 1024), |
|
|
('grpc.max_receive_message_length', 50 * 1024 * 1024), |
|
|
]) |
|
|
|
|
|
backend_pb2_grpc.add_BackendServicer_to_server(BackendServicer(), server) |
|
|
|
|
|
server.add_insecure_port(address) |
|
|
|
|
|
|
|
|
loop = asyncio.get_event_loop() |
|
|
for sig in (signal.SIGINT, signal.SIGTERM): |
|
|
loop.add_signal_handler( |
|
|
sig, lambda: asyncio.ensure_future(server.stop(5)) |
|
|
) |
|
|
|
|
|
|
|
|
await server.start() |
|
|
print("Server started. Listening on: " + address, file=sys.stderr) |
|
|
|
|
|
await server.wait_for_termination() |
|
|
|
|
|
if __name__ == "__main__": |
|
|
parser = argparse.ArgumentParser(description="Run the gRPC server.") |
|
|
parser.add_argument( |
|
|
"--addr", default="localhost:50051", help="The address to bind the server to." |
|
|
) |
|
|
args = parser.parse_args() |
|
|
|
|
|
asyncio.run(serve(args.addr)) |
|
|
|