feat(engine): #873 dual-Claw event contract + SseAdapter/BufferAdapter + chat.py SSE delegate + im_llm EngineCore thin-shell (gray-gated)
- app/core/engine_event.py (#880): EngineEvent dataclass + make_* factories, single construction entry, byte-compatible wire with legacy ChatPage.tsx - app/core/claw_adapters.py (#884): SseAdapter (hot frames zero-metadata, terminal keeps trace_id) + BufferAdapter (assemble() preserves __media__) - app/api/chat.py (#881): /messages/stream now delegates to SseAdapter; wire bytes unchanged (46B delta / done+message_id) - app/api/im_llm.py (#883): IM_USE_ENGINE_CORE gray gate; _call_engine_core reuses EngineCore via BufferAdapter (default OFF, preserves media fast-path) - app/api/chat_service.py: capture 3 container-ahead fixes (stray import asyncio removed, logger.info format, LiteLLM master key)
This commit is contained in:
1 parent
34648e4810
commit
037c965c26
5 files changed
+752
-21
No files matched your search
@@ -181,6 +181,16 @@ def _call_real_llm(db: Session, user=None, prompt: str = "", platform: str = "im
|
||||
return s
|
||||
return "(媒体生成未能返回有效内容,请重试)"
|
||||
|
||||
# ===== 1.5) EngineCore 薄壳(#873 · 灰阶门,默认关闭)=====
|
||||
# IM_USE_ENGINE_CORE=1 时,文本回复改走与 Web Claw 同一份 EngineCore
|
||||
# (_send_message_core),经 BufferAdapter 缓冲装配为整条回复;上方媒体快路径
|
||||
# 已提前返回,不受此门影响。默认 0/未设置 = 走下方既有自包含逻辑(零改动)。
|
||||
if os.environ.get("IM_USE_ENGINE_CORE") == "1":
|
||||
return _run_async(
|
||||
_call_engine_core(db, user_id, prompt, platform, user),
|
||||
timeout=300,
|
||||
)
|
||||
|
||||
# ===== 2) 文本回复:模型 failover + 技能自动发现 + 平台知识库注入 =====
|
||||
from ..platform_knowledge import get_platform_knowledge
|
||||
|
||||
@@ -413,3 +423,79 @@ def _call_real_llm(db: Session, user=None, prompt: str = "", platform: str = "im
|
||||
return f"(已尝试 {len(_try_models)} 个模型均调用失败。最后错误: {_summary}。请稍后重试。)"
|
||||
except Exception as e:
|
||||
return f"(AI 回复生成失败:{e})"
|
||||
|
||||
|
||||
async def _call_engine_core(db, user_id, prompt: str, platform: str, user=None) -> "str | dict":
|
||||
"""(#873 薄壳)复用 Web Claw 同一份 EngineCore 生成 IM 回复。
|
||||
|
||||
单引擎原则:IM Bot 不再维护第二份「模型 failover + 技能/MCP 执行」实现,
|
||||
而是把已发现的 skill_ids/mcp_ids 交给 _send_message_core,经 BufferAdapter
|
||||
缓冲装配成整条回复(媒体结构 __media__ 由 BufferAdapter 原样保留)。
|
||||
默认不启用(见 _call_real_llm 中的 IM_USE_ENGINE_CORE 灰阶门)。
|
||||
"""
|
||||
from app.api.chat_service import _send_message_core
|
||||
from app.api.chat_utils import SendMessageRequest
|
||||
from app.core.claw_adapters import BufferAdapter
|
||||
from ..models import Conversation, User
|
||||
|
||||
# 解析用户:IM 调用方常传 user=None,仅给 user_id
|
||||
real_user = user
|
||||
if real_user is None and user_id:
|
||||
real_user = db.query(User).filter(User.id == user_id).first()
|
||||
if real_user is None:
|
||||
return "(EngineCore 模式:无法解析当前用户,请检查 IM 账号绑定)"
|
||||
|
||||
# 能力发现(与 IM 自包含逻辑同一套,claw="im")
|
||||
from app.core.capability_discovery import (
|
||||
discover_mcp_servers,
|
||||
discover_skills,
|
||||
)
|
||||
_query_hint = prompt
|
||||
_marker = "用户当前问题:"
|
||||
if _marker in prompt:
|
||||
_query_hint = prompt[prompt.rfind(_marker) + len(_marker):].strip()
|
||||
skills = []
|
||||
try:
|
||||
skills = discover_skills(db, user_id, query=_query_hint, claw="im") or []
|
||||
except Exception as _e:
|
||||
logger.warning(f"[im_llm.engine] 技能发现失败(降级为空): {_e}")
|
||||
mcp_servers = []
|
||||
try:
|
||||
mcp_servers = discover_mcp_servers(db, None, claw="im") or []
|
||||
except Exception as _e:
|
||||
logger.warning(f"[im_llm.engine] MCP 发现失败(降级为空): {_e}")
|
||||
|
||||
skill_ids = [
|
||||
(s.get("id") if isinstance(s, dict) else getattr(s, "id", None))
|
||||
for s in skills
|
||||
]
|
||||
skill_ids = [s for s in skill_ids if s]
|
||||
mcp_ids = [
|
||||
(m.get("id") if isinstance(m, dict) else getattr(m, "id", None))
|
||||
for m in mcp_servers
|
||||
]
|
||||
mcp_ids = [m for m in mcp_ids if m]
|
||||
|
||||
# 构造本回合专用对话:EngineCore 要求已存在且归属当前用户的 Conversation
|
||||
conv = Conversation(
|
||||
id=gen_uuid(),
|
||||
user_id=real_user.id,
|
||||
title=f"IM对话-{platform}",
|
||||
source="im_bot",
|
||||
)
|
||||
db.add(conv)
|
||||
db.commit()
|
||||
|
||||
req = SendMessageRequest(
|
||||
content=prompt,
|
||||
skill_ids=skill_ids or None,
|
||||
mcp_ids=mcp_ids or None,
|
||||
use_tools=bool(skill_ids or mcp_ids),
|
||||
agent_ids=None,
|
||||
knowledge_base_ids=None,
|
||||
attachments=None,
|
||||
)
|
||||
|
||||
adapter = BufferAdapter(tenant_id=str(getattr(real_user, "tenant_id", "") or ""))
|
||||
await _send_message_core(conv.id, req, db, real_user, stream=True, on_event=adapter)
|
||||
return adapter.assemble()
|
||||
Reference in new issue
Block a user