133 lines
5.6 KiB
Python
133 lines
5.6 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""m11b_api —— M11b 对外契约的 **async 包装层**(dspy 直接调用的名字)。
|
||
|
||
dspy 端点以「裸全局名 + await」调用,因此这里导出的每个名字必须:
|
||
① 在本文件有真实实现(或从 tx_write/broadcast 再导出)
|
||
② 在 __init__.py 有 import 导出
|
||
③ 在 init.py load_pbl_runtime_ext() 内有 ``env.<name> = <name>`` 注册
|
||
三处缺一,上线即 ``NameError: name 'xxx' is not defined``(QC #1 指出的正是此缺口)。
|
||
|
||
导出(6 个契约):
|
||
apply_runtime_event / poll_events / read_states / assert_append_only
|
||
pbl_runtime_broadcast_pull / pbl_runtime_broadcast_stats
|
||
"""
|
||
|
||
import inspect
|
||
|
||
from . import broadcast as _hub
|
||
from . import latency as _lat
|
||
from . import rtx_db
|
||
from . import tx_write as _w
|
||
from .m11b_config import CHANNEL_PREFIX, contracts as _contracts
|
||
|
||
__all__ = ["apply_runtime_event", "poll_events", "read_states", "assert_append_only",
|
||
"pbl_runtime_broadcast_pull", "pbl_runtime_broadcast_stats",
|
||
"get_hub", "contracts"]
|
||
|
||
# 再导出核心实现(保持单一事实源:实现只在 tx_write/broadcast 里)
|
||
apply_runtime_event = _w.apply_runtime_event
|
||
poll_events = _w.poll_events
|
||
read_states = _w.read_states
|
||
assert_append_only = _w.assert_append_only
|
||
get_hub = _hub.get_hub
|
||
contracts = _contracts
|
||
|
||
|
||
def _bad(msg):
|
||
return {"ok": False, "code": "PBL-PARAM-0001", "error": msg}
|
||
|
||
|
||
def _fail(exc):
|
||
code = getattr(exc, "code", "PBL-RTX-0002")
|
||
return {"ok": False, "code": code, "error": str(getattr(exc, "msg", exc))}
|
||
|
||
|
||
async def pbl_runtime_broadcast_pull(tenant_id=None, world_id=None, session_id=None,
|
||
after_cursor=None, after_seq=None, limit=200,
|
||
channel=None, **_ignored):
|
||
"""广播拉取端点(3s 轮询兜底用)。
|
||
|
||
两种用法:
|
||
* 传 tenant_id(+world_id) → 走 poll_events(hub 命中优先,未命中退回查库)
|
||
* 只传 channel → 直接按通道名从 hub 取增量(跨租户禁用,须带 tenant_id 前缀)
|
||
"""
|
||
try:
|
||
if tenant_id in (None, "") and channel:
|
||
expect = "%s.%s." % (CHANNEL_PREFIX, "")
|
||
if not str(channel).startswith(CHANNEL_PREFIX + "."):
|
||
return _bad("channel 必须以 %s. 开头" % CHANNEL_PREFIX)
|
||
parts = str(channel).split(".")
|
||
if len(parts) < 4 or parts[3] in ("", "*"):
|
||
return _bad("channel 必须含租户段(pbl.world.<tenant>.<world>),"
|
||
"拒绝跨租户拉取")
|
||
tenant_id = parts[3]
|
||
world_id = parts[4] if len(parts) > 4 else None
|
||
events, truncated = _hub.get_hub().pull(
|
||
channel or _hub.channel_of(world_id, tenant_id), after_cursor,
|
||
limit or 200)
|
||
if events and not truncated and after_seq in (None, ""):
|
||
return {"ok": True, "source": "hub", "truncated": False,
|
||
"events": events, "next_cursor": events[-1]["cursor"],
|
||
"count": len(events)}
|
||
res = await poll_events(tenant_id=tenant_id, world_id=world_id,
|
||
session_id=session_id, after_cursor=after_cursor,
|
||
after_seq=after_seq, limit=limit)
|
||
res["count"] = len(res.get("events") or [])
|
||
return res
|
||
except rtx_db.RtxError as exc:
|
||
return _fail(exc)
|
||
except Exception as exc: # noqa: BLE001
|
||
return _fail(exc)
|
||
|
||
|
||
async def pbl_runtime_broadcast_stats(tenant_id=None, world_id=None, **_ignored):
|
||
"""广播/时延统计端点:hub 扇出计数 + 各阶段 SLA 实测摘要。"""
|
||
try:
|
||
hub = _hub.get_hub()
|
||
st = hub.stats()
|
||
st["sla"] = _lat.summary()
|
||
st["contracts"] = sorted(_contracts().keys())
|
||
st["channel_prefix"] = CHANNEL_PREFIX
|
||
if tenant_id not in (None, "") and world_id not in (None, ""):
|
||
st["channel"] = _hub.channel_of(world_id, tenant_id)
|
||
buffered, _ = hub.pull(st["channel"], None, 50)
|
||
st["channel_buffered"] = len(buffered)
|
||
st["ok"] = True
|
||
return st
|
||
except Exception as exc: # noqa: BLE001
|
||
return _fail(exc)
|
||
|
||
|
||
def self_check():
|
||
"""离线自检:6 个契约全部存在、可调用、协程签名正确(防三处同步漏项)。"""
|
||
msgs, ok = [], True
|
||
coros = ("apply_runtime_event", "poll_events", "read_states",
|
||
"pbl_runtime_broadcast_pull", "pbl_runtime_broadcast_stats")
|
||
for name in coros:
|
||
fn = globals().get(name)
|
||
if fn is None:
|
||
ok = False
|
||
msgs.append("m11b_api FAIL:契约 %s 未定义" % name)
|
||
elif not inspect.iscoroutinefunction(fn):
|
||
ok = False
|
||
msgs.append("m11b_api FAIL:%s 非协程" % name)
|
||
if ok:
|
||
msgs.append("m11b_api PASS:%d 个协程契约齐备" % len(coros))
|
||
if not callable(assert_append_only):
|
||
ok = False
|
||
msgs.append("m11b_api FAIL:assert_append_only 不可调用")
|
||
else:
|
||
msgs.append("m11b_api PASS:assert_append_only 同步守卫可调用")
|
||
if sorted(_contracts().keys()) != sorted([
|
||
"apply_runtime_event", "assert_append_only", "broadcast_hub",
|
||
"ensure_forward_partitions", "pbl_runtime_broadcast_pull",
|
||
"pbl_runtime_broadcast_stats", "poll_events", "read_states"]):
|
||
ok = False
|
||
msgs.append("m11b_api FAIL:contracts 清单与注册表不一致 %r"
|
||
% sorted(_contracts().keys()))
|
||
else:
|
||
msgs.append("m11b_api PASS:contracts 清单 8 项与注册表一致")
|
||
if ok:
|
||
msgs.append("m11b_api.self_check PASS")
|
||
return ok, msgs
|