M0:灵台 Kernel 骨架(事件溯源/命台席位/瀑布总线/插件注册)与神识客户端边界,新增 POST /runs 占位闭环与测试,Alembic 基线合并为单一起点
This commit is contained in:
1 parent
99e55fa0a8
commit
7ef3a15061
23 files changed
+2014
-1399
No files matched your search
@@ -0,0 +1,79 @@
|
||||
"""运行时事件总线:瀑布节点 + 可否决监听器。
|
||||
|
||||
设计依据 docs/architecture/lingtai-runtime.md §1/§2:注入、审计、安全都在事件层做,
|
||||
不改循环骨架。监听器按注册序执行;任一监听器抛 ``Veto`` 即中断该节点(否决),
|
||||
否决事实由调用方记录(safety.ruled 等事件),总线本身不落库。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import fnmatch
|
||||
from collections import defaultdict
|
||||
from collections.abc import Awaitable, Callable
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any
|
||||
|
||||
# 瀑布节点名(契约词典 §8.3 的可否决子集)
|
||||
NODE_RUN_REQUESTED = "run.requested"
|
||||
NODE_TURN_STARTED = "turn.started"
|
||||
NODE_LLM_PRE_REQUEST = "llm.pre_request"
|
||||
NODE_TOOL_PRE_EXECUTE = "tool.pre_execute"
|
||||
NODE_RESULT_PRE_ACCEPT = "result.pre_accept"
|
||||
|
||||
WATERFALL_NODES = frozenset(
|
||||
{
|
||||
NODE_RUN_REQUESTED,
|
||||
NODE_TURN_STARTED,
|
||||
NODE_LLM_PRE_REQUEST,
|
||||
NODE_TOOL_PRE_EXECUTE,
|
||||
NODE_RESULT_PRE_ACCEPT,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class Veto(Exception):
|
||||
"""监听器否决:携带裁决摘要(reason / 规则标识)。"""
|
||||
|
||||
def __init__(self, reason: str, *, rule: str = "", details: dict[str, Any] | None = None):
|
||||
super().__init__(reason)
|
||||
self.reason = reason
|
||||
self.rule = rule
|
||||
self.details = details or {}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class RunContext:
|
||||
"""节点监听器可见的运行上下文(只读视图,变更经事件日志)。"""
|
||||
|
||||
run_id: str
|
||||
character_id: str
|
||||
trace_id: str | None = None
|
||||
payload: dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
|
||||
Listener = Callable[[str, RunContext], Awaitable[None]]
|
||||
|
||||
|
||||
class WaterfallBus:
|
||||
"""瀑布节点订阅与触发。"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._listeners: dict[str, list[Listener]] = defaultdict(list)
|
||||
|
||||
def on(self, node: str, listener: Listener) -> None:
|
||||
"""注册节点监听器;支持 glob 通配(如 ``llm.*``)。"""
|
||||
if not any(fnmatch.fnmatch(node, pattern) for pattern in WATERFALL_NODES):
|
||||
raise ValueError(f"未知瀑布节点:{node}")
|
||||
self._listeners[node].append(listener)
|
||||
|
||||
async def fire(self, node: str, context: RunContext) -> None:
|
||||
"""按注册序触发监听器;任一否决即停止后续。
|
||||
|
||||
精确键与通配键(如 ``llm.*``)各自成列表,列表内保持注册序;
|
||||
两种键之间的相对顺序不保证,需要严格顺序的场景用精确注册。
|
||||
"""
|
||||
for key, listeners in self._listeners.items():
|
||||
if key != node and not fnmatch.fnmatch(node, key):
|
||||
continue
|
||||
for listener in list(listeners):
|
||||
await listener(node, context)
|
||||
Reference in new issue
Block a user