server: compatible with OpenAI API, add model alias
This commit is contained in:
@@ -132,9 +132,10 @@ them by name :
|
||||
```
|
||||
./build/tts-server \
|
||||
--model models/qwen-talker-1.7b-base-Q8_0.gguf \
|
||||
--codec models/qwen-tokenizer-12hz-Q8_0.gguf --port 8080
|
||||
--codec models/qwen-tokenizer-12hz-Q8_0.gguf \
|
||||
--alias qwen3-tts-base --port 8080
|
||||
|
||||
curl -X POST localhost:8080/v1/voices -H "Content-Type: application/json" \
|
||||
curl -X POST localhost:8080/v1/audio/voices -H "Content-Type: application/json" \
|
||||
-d "{\"name\":\"freeman\",\"ref_text\":\"$(cat ref.txt)\",
|
||||
\"spk_b64\":\"$(base64 -w0 ref.spk)\",\"rvq_b64\":\"$(base64 -w0 ref.rvq)\"}"
|
||||
|
||||
|
||||
@@ -617,6 +617,7 @@ Required:
|
||||
--codec <gguf> Codec GGUF (qwen-tokenizer-*.gguf)
|
||||
|
||||
Optional:
|
||||
--alias <name> Report this model id instead of the GGUF file name
|
||||
--host <ip> Listen address (default: 127.0.0.1)
|
||||
--port <n> Listen port (default: 8080)
|
||||
--lang <n> Language label (default: auto)
|
||||
@@ -636,14 +637,14 @@ POST /v1/audio/speech OAI text-to-speech; response_format "pcm"
|
||||
fields keep the engine defaults, temperature
|
||||
0 selects greedy decoding, the subtalker
|
||||
mirrors the talker knobs
|
||||
GET /v1/models single loaded model
|
||||
GET /v1/voices model speakers plus registered cloned voices
|
||||
POST /v1/voices register a cloned voice: {name, ref_text,
|
||||
GET /v1/models single loaded model, using --alias when set
|
||||
GET /v1/audio/voices model speakers plus registered cloned voices
|
||||
POST /v1/audio/voices register a cloned voice: {name, ref_text,
|
||||
wav_b64} extracts server side through
|
||||
qt_extract_voice_ref, {name, ref_text,
|
||||
spk_b64, rvq_b64} takes the pre-extracted
|
||||
latents verbatim
|
||||
DELETE /v1/voices/{name} drop a registered voice
|
||||
DELETE /v1/audio/voices/{name} drop a registered voice
|
||||
GET /health liveness probe
|
||||
```
|
||||
|
||||
|
||||
@@ -4,4 +4,5 @@
|
||||
./build/tts-server \
|
||||
--model models/qwen-talker-1.7b-base-Q8_0.gguf \
|
||||
--codec models/qwen-tokenizer-12hz-Q8_0.gguf \
|
||||
--alias qwen3-tts-base \
|
||||
--host 127.0.0.1 --port 8080 --lang auto
|
||||
|
||||
+10
-10
@@ -10,12 +10,12 @@
|
||||
// Endpoints:
|
||||
// POST /v1/audio/speech OAI text-to-speech
|
||||
// GET /v1/models single loaded model
|
||||
// GET /v1/voices model speakers plus registered cloned voices
|
||||
// POST /v1/voices register a cloned voice: {name, ref_text,
|
||||
// GET /v1/audio/voices model speakers plus registered cloned voices
|
||||
// POST /v1/audio/voices register a cloned voice: {name, ref_text,
|
||||
// wav_b64} extracts server side, {name,
|
||||
// ref_text, spk_b64, rvq_b64} takes
|
||||
// pre-extracted latents verbatim
|
||||
// DELETE /v1/voices/{name} drop a registered voice
|
||||
// DELETE /v1/audio/voices/{name} drop a registered voice
|
||||
// GET /health liveness probe
|
||||
//
|
||||
// Audio out: response_format "pcm" streams s16le 24 kHz mono chunked as it
|
||||
@@ -54,7 +54,7 @@ struct tts_request {
|
||||
float repetition_penalty; // strictly positive
|
||||
};
|
||||
|
||||
// One voice registration parsed from the POST /v1/voices JSON body.
|
||||
// One voice registration parsed from the POST /v1/audio/voices JSON body.
|
||||
// Exactly one payload form is present: wav holds decoded base64 WAV
|
||||
// bytes for server side extraction, or spk plus rvq hold the raw
|
||||
// contents of pre-extracted .spk and .rvq files. ref_text carries the
|
||||
@@ -75,7 +75,7 @@ using tts_sink = std::function<bool(const float * samples, int n_samples)>;
|
||||
// Adapter implemented by each project tool.
|
||||
struct tts_backend {
|
||||
std::string model_id; // reported by GET /v1/models
|
||||
std::vector<std::string> voices; // reported by GET /v1/voices, may be empty
|
||||
std::vector<std::string> voices; // reported by GET /v1/audio/voices, may be empty
|
||||
// Run synthesis. When the request streams, the adapter routes the ABI
|
||||
// on_chunk to sink ; otherwise it pushes the whole buffer once. Returns
|
||||
// the ABI status (0 on success), and fills err with the ABI message on
|
||||
@@ -84,7 +84,7 @@ struct tts_backend {
|
||||
// Voice registry hooks, all optional: a null hook answers 501 on the
|
||||
// matching route. register_voice stores or replaces a cloned voice,
|
||||
// remove_voice drops one (false when absent), registered_voices lists
|
||||
// the current names for GET /v1/voices alongside the model speakers.
|
||||
// the current names for GET /v1/audio/voices alongside the model speakers.
|
||||
std::function<bool(const tts_voice_upload & up, std::string & err)> register_voice;
|
||||
std::function<bool(const std::string & name)> remove_voice;
|
||||
std::function<std::vector<std::string>()> registered_voices;
|
||||
@@ -334,7 +334,7 @@ static bool tts_b64_decode(const std::string & in, std::string & out) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parse the POST /v1/voices body: name plus either wav_b64 or the
|
||||
// Parse the POST /v1/audio/voices body: name plus either wav_b64 or the
|
||||
// spk_b64 / rvq_b64 pair, ref_text optional (enables ICL clone mode).
|
||||
static bool tts_parse_voice_upload(const std::string & body, tts_voice_upload & up, std::string & err) {
|
||||
yyjson_doc * doc = yyjson_read(body.c_str(), body.size(), 0);
|
||||
@@ -517,11 +517,11 @@ static int tts_server_run(const tts_backend & be, const server_config & cfg) {
|
||||
[&be](const httplib::Request & req, httplib::Response & res) { tts_handle_speech(be, req, res); });
|
||||
svr.Get("/v1/models",
|
||||
[&be](const httplib::Request & req, httplib::Response & res) { tts_handle_models(be, req, res); });
|
||||
svr.Get("/v1/voices",
|
||||
svr.Get("/v1/audio/voices",
|
||||
[&be](const httplib::Request & req, httplib::Response & res) { tts_handle_voices(be, req, res); });
|
||||
svr.Post("/v1/voices",
|
||||
svr.Post("/v1/audio/voices",
|
||||
[&be](const httplib::Request & req, httplib::Response & res) { tts_handle_voice_register(be, req, res); });
|
||||
svr.Delete(R"(/v1/voices/(.+))",
|
||||
svr.Delete(R"(/v1/audio/voices/(.+))",
|
||||
[&be](const httplib::Request & req, httplib::Response & res) { tts_handle_voice_delete(be, req, res); });
|
||||
svr.Get("/health", tts_handle_health);
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ static void print_usage(const char * prog) {
|
||||
" --model <gguf> Talker LM GGUF (qwen-talker-*.gguf)\n"
|
||||
" --codec <gguf> Codec GGUF (qwen-tokenizer-*.gguf)\n\n"
|
||||
"Optional:\n"
|
||||
" --alias <name> Report this model id instead of the GGUF file name\n"
|
||||
" --host <ip> Listen address (default: 127.0.0.1)\n"
|
||||
" --port <n> Listen port (default: 8080)\n"
|
||||
" --lang <name> Language label (default: auto)\n"
|
||||
@@ -60,6 +61,7 @@ static std::string basename_of(const char * path) {
|
||||
int main(int argc, char ** argv) {
|
||||
const char * talker_path = NULL;
|
||||
const char * codec_path = NULL;
|
||||
std::string model_alias;
|
||||
std::string lang = "auto";
|
||||
server_config cfg;
|
||||
bool use_fa = true;
|
||||
@@ -71,6 +73,8 @@ int main(int argc, char ** argv) {
|
||||
talker_path = argv[++i];
|
||||
} else if (!std::strcmp(arg, "--codec") && i + 1 < argc) {
|
||||
codec_path = argv[++i];
|
||||
} else if (!std::strcmp(arg, "--alias") && i + 1 < argc) {
|
||||
model_alias = argv[++i];
|
||||
} else if (!std::strcmp(arg, "--host") && i + 1 < argc) {
|
||||
cfg.host = argv[++i];
|
||||
} else if (!std::strcmp(arg, "--port") && i + 1 < argc) {
|
||||
@@ -110,13 +114,13 @@ int main(int argc, char ** argv) {
|
||||
}
|
||||
|
||||
tts_backend be;
|
||||
be.model_id = basename_of(talker_path);
|
||||
be.model_id = model_alias.empty() ? basename_of(talker_path) : model_alias;
|
||||
int n = qt_n_speakers(q);
|
||||
for (int i = 0; i < n; i++) {
|
||||
be.voices.push_back(qt_speaker_name(q, i));
|
||||
}
|
||||
|
||||
// Voice registry: POST /v1/voices stores a cloned voice either from a
|
||||
// Voice registry: POST /v1/audio/voices stores a cloned voice either from a
|
||||
// WAV (server side extraction through qt_extract_voice_ref) or from
|
||||
// pre-extracted .spk / .rvq payloads. Re-registering a name replaces
|
||||
// the previous entry.
|
||||
|
||||
Reference in New Issue
Block a user