Initial commit: V1
This commit is contained in:
+34
-44
@@ -1,43 +1,44 @@
|
||||
"""
|
||||
text_ai.py - LLM 文本生成
|
||||
用于场景划分等 AI 推理任务
|
||||
支持多 LLM 提供商切换
|
||||
text_ai.py - LLM text generation client.
|
||||
|
||||
Supports multiple providers defined in config.py.
|
||||
"""
|
||||
|
||||
from openai import OpenAI
|
||||
from config import LLM_PROVIDERS, DEFAULT_LLM, LLM_API_KEY, LLM_API_BASE, LLM_MODEL
|
||||
from config import LLM_PROVIDERS
|
||||
|
||||
|
||||
def text_ai(in_put: str, system_prompt: str = "You are a helpful assistant.",
|
||||
provider: str = None) -> str:
|
||||
"""
|
||||
调用 LLM 生成文本
|
||||
"""Call LLM to generate text.
|
||||
|
||||
Args:
|
||||
in_put: 用户输入内容
|
||||
system_prompt: 系统提示词
|
||||
provider: LLM 提供商名称(对应 LLM_PROVIDERS 的 key),None 则用默认
|
||||
|
||||
in_put: user message
|
||||
system_prompt: system prompt
|
||||
provider: provider name (key in LLM_PROVIDERS), None = first in dict
|
||||
Returns:
|
||||
AI 生成的文本
|
||||
generated text
|
||||
"""
|
||||
if provider and provider in LLM_PROVIDERS:
|
||||
cfg = LLM_PROVIDERS[provider]
|
||||
api_key = cfg["api_key"]
|
||||
api_base = cfg["api_base"]
|
||||
model = cfg["model"]
|
||||
else:
|
||||
api_key = LLM_API_KEY
|
||||
api_base = LLM_API_BASE
|
||||
model = LLM_MODEL
|
||||
# Default to first provider in dict
|
||||
cfg = next(iter(LLM_PROVIDERS.values()))
|
||||
provider = next(iter(LLM_PROVIDERS))
|
||||
|
||||
client = OpenAI(
|
||||
api_key=api_key,
|
||||
base_url=api_base,
|
||||
)
|
||||
api_key = cfg["api_key"]
|
||||
api_base = cfg["api_base"]
|
||||
model = cfg["model"]
|
||||
|
||||
# ModelScope 的 Qwen3 系列和 GLM 系列默认开启 thinking,需要关掉
|
||||
# 注意:MiniMax 系列不是 Qwen/GLM,不需要也不能传 enable_thinking
|
||||
if not api_key:
|
||||
raise ValueError(
|
||||
f"API key not configured for '{provider}'. "
|
||||
f"Edit config.py and fill in the api_key field."
|
||||
)
|
||||
|
||||
client = OpenAI(api_key=api_key, base_url=api_base)
|
||||
|
||||
# ModelScope Qwen3/GLM default to thinking mode, disable it
|
||||
extra_body = {}
|
||||
is_modelscope = "modelscope" in api_base.lower()
|
||||
is_qwen = "qwen" in model.lower()
|
||||
@@ -49,38 +50,31 @@ def text_ai(in_put: str, system_prompt: str = "You are a helpful assistant.",
|
||||
model=model,
|
||||
messages=[
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": in_put}
|
||||
{"role": "user", "content": in_put},
|
||||
],
|
||||
max_tokens=16384,
|
||||
stream=False,
|
||||
extra_body=extra_body if extra_body else None,
|
||||
extra_body=extra_body or None,
|
||||
)
|
||||
|
||||
# 防御:choices 为空或 None
|
||||
if not response.choices:
|
||||
# 尝试从 response 对象提取有用信息
|
||||
resp_dict = response.model_dump() if hasattr(response, "model_dump") else {}
|
||||
error_msg = resp_dict.get("error", {})
|
||||
if isinstance(error_msg, dict):
|
||||
err_text = error_msg.get("message", str(error_msg))
|
||||
else:
|
||||
err_text = str(resp_dict)
|
||||
err_text = error_msg.get("message", str(error_msg)) if isinstance(error_msg, dict) else str(resp_dict)
|
||||
raise ValueError(
|
||||
f"模型 '{model}' 返回了空的 choices。\n"
|
||||
f"响应内容: {err_text}\n"
|
||||
f"可能是模型暂时不可用或请求被拒绝。"
|
||||
f"Model '{model}' returned empty choices.\n"
|
||||
f"Response: {err_text}\n"
|
||||
f"Model may be unavailable or request was rejected."
|
||||
)
|
||||
|
||||
msg = response.choices[0].message
|
||||
content = msg.content
|
||||
|
||||
# 检测输出是否被截断
|
||||
finish = response.choices[0].finish_reason
|
||||
if finish == "length":
|
||||
print(f"[WARN] LLM output truncated (finish_reason=length), max_tokens may be too small")
|
||||
if response.choices[0].finish_reason == "length":
|
||||
print(f"[WARN] LLM output truncated (finish_reason=length)")
|
||||
|
||||
if content is None:
|
||||
# fallback:尝试多种字段名(不同 API 叫法不同)
|
||||
# Fallback: try alternate field names
|
||||
for attr in ("thinking_content", "reasoning_content", "text", "output"):
|
||||
fallback = getattr(msg, attr, None)
|
||||
if fallback:
|
||||
@@ -88,7 +82,6 @@ def text_ai(in_put: str, system_prompt: str = "You are a helpful assistant.",
|
||||
break
|
||||
|
||||
if content is None:
|
||||
# 最后一搏:尝试把 message 对象当 dict 看
|
||||
try:
|
||||
msg_dict = msg.model_dump() if hasattr(msg, "model_dump") else vars(msg)
|
||||
for v in msg_dict.values():
|
||||
@@ -99,11 +92,8 @@ def text_ai(in_put: str, system_prompt: str = "You are a helpful assistant.",
|
||||
pass
|
||||
|
||||
if content is None:
|
||||
finish = response.choices[0].finish_reason
|
||||
raise ValueError(
|
||||
f"模型 '{model}' 返回内容为空(content=None),"
|
||||
f"finish_reason={finish}。\n"
|
||||
f"如果使用 MiniMax 系列,请改用 Qwen3.5-35B (ModelScope 免费) 或其他 Qwen 模型。"
|
||||
f"Model '{model}' returned None content (finish_reason={response.choices[0].finish_reason})."
|
||||
)
|
||||
return content
|
||||
|
||||
|
||||
Reference in New Issue
Block a user