Use generated short id for llm asr tts

This commit is contained in:
Xin Wang
2026-02-12 19:05:50 +08:00
parent 14991af1bf
commit 28ca003662
6 changed files with 98 additions and 27 deletions

17
api/app/id_generator.py Normal file
View File

@@ -0,0 +1,17 @@
import uuid
from typing import Any, Type
from sqlalchemy.orm import Session
def short_id(prefix: str, size: int = 8) -> str:
return f"{prefix}_{uuid.uuid4().hex[:size]}"
def unique_short_id(prefix: str, db: Session, model_cls: Type[Any], size: int = 8) -> str:
for _ in range(10):
candidate = short_id(prefix, size=size)
exists = db.query(model_cls.id).filter(model_cls.id == candidate).first()
if not exists:
return candidate
raise RuntimeError(f"failed to generate unique id for {model_cls.__name__}")

View File

@@ -1,6 +1,5 @@
import os import os
import time import time
import uuid
from typing import List, Optional from typing import List, Optional
import httpx import httpx
@@ -8,6 +7,7 @@ from fastapi import APIRouter, Depends, File, Form, HTTPException, UploadFile
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from ..db import get_db from ..db import get_db
from ..id_generator import unique_short_id
from ..models import ASRModel from ..models import ASRModel
from ..schemas import ( from ..schemas import (
ASRModelCreate, ASRModelUpdate, ASRModelOut, ASRModelCreate, ASRModelUpdate, ASRModelOut,
@@ -72,7 +72,7 @@ def get_asr_model(id: str, db: Session = Depends(get_db)):
def create_asr_model(data: ASRModelCreate, db: Session = Depends(get_db)): def create_asr_model(data: ASRModelCreate, db: Session = Depends(get_db)):
"""创建ASR模型""" """创建ASR模型"""
asr_model = ASRModel( asr_model = ASRModel(
id=data.id or str(uuid.uuid4())[:8], id=unique_short_id("asr", db, ASRModel),
user_id=1, # 默认用户 user_id=1, # 默认用户
name=data.name, name=data.name,
vendor=data.vendor, vendor=data.vendor,

View File

@@ -1,12 +1,12 @@
from fastapi import APIRouter, Depends, HTTPException from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from typing import List, Optional from typing import List, Optional
import uuid
import httpx import httpx
import time import time
from datetime import datetime from datetime import datetime
from ..db import get_db from ..db import get_db
from ..id_generator import unique_short_id
from ..models import LLMModel from ..models import LLMModel
from ..schemas import ( from ..schemas import (
LLMModelCreate, LLMModelUpdate, LLMModelOut, LLMModelCreate, LLMModelUpdate, LLMModelOut,
@@ -53,7 +53,7 @@ def get_llm_model(id: str, db: Session = Depends(get_db)):
def create_llm_model(data: LLMModelCreate, db: Session = Depends(get_db)): def create_llm_model(data: LLMModelCreate, db: Session = Depends(get_db)):
"""创建LLM模型""" """创建LLM模型"""
llm_model = LLMModel( llm_model = LLMModel(
id=data.id or str(uuid.uuid4())[:8], id=unique_short_id("llm", db, LLMModel),
user_id=1, # 默认用户 user_id=1, # 默认用户
name=data.name, name=data.name,
vendor=data.vendor, vendor=data.vendor,

View File

@@ -1,6 +1,5 @@
import base64 import base64
import os import os
import uuid
from typing import Optional from typing import Optional
import httpx import httpx
@@ -8,6 +7,7 @@ from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from ..db import get_db from ..db import get_db
from ..id_generator import unique_short_id
from ..models import Voice from ..models import Voice
from ..schemas import VoiceCreate, VoiceOut, VoicePreviewRequest, VoicePreviewResponse, VoiceUpdate from ..schemas import VoiceCreate, VoiceOut, VoicePreviewRequest, VoicePreviewResponse, VoiceUpdate
@@ -78,7 +78,7 @@ def create_voice(data: VoiceCreate, db: Session = Depends(get_db)):
voice_key = raw_id if ":" in raw_id else f"{model}:{raw_id}" voice_key = raw_id if ":" in raw_id else f"{model}:{raw_id}"
voice = Voice( voice = Voice(
id=data.id or str(uuid.uuid4())[:8], id=unique_short_id("tts", db, Voice),
user_id=1, user_id=1,
name=data.name, name=data.name,
vendor=vendor, vendor=vendor,

View File

@@ -9,8 +9,29 @@ from contextlib import contextmanager
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from app.db import Base, engine, DATABASE_URL from app.db import Base, engine, DATABASE_URL
from app.id_generator import short_id
from app.models import Voice, Assistant, KnowledgeBase, Workflow, LLMModel, ASRModel, KnowledgeDocument from app.models import Voice, Assistant, KnowledgeBase, Workflow, LLMModel, ASRModel, KnowledgeDocument
VOICE_MODEL = "FunAudioLLM/CosyVoice2-0.5B"
SEED_VOICE_IDS = {
"alex": short_id("tts"),
"david": short_id("tts"),
"bella": short_id("tts"),
"claire": short_id("tts"),
}
SEED_LLM_IDS = {
"deepseek_chat": short_id("llm"),
"glm_4": short_id("llm"),
"embedding_3_small": short_id("llm"),
}
SEED_ASR_IDS = {
"sensevoice_small": short_id("asr"),
"telespeech_asr": short_id("asr"),
}
def ensure_db_dir(): def ensure_db_dir():
"""确保 SQLite 数据目录存在。""" """确保 SQLite 数据目录存在。"""
@@ -105,15 +126,51 @@ def init_default_data():
# 参考: https://docs.siliconflow.cn/cn/api-reference/audio/create-speech # 参考: https://docs.siliconflow.cn/cn/api-reference/audio/create-speech
voices = [ voices = [
# 男声 (Male Voices) # 男声 (Male Voices)
Voice(id="alex", name="Alex", vendor="OpenAI Compatible", gender="Male", language="en", Voice(
description="Steady male voice.", is_system=True), id=SEED_VOICE_IDS["alex"],
Voice(id="david", name="David", vendor="OpenAI Compatible", gender="Male", language="en", name="Alex",
description="Cheerful male voice.", is_system=True), vendor="OpenAI Compatible",
gender="Male",
language="en",
description="Steady male voice.",
model=VOICE_MODEL,
voice_key=f"{VOICE_MODEL}:alex",
is_system=True,
),
Voice(
id=SEED_VOICE_IDS["david"],
name="David",
vendor="OpenAI Compatible",
gender="Male",
language="en",
description="Cheerful male voice.",
model=VOICE_MODEL,
voice_key=f"{VOICE_MODEL}:david",
is_system=True,
),
# 女声 (Female Voices) # 女声 (Female Voices)
Voice(id="bella", name="Bella", vendor="OpenAI Compatible", gender="Female", language="en", Voice(
description="Passionate female voice.", is_system=True), id=SEED_VOICE_IDS["bella"],
Voice(id="claire", name="Claire", vendor="OpenAI Compatible", gender="Female", language="en", name="Bella",
description="Gentle female voice.", is_system=True), vendor="OpenAI Compatible",
gender="Female",
language="en",
description="Passionate female voice.",
model=VOICE_MODEL,
voice_key=f"{VOICE_MODEL}:bella",
is_system=True,
),
Voice(
id=SEED_VOICE_IDS["claire"],
name="Claire",
vendor="OpenAI Compatible",
gender="Female",
language="en",
description="Gentle female voice.",
model=VOICE_MODEL,
voice_key=f"{VOICE_MODEL}:claire",
is_system=True,
),
] ]
seed_if_empty(db, Voice, voices, "✅ 默认声音数据已初始化 (OpenAI Compatible CosyVoice 2.0)") seed_if_empty(db, Voice, voices, "✅ 默认声音数据已初始化 (OpenAI Compatible CosyVoice 2.0)")
@@ -144,14 +201,14 @@ def init_default_assistants():
prompt="你是一个友好的AI助手请用简洁清晰的语言回答用户的问题。", prompt="你是一个友好的AI助手请用简洁清晰的语言回答用户的问题。",
language="zh", language="zh",
voice_output_enabled=True, voice_output_enabled=True,
voice="anna", voice=SEED_VOICE_IDS["bella"],
speed=1.0, speed=1.0,
hotwords=[], hotwords=[],
tools=["current_time"], tools=["current_time"],
interruption_sensitivity=500, interruption_sensitivity=500,
config_mode="platform", config_mode="platform",
llm_model_id="deepseek-chat", llm_model_id=SEED_LLM_IDS["deepseek_chat"],
asr_model_id="paraformer-v2", asr_model_id=SEED_ASR_IDS["sensevoice_small"],
), ),
Assistant( Assistant(
id="customer_service", id="customer_service",
@@ -162,7 +219,7 @@ def init_default_assistants():
prompt="你是一个专业的客服人员,耐心解答客户问题,提供优质的服务体验。", prompt="你是一个专业的客服人员,耐心解答客户问题,提供优质的服务体验。",
language="zh", language="zh",
voice_output_enabled=True, voice_output_enabled=True,
voice="bella", voice=SEED_VOICE_IDS["claire"],
speed=1.0, speed=1.0,
hotwords=["客服", "投诉", "咨询"], hotwords=["客服", "投诉", "咨询"],
tools=["current_time"], tools=["current_time"],
@@ -178,7 +235,7 @@ def init_default_assistants():
prompt="You are a friendly English tutor. Help users practice English conversation and explain grammar points clearly.", prompt="You are a friendly English tutor. Help users practice English conversation and explain grammar points clearly.",
language="en", language="en",
voice_output_enabled=True, voice_output_enabled=True,
voice="alex", voice=SEED_VOICE_IDS["alex"],
speed=1.0, speed=1.0,
hotwords=["grammar", "vocabulary", "practice"], hotwords=["grammar", "vocabulary", "practice"],
tools=["current_time"], tools=["current_time"],
@@ -257,7 +314,7 @@ def init_default_llm_models():
with db_session() as db: with db_session() as db:
llm_models = [ llm_models = [
LLMModel( LLMModel(
id="deepseek-chat", id=SEED_LLM_IDS["deepseek_chat"],
user_id=1, user_id=1,
name="DeepSeek Chat", name="DeepSeek Chat",
vendor="OpenAI Compatible", vendor="OpenAI Compatible",
@@ -270,7 +327,7 @@ def init_default_llm_models():
enabled=True, enabled=True,
), ),
LLMModel( LLMModel(
id="glm-4", id=SEED_LLM_IDS["glm_4"],
user_id=1, user_id=1,
name="GLM-4", name="GLM-4",
vendor="ZhipuAI", vendor="ZhipuAI",
@@ -283,7 +340,7 @@ def init_default_llm_models():
enabled=True, enabled=True,
), ),
LLMModel( LLMModel(
id="text-embedding-3-small", id=SEED_LLM_IDS["embedding_3_small"],
user_id=1, user_id=1,
name="Embedding 3 Small", name="Embedding 3 Small",
vendor="OpenAI Compatible", vendor="OpenAI Compatible",
@@ -302,7 +359,7 @@ def init_default_asr_models():
with db_session() as db: with db_session() as db:
asr_models = [ asr_models = [
ASRModel( ASRModel(
id="FunAudioLLM/SenseVoiceSmall", id=SEED_ASR_IDS["sensevoice_small"],
user_id=1, user_id=1,
name="FunAudioLLM/SenseVoiceSmall", name="FunAudioLLM/SenseVoiceSmall",
vendor="OpenAI Compatible", vendor="OpenAI Compatible",
@@ -316,7 +373,7 @@ def init_default_asr_models():
enabled=True, enabled=True,
), ),
ASRModel( ASRModel(
id="TeleAI/TeleSpeechASR", id=SEED_ASR_IDS["telespeech_asr"],
user_id=1, user_id=1,
name="TeleAI/TeleSpeechASR", name="TeleAI/TeleSpeechASR",
vendor="OpenAI Compatible", vendor="OpenAI Compatible",

View File

@@ -297,7 +297,6 @@ export const fetchVoices = async (): Promise<Voice[]> => {
export const createVoice = async (data: Partial<Voice>): Promise<Voice> => { export const createVoice = async (data: Partial<Voice>): Promise<Voice> => {
const payload = { const payload = {
id: data.id || undefined,
name: data.name || 'New Voice', name: data.name || 'New Voice',
vendor: data.vendor || 'OpenAI Compatible', vendor: data.vendor || 'OpenAI Compatible',
gender: data.gender || 'Female', gender: data.gender || 'Female',
@@ -359,7 +358,6 @@ export const fetchASRModels = async (): Promise<ASRModel[]> => {
export const createASRModel = async (data: Partial<ASRModel>): Promise<ASRModel> => { export const createASRModel = async (data: Partial<ASRModel>): Promise<ASRModel> => {
const payload = { const payload = {
id: data.id || undefined,
name: data.name || 'New ASR Model', name: data.name || 'New ASR Model',
vendor: data.vendor || 'OpenAI Compatible', vendor: data.vendor || 'OpenAI Compatible',
language: data.language || 'zh', language: data.language || 'zh',
@@ -450,7 +448,6 @@ export const fetchLLMModels = async (): Promise<LLMModel[]> => {
export const createLLMModel = async (data: Partial<LLMModel>): Promise<LLMModel> => { export const createLLMModel = async (data: Partial<LLMModel>): Promise<LLMModel> => {
const payload = { const payload = {
id: data.id || undefined,
name: data.name || 'New LLM Model', name: data.name || 'New LLM Model',
vendor: data.vendor || 'OpenAI Compatible', vendor: data.vendor || 'OpenAI Compatible',
type: data.type || 'text', type: data.type || 'text',