✨ feat: 灵台引擎 Python 绑定与消息路径接入(WP-15/WP-03 交界)

- engine/bindings:nanobind 官方薄绑定包 hunjing-lingtai(LingtaiEngine RAII、HjStatus→LingtaiError 映射、HJ_ABI_VERSION 导出);scikit-build-core 构建,直接编译引擎源码自包含
- backend 以 editable 路径依赖接入;typings/lingtai.pyi 类型存根(mypy strict 全绿)
- app/core/lingtai.py:懒初始化单例 + LingtaiUnavailable fail-closed(禁止宿主第二实现降级)
- 迁移 3:message.parser_version;post_message 的 markdown 输入经 hj_transform 规范化为 canonical blocks(展开 code/mermaid/math 并标注 parser_version,契约第 4 节)
- 测试 29 项全绿:消息展开、Python/C++ golden 字节一致、引擎注入失败时消息路径显式 500(fail-closed 验证)
This commit is contained in:
chenxin committed 2026-09-10 11:26:40 +08:00
1 parent 127191c889
commit 491136c770
15 files changed
+314 -4

No files matched your search

+15 -4
View File
@@ -14,6 +14,7 @@ from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.conversations.schemas import MessageIn
from app.core import lingtai
from app.core.ids import new_id
from app.models import (
AuditEvent,
@@ -122,15 +123,25 @@ async def replay_or_execute_message(
conversation = await _require_conversation(db, domain_id, conversation_id)
branch = await _get_default_branch(db, conversation.id)
blocks: list[dict[str, Any]] = [
{**b.model_dump(exclude_none=True), "id": new_id("block_"), "status": "completed"}
for b in body.content
]
blocks: list[dict[str, Any]] = []
parser_version: str | None = None
for b in body.content:
if b.type == "markdown":
# 契约第 4 节:markdown 输入经确定性 parser 规范化为 canonical blocks
normalized = lingtai.transform_markdown(b.text)
parser_version = str(normalized["parser_version"])
blocks.extend(normalized["blocks"])
else:
blocks.append({**b.model_dump(exclude_none=True), "status": "completed"})
for block in blocks:
block["id"] = new_id("block_")
block.setdefault("status", "completed")
message = Message(
conversation_id=conversation.id,
branch_id=branch.id,
role="user",
content=blocks,
parser_version=parser_version,
)
db.add(message)
await db.flush()