feat: protocol base lib + rule-based parser MVP + 31 tests

This commit is contained in:
root
2026-06-01 11:12:45 +08:00
parent 6edda94a89
commit ac663fca16
22 changed files with 1066 additions and 373 deletions

211
tests/test_protocol.py Normal file
View File

@ -0,0 +1,211 @@
import sys
import unittest
import numpy as np
sys.path.insert(0, "/home/program/NextAgent/src")
from protocol.registry import PROTOCOL_REGISTRY, FMT_NAMES, PARSER_NAMES
from protocol.codec import CodecAdapter, IdentityCodec, EncodingError, DecodingError
from protocol.tensor_bus import TensorBus
from parsers.rule_based import RuleBasedParser
class _TestCodec(CodecAdapter):
"""测试用编解码器:fmt_id=0 (ref_tensor), parser_id=0 (ref_parser)"""
def __init__(self):
super().__init__(subnet_id="core", fmt_id=0, parser_id=0)
class _LaxCodec(CodecAdapter):
"""宽松编解码器(类似 IdentityCodec 但使用合法 id)"""
def __init__(self):
super().__init__(subnet_id="core", fmt_id=0, parser_id=0)
def _validate_prefix(self, tensor):
if tensor.shape[0] < self.prefix_len:
raise DecodingError(f"张量过短: {tensor.shape[0]} < {self.prefix_len}")
def _validate_internal(self, tensor):
if not isinstance(tensor, np.ndarray):
raise EncodingError(f"内部类型必须是 np.ndarray, 得到 {type(tensor)}")
class TestProtocolRegistry(unittest.TestCase):
def test_validate_fmt_id_valid(self):
for fid in FMT_NAMES:
PROTOCOL_REGISTRY.validate_fmt_id(fid)
def test_validate_fmt_id_invalid(self):
with self.assertRaises(ValueError):
PROTOCOL_REGISTRY.validate_fmt_id(999)
def test_validate_parser_id_valid(self):
for pid in PARSER_NAMES:
PROTOCOL_REGISTRY.validate_parser_id(pid)
def test_validate_parser_id_invalid(self):
with self.assertRaises(ValueError):
PROTOCOL_REGISTRY.validate_parser_id(999)
def test_validate_subnet_id_valid(self):
for sid in PROTOCOL_REGISTRY.subnet_ids:
PROTOCOL_REGISTRY.validate_subnet_id(sid)
def test_validate_subnet_id_invalid(self):
with self.assertRaises(ValueError):
PROTOCOL_REGISTRY.validate_subnet_id("nonexistent")
def test_fmt_name_known(self):
self.assertEqual(PROTOCOL_REGISTRY.fmt_name(0), "ref_tensor")
def test_fmt_name_unknown(self):
self.assertEqual(PROTOCOL_REGISTRY.fmt_name(999), "unknown_fmt_999")
def test_parser_name_known(self):
self.assertEqual(PROTOCOL_REGISTRY.parser_name(0), "ref_parser")
def test_parser_name_unknown(self):
self.assertEqual(PROTOCOL_REGISTRY.parser_name(999),
"unknown_parser_999")
class TestCodecAdapter(unittest.TestCase):
def test_encode_decode_roundtrip(self):
codec = _TestCodec()
payload = np.array([1.0, 2.0, 3.0, 4.0])
comm = codec.encode(payload, target="routing", reply_req=True)
decoded = codec.decode(comm)
np.testing.assert_array_equal(decoded, payload)
def test_encode_raises_on_non_ndarray(self):
codec = _TestCodec()
with self.assertRaises(EncodingError):
codec.encode([1, 2, 3], target="routing", reply_req=True)
def test_decode_raises_on_short_tensor(self):
codec = _TestCodec()
short = np.array([0.0])
with self.assertRaises(DecodingError):
codec.decode(short)
def test_decode_raises_on_wrong_fmt_id(self):
codec = _TestCodec()
wrong = np.array([99.0, 0.0, 1.0, 2.0])
with self.assertRaises(DecodingError):
codec.decode(wrong)
class TestLaxCodec(unittest.TestCase):
def test_encode_decode_roundtrip(self):
codec = _LaxCodec()
payload = np.array([10.0, 20.0, 30.0])
comm = codec.encode(payload, target="core", reply_req=False)
decoded = codec.decode(comm)
np.testing.assert_array_equal(decoded, payload)
def test_decode_allows_any_prefix(self):
codec = _LaxCodec()
comm = np.array([99.0, 99.0, 42.0])
decoded = codec.decode(comm)
np.testing.assert_array_equal(decoded, np.array([42.0]))
def test_decode_raises_on_short_tensor(self):
codec = _LaxCodec()
with self.assertRaises(DecodingError):
codec.decode(np.array([0.0]))
def test_encode_raises_on_non_ndarray(self):
codec = _LaxCodec()
with self.assertRaises(EncodingError):
codec.encode("bad", target="core", reply_req=True)
class TestRuleBasedParser(unittest.TestCase):
def setUp(self):
self.parser = RuleBasedParser()
def test_ref_tensor(self):
tensor = np.array([0.0, 0.0, 5.0, 1.0, 2.0, 3.0])
result = self.parser.parse(tensor)
self.assertEqual(result, "引用张量: 5个标签")
def test_memory_result(self):
tensor = np.array([1.0, 1.0, 3.0, 7.0])
result = self.parser.parse(tensor)
self.assertEqual(result, "记忆结果: 3个实体, 7条关系")
def test_memory_query(self):
tensor = np.array([2.0, 2.0, 1.0, 2.0, 3.0])
result = self.parser.parse(tensor)
self.assertEqual(result, "记忆查询")
def test_memory_update(self):
tensor = np.array([3.0, 3.0, 2.0, 4.0])
result = self.parser.parse(tensor)
self.assertEqual(result, "记忆更新: 2个实体, 4条关系")
def test_reasoning_path(self):
tensor = np.array([4.0, 4.0, 6.0])
result = self.parser.parse(tensor)
self.assertEqual(result, "推理路径: 6步")
def test_expression_verify(self):
tensor = np.zeros(2 + 769)
tensor[0] = 5.0
tensor[1] = 5.0
tensor[2 + 768] = 0.85
result = self.parser.parse(tensor)
self.assertEqual(result, "表达验证: 意图对齐度=0.85")
def test_control(self):
tensor = np.array([6.0, 6.0])
result = self.parser.parse(tensor)
self.assertEqual(result, "控制信号")
def test_short_tensor(self):
tensor = np.array([0.0])
result = self.parser.parse(tensor)
self.assertIn("过短", result)
class TestTensorBus(unittest.TestCase):
def setUp(self):
self.bus = TensorBus()
self.codec_a = _TestCodec()
self.codec_b = _LaxCodec()
self.bus.register("routing", self.codec_a)
self.bus.register("core", self.codec_b)
def test_register_and_send(self):
payload = np.array([7.0, 8.0, 9.0])
result = self.bus.send(payload, source="routing", target="core")
np.testing.assert_array_equal(result, payload)
def test_recv(self):
payload = np.array([1.0, 2.0])
comm = self.codec_a.encode(payload, target="routing", reply_req=True)
result = self.bus.recv("routing", comm)
np.testing.assert_array_equal(result, payload)
def test_send_unregistered_source(self):
with self.assertRaises(KeyError):
self.bus.send(np.array([1.0]), source="nonexistent", target="core")
def test_send_unregistered_target(self):
with self.assertRaises(KeyError):
self.bus.send(np.array([1.0]), source="routing",
target="nonexistent")
def test_recv_unregistered_source(self):
with self.assertRaises(KeyError):
self.bus.recv("nonexistent", np.array([1.0, 2.0]))
if __name__ == "__main__":
unittest.main()