✨ 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:
1 parent
127191c889
commit
491136c770
15 files changed
+314
-4
No files matched your search
@@ -2,6 +2,7 @@
|
||||
|
||||
import json
|
||||
|
||||
import pytest
|
||||
from httpx import AsyncClient
|
||||
from sqlalchemy import select
|
||||
|
||||
@@ -146,3 +147,52 @@ class TestAtomicity:
|
||||
outbox = (await session.scalars(select(models.OutboxEvent))).all()
|
||||
assert [e for e in outbox if e.aggregate_type == "message"] == []
|
||||
assert (await session.scalars(select(models.IdempotencyRecord))).all() == []
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("client")
|
||||
class TestLingtaiNormalization:
|
||||
async def test_markdown_input_expanded_by_engine(self, client: AsyncClient) -> None:
|
||||
headers = await login(client, "norm@example.com")
|
||||
cid = await _create_conversation(client, headers)
|
||||
resp = await client.post(
|
||||
f"/v1/conversations/{cid}/messages",
|
||||
json={
|
||||
"content": [
|
||||
{"type": "markdown", "text": "说明\n```mermaid\nA-->B\n```\n$$\nz\n$$"}
|
||||
]
|
||||
},
|
||||
headers={**headers, "Idempotency-Key": "idem-norm"},
|
||||
)
|
||||
assert resp.status_code == 201, resp.text
|
||||
blocks = resp.json()["message"]["content"]
|
||||
assert [b["type"] for b in blocks] == ["markdown", "mermaid", "math"]
|
||||
assert all(b["id"].startswith("block_") for b in blocks)
|
||||
assert resp.json()["message"].get("parser_version") == "hunjing-markdown-v1"
|
||||
|
||||
async def test_python_cpp_golden_consistency(self) -> None:
|
||||
"""绑定输出与 C++ doctest 期望字节一致(跨端一致性最小 golden)。"""
|
||||
from app.core import lingtai
|
||||
|
||||
out = lingtai.transform_markdown("x\n```mermaid\nA-->B\n```\n$$\nz\n$$\n")
|
||||
assert out["parser_version"] == "hunjing-markdown-v1"
|
||||
assert [b["type"] for b in out["blocks"]] == ["markdown", "mermaid", "math"]
|
||||
# 与 engine/tests/test_transform.cpp 的确定性用例保持同一输入输出
|
||||
out2 = lingtai.transform_markdown("x\n```mermaid\nA-->B\n```\n$$\nz\n$$\n")
|
||||
assert out == out2
|
||||
|
||||
async def test_engine_failure_fails_closed(self, client: AsyncClient, monkeypatch) -> None:
|
||||
from app.core import lingtai as lt
|
||||
|
||||
def boom(text: str):
|
||||
raise lt.LingtaiUnavailable("灵台引擎调用失败:注入")
|
||||
|
||||
monkeypatch.setattr(lt, "transform_markdown", boom)
|
||||
headers = await login(client, "fc@example.com")
|
||||
cid = await _create_conversation(client, headers)
|
||||
resp = await client.post(
|
||||
f"/v1/conversations/{cid}/messages",
|
||||
json={"content": [{"type": "markdown", "text": "hi"}]},
|
||||
headers={**headers, "Idempotency-Key": "idem-fc"},
|
||||
)
|
||||
# fail closed:引擎不可用时显式失败,不静默降级直存
|
||||
assert resp.status_code == 500
|
||||
Reference in new issue
Block a user