Files
video/text_ai.py
T
2026-04-25 12:50:36 +08:00

114 lines
3.7 KiB
Python

"""
text_ai.py - LLM 文本生成
用于场景划分等 AI 推理任务
支持多 LLM 提供商切换
"""
from openai import OpenAI
from config import LLM_PROVIDERS, DEFAULT_LLM, LLM_API_KEY, LLM_API_BASE, LLM_MODEL
def text_ai(in_put: str, system_prompt: str = "You are a helpful assistant.",
provider: str = None) -> str:
"""
调用 LLM 生成文本
Args:
in_put: 用户输入内容
system_prompt: 系统提示词
provider: LLM 提供商名称(对应 LLM_PROVIDERS 的 key),None 则用默认
Returns:
AI 生成的文本
"""
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
client = OpenAI(
api_key=api_key,
base_url=api_base,
)
# ModelScope 的 Qwen3 系列和 GLM 系列默认开启 thinking,需要关掉
# 注意:MiniMax 系列不是 Qwen/GLM,不需要也不能传 enable_thinking
extra_body = {}
is_modelscope = "modelscope" in api_base.lower()
is_qwen = "qwen" in model.lower()
is_glm = "glm" in model.lower() or "zhipuai" in model.lower()
if is_modelscope and (is_qwen or is_glm):
extra_body["enable_thinking"] = False
response = client.chat.completions.create(
model=model,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": in_put}
],
max_tokens=16384,
stream=False,
extra_body=extra_body if extra_body else 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)
raise ValueError(
f"模型 '{model}' 返回了空的 choices。\n"
f"响应内容: {err_text}\n"
f"可能是模型暂时不可用或请求被拒绝。"
)
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 content is None:
# fallback:尝试多种字段名(不同 API 叫法不同)
for attr in ("thinking_content", "reasoning_content", "text", "output"):
fallback = getattr(msg, attr, None)
if fallback:
content = fallback
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():
if isinstance(v, str) and v.strip():
content = v
break
except Exception:
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 模型。"
)
return content
if __name__ == "__main__":
result = text_ai("Hello, say hi in one sentence.")
print(result)