fix(llm): 模型不可用报真实错误+name/model_id双匹配+org语义对齐
- _no_llm_error: 错误写明模型名/原因/行动指引,禁止笼统No LLM API configured - _get_model_config: name/model_id 双匹配(与gateway解析语义一致),org过滤含系统级共享 - agent_loop_v2 run(): LLM调用异常捕获→yield error事件,前端显示真实错误不再卡死思考中
This commit is contained in:
parent
519b4bfcf4
commit
7023742880
@ -198,7 +198,14 @@ class AgentExecutor:
|
||||
await self._maybe_compress()
|
||||
|
||||
# 5b. LLM 调用(native function calling,返回 dict)
|
||||
resp = await self._call_llm()
|
||||
# 模型不可用等配置错误必须真实报给用户(error 事件),
|
||||
# 绝不能让异常打断流——前端会停在"思考中..."干等。
|
||||
try:
|
||||
resp = await self._call_llm()
|
||||
except Exception as e:
|
||||
logger.error(f"run: LLM 调用失败 turn={turn+1}: {e}")
|
||||
yield json.dumps({"type": "error", "message": str(e)}, ensure_ascii=False) + "\n"
|
||||
return
|
||||
|
||||
# 5c. 原生 function calling:优先处理 tool_calls
|
||||
native_calls = (resp.get("tool_calls") or []) if isinstance(resp, dict) else []
|
||||
|
||||
@ -50,10 +50,14 @@ async def _get_model_config(model_name: str = None, org_id: str = None) -> dict:
|
||||
dbname = "pipeline"
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
if model_name:
|
||||
sql = "SELECT api_base, api_key, model_id FROM llm WHERE name=${name}$ AND status='active'"
|
||||
# name/model_id 双匹配:与 gateway._resolve_and_persist_model 语义一致——
|
||||
# 调用方传 llm.name 或 API 模型名(model_id)都能解析,避免语义漂移。
|
||||
sql = ("SELECT api_base, api_key, model_id FROM llm "
|
||||
"WHERE status='active' AND (name=${name}$ OR model_id=${name}$)")
|
||||
params = {"name": model_name}
|
||||
if org_id:
|
||||
sql += " AND org_id=${org}$"
|
||||
# 与 gateway 一致:本机构模型 + 系统级共享(org_id 为空/0)
|
||||
sql += " AND (org_id=${org}$ OR org_id='' OR org_id='0')"
|
||||
params["org"] = org_id
|
||||
sql += " LIMIT 1"
|
||||
recs = await sor.sqlExe(sql, params)
|
||||
@ -80,6 +84,24 @@ async def _get_model_config(model_name: str = None, org_id: str = None) -> dict:
|
||||
return {}
|
||||
|
||||
|
||||
def _no_llm_error(model_name=None, org_id=None) -> ValueError:
|
||||
"""模型不可用的错误必须真实可行动:写明模型名与原因,前端原样展示、运维据此处理。
|
||||
|
||||
禁止笼统的 'No LLM API configured'——用户看到它只会卡住干等。
|
||||
"""
|
||||
if model_name:
|
||||
return ValueError(
|
||||
f"模型「{model_name}」不可用:llm 表中未找到可用配置"
|
||||
f"(name/model_id 不匹配、status 非 active"
|
||||
+ (f",或不属于当前机构 org={org_id}" if org_id else "")
|
||||
+ ")。请在模型下拉里改选可用模型,或联系管理员在模型管理中补齐配置。"
|
||||
)
|
||||
return ValueError(
|
||||
"没有可用模型:当前会话未指定模型,且 llm 表无启用模型。"
|
||||
"请在模型下拉中选择模型,或联系管理员配置模型。"
|
||||
)
|
||||
|
||||
|
||||
# 瞬时错误重试:超时/连接错误/限流/服务端5xx 均重试;配置错误(无key)、鉴权/参数4xx 不重试
|
||||
_RETRYABLE_STATUS = (429, 500, 502, 503, 504)
|
||||
_LLM_MAX_ATTEMPTS = 3
|
||||
@ -168,7 +190,7 @@ async def llm_call(prompt: str, model: str = None, temperature: float = 0.7, org
|
||||
model_id = model or os.environ.get("LLM_MODEL", "gpt-4o-mini")
|
||||
|
||||
if not api_key:
|
||||
raise ValueError("No LLM API configured. Please add a model in the llm table or set LLM_API_KEY env var.")
|
||||
raise _no_llm_error(model, org_id)
|
||||
|
||||
import aiohttp
|
||||
|
||||
@ -207,7 +229,7 @@ async def llm_call_msgs(messages: list, model: str = None, temperature: float =
|
||||
model_id = model or os.environ.get("LLM_MODEL", "gpt-4o-mini")
|
||||
|
||||
if not api_key:
|
||||
raise ValueError("No LLM API configured")
|
||||
raise _no_llm_error(model, org_id)
|
||||
|
||||
headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
|
||||
payload = {"model": model_id, "messages": messages, "temperature": temperature}
|
||||
@ -237,7 +259,7 @@ async def llm_call_msgs_native(messages: list, tools: list = None, model: str =
|
||||
model_id = model or os.environ.get("LLM_MODEL", "gpt-4o-mini")
|
||||
|
||||
if not api_key:
|
||||
raise ValueError("No LLM API configured")
|
||||
raise _no_llm_error(model, org_id)
|
||||
|
||||
headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
|
||||
payload = {"model": model_id, "messages": messages, "temperature": temperature}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user