feat(ai): add dedicated digital avatar model config
This commit is contained in:
@@ -23,13 +23,10 @@ from services.token_billing import (
|
||||
reserve_avatar_tokens,
|
||||
settle_reservation,
|
||||
)
|
||||
from services.chat_model_config import ChatModelConfig, get_chat_model_config
|
||||
|
||||
router = APIRouter(tags=["数字分身聊天"])
|
||||
|
||||
CHAT_API_URL = os.getenv("CHAT_API_URL", "https://dashscope.aliyuncs.com/compatible-mode/v1")
|
||||
CHAT_API_KEY = os.getenv("CHAT_API_KEY", "")
|
||||
CHAT_MODEL = os.getenv("CHAT_MODEL", "qwen-plus")
|
||||
CHAT_MAX_OUTPUT_TOKENS = max(128, int(os.getenv("CHAT_MAX_OUTPUT_TOKENS", "1024")))
|
||||
MAX_MESSAGE_LENGTH = 4000
|
||||
MAX_HISTORY_MESSAGES = 10
|
||||
QA_LEXICAL_THRESHOLD = 0.72
|
||||
@@ -287,22 +284,25 @@ def _search_knowledge(db: Session, avatar_id: str, question: str, top_k: int = 5
|
||||
return results
|
||||
|
||||
|
||||
def _call_qwen(messages: list[dict], temperature: float) -> dict:
|
||||
if not CHAT_API_KEY:
|
||||
def _call_qwen(
|
||||
messages: list[dict], temperature: float, model_config: ChatModelConfig | None = None
|
||||
) -> dict:
|
||||
model_config = model_config or get_chat_model_config()
|
||||
if not model_config.api_key:
|
||||
raise RuntimeError("Qwen 模型服务未配置 CHAT_API_KEY")
|
||||
url = f"{CHAT_API_URL.rstrip('/')}/chat/completions"
|
||||
url = f"{model_config.api_base_url}/chat/completions"
|
||||
payload = {
|
||||
"model": CHAT_MODEL,
|
||||
"model": model_config.model,
|
||||
"messages": messages,
|
||||
"temperature": temperature,
|
||||
"max_tokens": CHAT_MAX_OUTPUT_TOKENS,
|
||||
"max_tokens": model_config.max_tokens,
|
||||
}
|
||||
try:
|
||||
response = httpx.post(
|
||||
url,
|
||||
headers={"Authorization": f"Bearer {CHAT_API_KEY}"},
|
||||
headers={"Authorization": f"Bearer {model_config.api_key}"},
|
||||
json=payload,
|
||||
timeout=30,
|
||||
timeout=model_config.timeout_seconds,
|
||||
)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
@@ -314,21 +314,30 @@ def _call_qwen(messages: list[dict], temperature: float) -> dict:
|
||||
return {"answer": answer.strip(), "usage": data.get("usage") or {}}
|
||||
|
||||
|
||||
def _iter_qwen_stream(messages: list[dict], temperature: float):
|
||||
def _iter_qwen_stream(
|
||||
messages: list[dict], temperature: float, model_config: ChatModelConfig | None = None
|
||||
):
|
||||
"""将 OpenAI 兼容接口的 SSE 分片原样转为文本增量。"""
|
||||
if not CHAT_API_KEY:
|
||||
model_config = model_config or get_chat_model_config()
|
||||
if not model_config.api_key:
|
||||
raise RuntimeError("模型服务未配置")
|
||||
url = f"{CHAT_API_URL.rstrip('/')}/chat/completions"
|
||||
url = f"{model_config.api_base_url}/chat/completions"
|
||||
payload = {
|
||||
"model": CHAT_MODEL,
|
||||
"model": model_config.model,
|
||||
"messages": messages,
|
||||
"temperature": temperature,
|
||||
"max_tokens": CHAT_MAX_OUTPUT_TOKENS,
|
||||
"max_tokens": model_config.max_tokens,
|
||||
"stream": True,
|
||||
"stream_options": {"include_usage": True},
|
||||
}
|
||||
try:
|
||||
with httpx.stream("POST", url, headers={"Authorization": f"Bearer {CHAT_API_KEY}"}, json=payload, timeout=45) as response:
|
||||
with httpx.stream(
|
||||
"POST",
|
||||
url,
|
||||
headers={"Authorization": f"Bearer {model_config.api_key}"},
|
||||
json=payload,
|
||||
timeout=max(45, model_config.timeout_seconds),
|
||||
) as response:
|
||||
response.raise_for_status()
|
||||
for raw_line in response.iter_lines():
|
||||
line = raw_line.decode() if isinstance(raw_line, bytes) else raw_line
|
||||
@@ -387,16 +396,21 @@ def _resolve_reply(
|
||||
if model_client is not None:
|
||||
answer = model_client(messages=messages, temperature=temperature)
|
||||
else:
|
||||
model_config = get_chat_model_config()
|
||||
reservation = reserve_avatar_tokens(
|
||||
db,
|
||||
avatar,
|
||||
usage_source,
|
||||
CHAT_MODEL,
|
||||
model_config.model,
|
||||
messages,
|
||||
CHAT_MAX_OUTPUT_TOKENS,
|
||||
model_config.max_tokens,
|
||||
)
|
||||
try:
|
||||
model_result = _call_qwen(messages=messages, temperature=temperature)
|
||||
model_result = _call_qwen(
|
||||
messages=messages,
|
||||
temperature=temperature,
|
||||
model_config=model_config,
|
||||
)
|
||||
answer = model_result["answer"]
|
||||
token_usage = settle_reservation(
|
||||
db,
|
||||
@@ -436,15 +450,16 @@ def _stream_reply(
|
||||
config = _config(avatar)
|
||||
temperature = min(0.45 if references else 0.25, 0.2 + config["creativity"] / 100 * 0.6)
|
||||
messages = _build_prompt(avatar, history, question, references)
|
||||
model_config = get_chat_model_config()
|
||||
reservation = reserve_avatar_tokens(
|
||||
db,
|
||||
avatar,
|
||||
usage_source,
|
||||
CHAT_MODEL,
|
||||
model_config.model,
|
||||
messages,
|
||||
CHAT_MAX_OUTPUT_TOKENS,
|
||||
model_config.max_tokens,
|
||||
)
|
||||
chunks = _iter_qwen_stream(messages, temperature)
|
||||
chunks = _iter_qwen_stream(messages, temperature, model_config)
|
||||
if matched:
|
||||
messages, reservation = [], None
|
||||
if public:
|
||||
|
||||
Reference in New Issue
Block a user