Use generated short id for llm asr tts
This commit is contained in:
17
api/app/id_generator.py
Normal file
17
api/app/id_generator.py
Normal 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__}")
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
Reference in New Issue
Block a user