nmp-protocol 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,94 @@
1
+ Metadata-Version: 2.4
2
+ Name: nmp-protocol
3
+ Version: 1.0.0
4
+ Summary: NMP (NeuroMessage Protocol) — Open communication protocol for multi-agent systems with behavioral modulation signals.
5
+ Project-URL: Homepage, https://nmp-protocol.org
6
+ Project-URL: Documentation, https://nmp-protocol.org
7
+ Project-URL: Repository, https://github.com/AIP-Labs/nmp-protocol
8
+ Project-URL: Specification, https://nmp-protocol.org
9
+ Project-URL: Bug Tracker, https://github.com/AIP-Labs/nmp-protocol/issues
10
+ Author-email: Renato Aparecido Gomes <renato@koloni.dev>
11
+ License-Expression: Apache-2.0
12
+ Keywords: a2a,active-inference,agent-communication,behavioral-signals,mcp,multi-agent,neuro-message-protocol,nmp,protocol,somatic-markers
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: Apache Software License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Topic :: System :: Networking
25
+ Requires-Python: >=3.10
26
+ Description-Content-Type: text/markdown
27
+
28
+ # NMP — NeuroMessage Protocol
29
+
30
+ **Open communication protocol for multi-agent AI systems with behavioral modulation.**
31
+
32
+ NMP is to agent communication what HTTP is to web communication — but with built-in behavioral signals. While MCP handles tool access and A2A handles agent coordination, NMP carries **intention**, **modulation**, and **accountability** alongside data.
33
+
34
+ ## Quick Start
35
+
36
+ ```bash
37
+ pip install nmp-protocol
38
+ ```
39
+
40
+ ```python
41
+ from nmp import create_message, TrustLevel, validate_message
42
+
43
+ # Create an NMP message with behavioral signals
44
+ msg = create_message(
45
+ from_agent="router",
46
+ to_agent="legal-analyzer",
47
+ content="Analyze this contract for risk clauses",
48
+ trust=TrustLevel.VERIFIED,
49
+ )
50
+
51
+ # Modulate behavioral signals
52
+ msg.signals.urgency = 0.7 # Time-critical
53
+ msg.signals.quality = 0.9 # High analysis depth needed
54
+ msg.signals.inhibition = 0.0 # No blocking needed
55
+
56
+ # Validate
57
+ errors = validate_message(msg)
58
+ assert not errors
59
+
60
+ # Serialize
61
+ json_str = msg.to_json()
62
+ ```
63
+
64
+ ## 6 Behavioral Signals
65
+
66
+ | Signal | Range | Biological Analog | Purpose |
67
+ |--------|-------|-------------------|---------|
68
+ | `urgency` | [0, 1] | Noradrenaline | Processing speed (Yerkes-Dodson curve) |
69
+ | `quality` | [0, 1] | Serotonin | Analysis depth |
70
+ | `inhibition` | [0, 1] | GABA | Blocking / pausing |
71
+ | `activation` | [0, 1] | Glutamate | Processing intensity |
72
+ | `focus` | [0, 1] | Acetylcholine | Context narrowing |
73
+ | `reward` | [-0.5, 1] | Dopamine | Feedback signal |
74
+
75
+ ## Trust Levels
76
+
77
+ Trust can only decrease through a pipeline: `min(current_trust, source_trust)`
78
+
79
+ | Level | Value | Meaning |
80
+ |-------|-------|---------|
81
+ | `HOSTILE` | 0 | Known malicious source |
82
+ | `UNTRUSTED` | 1 | Unknown / unverified |
83
+ | `VERIFIED` | 2 | Authenticated but not fully trusted |
84
+ | `TRUSTED` | 3 | Fully trusted internal agent |
85
+
86
+ ## Specification
87
+
88
+ Full spec: [nmp-protocol.org](https://nmp-protocol.org)
89
+
90
+ Reference implementations: Python (this package), [TypeScript](https://www.npmjs.com/package/nmp-protocol), [Go](https://pkg.go.dev/github.com/AIP-Labs/nmp-protocol)
91
+
92
+ ## License
93
+
94
+ Apache 2.0. Spec licensed under CC BY-SA 4.0.
@@ -0,0 +1,67 @@
1
+ # NMP — NeuroMessage Protocol
2
+
3
+ **Open communication protocol for multi-agent AI systems with behavioral modulation.**
4
+
5
+ NMP is to agent communication what HTTP is to web communication — but with built-in behavioral signals. While MCP handles tool access and A2A handles agent coordination, NMP carries **intention**, **modulation**, and **accountability** alongside data.
6
+
7
+ ## Quick Start
8
+
9
+ ```bash
10
+ pip install nmp-protocol
11
+ ```
12
+
13
+ ```python
14
+ from nmp import create_message, TrustLevel, validate_message
15
+
16
+ # Create an NMP message with behavioral signals
17
+ msg = create_message(
18
+ from_agent="router",
19
+ to_agent="legal-analyzer",
20
+ content="Analyze this contract for risk clauses",
21
+ trust=TrustLevel.VERIFIED,
22
+ )
23
+
24
+ # Modulate behavioral signals
25
+ msg.signals.urgency = 0.7 # Time-critical
26
+ msg.signals.quality = 0.9 # High analysis depth needed
27
+ msg.signals.inhibition = 0.0 # No blocking needed
28
+
29
+ # Validate
30
+ errors = validate_message(msg)
31
+ assert not errors
32
+
33
+ # Serialize
34
+ json_str = msg.to_json()
35
+ ```
36
+
37
+ ## 6 Behavioral Signals
38
+
39
+ | Signal | Range | Biological Analog | Purpose |
40
+ |--------|-------|-------------------|---------|
41
+ | `urgency` | [0, 1] | Noradrenaline | Processing speed (Yerkes-Dodson curve) |
42
+ | `quality` | [0, 1] | Serotonin | Analysis depth |
43
+ | `inhibition` | [0, 1] | GABA | Blocking / pausing |
44
+ | `activation` | [0, 1] | Glutamate | Processing intensity |
45
+ | `focus` | [0, 1] | Acetylcholine | Context narrowing |
46
+ | `reward` | [-0.5, 1] | Dopamine | Feedback signal |
47
+
48
+ ## Trust Levels
49
+
50
+ Trust can only decrease through a pipeline: `min(current_trust, source_trust)`
51
+
52
+ | Level | Value | Meaning |
53
+ |-------|-------|---------|
54
+ | `HOSTILE` | 0 | Known malicious source |
55
+ | `UNTRUSTED` | 1 | Unknown / unverified |
56
+ | `VERIFIED` | 2 | Authenticated but not fully trusted |
57
+ | `TRUSTED` | 3 | Fully trusted internal agent |
58
+
59
+ ## Specification
60
+
61
+ Full spec: [nmp-protocol.org](https://nmp-protocol.org)
62
+
63
+ Reference implementations: Python (this package), [TypeScript](https://www.npmjs.com/package/nmp-protocol), [Go](https://pkg.go.dev/github.com/AIP-Labs/nmp-protocol)
64
+
65
+ ## License
66
+
67
+ Apache 2.0. Spec licensed under CC BY-SA 4.0.
@@ -0,0 +1,7 @@
1
+ """NMP — NeuroMessage Protocol. Standalone reference implementation."""
2
+ __version__ = "1.0.0"
3
+ from .message import (
4
+ NMPMessage, NMPSignals, TrustLevel, SystemMode,
5
+ Budget, TraceEntry,
6
+ create_message, validate_message,
7
+ )
@@ -0,0 +1,277 @@
1
+ """NMP v1.0 — Standalone Python Reference Implementation."""
2
+ from __future__ import annotations
3
+ import json, math, uuid, time
4
+ from dataclasses import dataclass, field, asdict
5
+ from enum import IntEnum
6
+ from typing import Any
7
+
8
+ class TrustLevel(IntEnum):
9
+ HOSTILE = 0
10
+ UNTRUSTED = 1
11
+ VERIFIED = 2
12
+ TRUSTED = 3
13
+
14
+ class SystemMode(IntEnum):
15
+ REFLEX = 0
16
+ FAST = 1
17
+ STANDARD = 2
18
+ DEEP = 3
19
+
20
+ @dataclass
21
+ class NMPSignals:
22
+ urgency: float = 0.5
23
+ quality: float = 0.5
24
+ inhibition: float = 0.0
25
+ activation: float = 0.5
26
+ focus: float = 0.5
27
+ reward: float = 0.0
28
+
29
+ def __post_init__(self):
30
+ self.urgency = max(0.0, min(1.0, self.urgency))
31
+ self.quality = max(0.0, min(1.0, self.quality))
32
+ self.inhibition = max(0.0, min(1.0, self.inhibition))
33
+ self.activation = max(0.0, min(1.0, self.activation))
34
+ self.focus = max(0.0, min(1.0, self.focus))
35
+ self.reward = max(-0.5, min(1.0, self.reward))
36
+
37
+ def validate(self) -> list[str]:
38
+ errors = []
39
+ for field_name in ("urgency", "quality", "inhibition", "activation", "focus"):
40
+ val = getattr(self, field_name)
41
+ if not 0.0 <= val <= 1.0:
42
+ errors.append(f"{field_name} must be 0.0-1.0, got {val}")
43
+ if not -0.5 <= self.reward <= 1.0:
44
+ errors.append(f"reward must be -0.5-1.0, got {self.reward}")
45
+ return errors
46
+
47
+ def urgency_performance(self) -> float:
48
+ """Yerkes-Dodson inverted-U curve: peak at urgency=0.5."""
49
+ return math.exp(-((self.urgency - 0.5) ** 2) / (2 * 0.15 ** 2))
50
+
51
+ def detect_conflicts(self) -> list[str]:
52
+ """Detect signal conflicts per NMP spec."""
53
+ conflicts = []
54
+ if self.urgency > 0.7 and self.quality > 0.7:
55
+ conflicts.append("urgency_vs_quality")
56
+ if self.inhibition > 0.5 and self.activation > 0.5:
57
+ conflicts.append("inhibition_vs_activation")
58
+ if self.urgency > 0.7 and self.reward > 0.7:
59
+ conflicts.append("impulsive_decision")
60
+ return conflicts
61
+
62
+ def to_dict(self) -> dict[str, float]:
63
+ return asdict(self)
64
+
65
+ @classmethod
66
+ def from_dict(cls, d: dict[str, float]) -> NMPSignals:
67
+ return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
68
+
69
+
70
+ @dataclass
71
+ class Budget:
72
+ max_tokens: int = 5000
73
+ tokens_used: int = 0
74
+ system_mode: int = 1 # SystemMode value
75
+
76
+ @property
77
+ def usage_ratio(self) -> float:
78
+ return self.tokens_used / max(1, self.max_tokens)
79
+
80
+ @property
81
+ def exhausted(self) -> bool:
82
+ return self.tokens_used >= self.max_tokens * 0.95
83
+
84
+ def consume(self, tokens: int) -> None:
85
+ self.tokens_used += tokens
86
+
87
+
88
+ @dataclass
89
+ class TraceEntry:
90
+ agent: str = ""
91
+ action: str = ""
92
+ tokens: int = 0
93
+ timestamp: str = field(default_factory=lambda: time.strftime("%Y-%m-%dT%H:%M:%SZ"))
94
+ signals_snapshot: dict[str, float] = field(default_factory=dict)
95
+
96
+
97
+ @dataclass
98
+ class NMPMessage:
99
+ version: str = "1.0"
100
+ id: str = field(default_factory=lambda: uuid.uuid4().hex[:12])
101
+ trace_id: str = field(default_factory=lambda: uuid.uuid4().hex[:16])
102
+ timestamp: str = field(default_factory=lambda: time.strftime("%Y-%m-%dT%H:%M:%SZ"))
103
+ from_agent: str = ""
104
+ to_agent: str = ""
105
+ tenant_id: str = "default"
106
+ trust_level: int = 3
107
+ trust_source: str = "user_input"
108
+ payload_type: str = "task"
109
+ content: str = ""
110
+ context: dict[str, Any] = field(default_factory=dict)
111
+ signals: NMPSignals = field(default_factory=NMPSignals)
112
+ budget: Budget = field(default_factory=Budget)
113
+ trace: list[TraceEntry] = field(default_factory=list)
114
+ results: dict[str, Any] = field(default_factory=dict)
115
+
116
+ def add_trace(self, agent: str, action: str, tokens: int = 0):
117
+ entry = TraceEntry(
118
+ agent=agent, action=action, tokens=tokens,
119
+ signals_snapshot=self.signals.to_dict(),
120
+ )
121
+ self.trace.append(entry)
122
+ self.budget.consume(tokens)
123
+
124
+ def lower_trust(self, new_level: int | TrustLevel) -> None:
125
+ new_val = int(new_level)
126
+ if new_val > self.trust_level:
127
+ raise ValueError(f"Cannot raise trust from {self.trust_level} to {new_val}")
128
+ self.trust_level = new_val
129
+
130
+ def to_json(self) -> str:
131
+ return json.dumps(self.to_dict(), indent=2, ensure_ascii=False, default=str)
132
+
133
+ def to_dict(self) -> dict[str, Any]:
134
+ return {
135
+ "version": self.version,
136
+ "id": self.id,
137
+ "trace_id": self.trace_id,
138
+ "timestamp": self.timestamp,
139
+ "from": self.from_agent,
140
+ "to": self.to_agent,
141
+ "tenant_id": self.tenant_id,
142
+ "trust": {
143
+ "level": self.trust_level,
144
+ "source": self.trust_source,
145
+ },
146
+ "payload": {
147
+ "type": self.payload_type,
148
+ "content": self.content,
149
+ "context": self.context,
150
+ },
151
+ "signals": self.signals.to_dict(),
152
+ "budget": {
153
+ "max_tokens": self.budget.max_tokens,
154
+ "tokens_used": self.budget.tokens_used,
155
+ "system_mode": self.budget.system_mode,
156
+ },
157
+ "trace": [
158
+ {"agent": t.agent, "action": t.action, "tokens": t.tokens,
159
+ "timestamp": t.timestamp, "signals_snapshot": t.signals_snapshot}
160
+ for t in self.trace
161
+ ],
162
+ "results": self.results,
163
+ }
164
+
165
+ @classmethod
166
+ def from_json(cls, data: str) -> NMPMessage:
167
+ d = json.loads(data)
168
+ return cls.from_dict(d)
169
+
170
+ @classmethod
171
+ def from_dict(cls, d: dict[str, Any]) -> NMPMessage:
172
+ trust_data = d.get("trust", {})
173
+ payload_data = d.get("payload", {})
174
+ signals_data = d.get("signals", {})
175
+ budget_data = d.get("budget", {})
176
+ trace_data = d.get("trace", [])
177
+
178
+ msg = cls(
179
+ version=d.get("version", "1.0"),
180
+ id=d.get("id", uuid.uuid4().hex[:12]),
181
+ trace_id=d.get("trace_id", uuid.uuid4().hex[:16]),
182
+ timestamp=d.get("timestamp", time.strftime("%Y-%m-%dT%H:%M:%SZ")),
183
+ from_agent=d.get("from", ""),
184
+ to_agent=d.get("to", ""),
185
+ tenant_id=d.get("tenant_id", "default"),
186
+ trust_level=trust_data.get("level", 3) if isinstance(trust_data, dict) else 3,
187
+ trust_source=trust_data.get("source", "user_input") if isinstance(trust_data, dict) else "user_input",
188
+ payload_type=payload_data.get("type", "task") if isinstance(payload_data, dict) else "task",
189
+ content=payload_data.get("content", "") if isinstance(payload_data, dict) else "",
190
+ context=payload_data.get("context", {}) if isinstance(payload_data, dict) else {},
191
+ signals=NMPSignals(**{k: v for k, v in signals_data.items() if k in NMPSignals.__dataclass_fields__}),
192
+ budget=Budget(
193
+ max_tokens=budget_data.get("max_tokens", 5000) if isinstance(budget_data, dict) else 5000,
194
+ tokens_used=budget_data.get("tokens_used", 0) if isinstance(budget_data, dict) else 0,
195
+ system_mode=budget_data.get("system_mode", 1) if isinstance(budget_data, dict) else 1,
196
+ ),
197
+ results=d.get("results", {}),
198
+ )
199
+ for t in trace_data:
200
+ if isinstance(t, dict):
201
+ msg.trace.append(TraceEntry(
202
+ agent=t.get("agent", ""),
203
+ action=t.get("action", ""),
204
+ tokens=t.get("tokens", 0),
205
+ timestamp=t.get("timestamp", ""),
206
+ signals_snapshot=t.get("signals_snapshot", {}),
207
+ ))
208
+ return msg
209
+
210
+
211
+ def create_message(content: str = "", from_agent: str = "", trust: int | None = None,
212
+ system_mode: int = 1, max_tokens: int = 5000,
213
+ trust_level: int | None = None, trust_source: str = "user_input",
214
+ **kwargs) -> NMPMessage:
215
+ effective_trust = trust_level if trust_level is not None else (trust if trust is not None else 3)
216
+ return NMPMessage(
217
+ content=content, from_agent=from_agent,
218
+ trust_level=effective_trust, trust_source=trust_source,
219
+ budget=Budget(max_tokens=max_tokens, system_mode=system_mode),
220
+ **kwargs,
221
+ )
222
+
223
+
224
+ def validate_message(msg_or_dict) -> list[str]:
225
+ """Validate an NMPMessage or a dict representation."""
226
+ errors = []
227
+ if isinstance(msg_or_dict, NMPMessage):
228
+ errors.extend(msg_or_dict.signals.validate())
229
+ if msg_or_dict.version != "1.0":
230
+ errors.append(f"Unsupported version: {msg_or_dict.version}")
231
+ if not 0 <= msg_or_dict.trust_level <= 3:
232
+ errors.append(f"Invalid trust_level: {msg_or_dict.trust_level}")
233
+ if msg_or_dict.payload_type not in ("task", "response", "feedback", "alert", "pain", "reflex"):
234
+ errors.append(f"Invalid payload_type: {msg_or_dict.payload_type}")
235
+ if msg_or_dict.budget.tokens_used > msg_or_dict.budget.max_tokens:
236
+ errors.append("Budget exceeded")
237
+ return errors
238
+
239
+ # Dict validation (schema compliance)
240
+ d = msg_or_dict
241
+ if not isinstance(d, dict):
242
+ return ["Input must be NMPMessage or dict"]
243
+
244
+ if "version" not in d:
245
+ errors.append("Missing required field: version")
246
+ elif d["version"] != "1.0":
247
+ errors.append(f"Unsupported version: {d['version']}")
248
+
249
+ trust = d.get("trust", {})
250
+ if isinstance(trust, dict):
251
+ level = trust.get("level", 3)
252
+ if not 0 <= level <= 3:
253
+ errors.append(f"Invalid trust.level: {level}")
254
+
255
+ payload = d.get("payload", {})
256
+ if isinstance(payload, dict):
257
+ ptype = payload.get("type", "task")
258
+ if ptype not in ("task", "response", "feedback", "alert", "pain", "reflex"):
259
+ errors.append(f"Invalid payload.type: {ptype}")
260
+
261
+ signals = d.get("signals", {})
262
+ if isinstance(signals, dict):
263
+ for fname in ("urgency", "quality", "inhibition", "activation", "focus"):
264
+ val = signals.get(fname, 0.5)
265
+ if not 0.0 <= val <= 1.0:
266
+ errors.append(f"{fname} must be 0.0-1.0, got {val}")
267
+ reward = signals.get("reward", 0.0)
268
+ if not -0.5 <= reward <= 1.0:
269
+ errors.append(f"reward must be -0.5-1.0, got {reward}")
270
+
271
+ budget = d.get("budget", {})
272
+ if isinstance(budget, dict):
273
+ sm = budget.get("system_mode", 1)
274
+ if not isinstance(sm, int) or sm not in (0, 1, 2, 3):
275
+ errors.append(f"Invalid budget.system_mode: {sm}")
276
+
277
+ return errors
@@ -0,0 +1,49 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "nmp-protocol"
7
+ version = "1.0.0"
8
+ description = "NMP (NeuroMessage Protocol) — Open communication protocol for multi-agent systems with behavioral modulation signals."
9
+ readme = "README.md"
10
+ license = "Apache-2.0"
11
+ requires-python = ">=3.10"
12
+ authors = [
13
+ { name = "Renato Aparecido Gomes", email = "renato@koloni.dev" },
14
+ ]
15
+ keywords = [
16
+ "nmp", "neuro-message-protocol", "multi-agent", "protocol",
17
+ "behavioral-signals", "agent-communication", "mcp", "a2a",
18
+ "somatic-markers", "active-inference",
19
+ ]
20
+ classifiers = [
21
+ "Development Status :: 4 - Beta",
22
+ "Intended Audience :: Developers",
23
+ "License :: OSI Approved :: Apache Software License",
24
+ "Programming Language :: Python :: 3",
25
+ "Programming Language :: Python :: 3.10",
26
+ "Programming Language :: Python :: 3.11",
27
+ "Programming Language :: Python :: 3.12",
28
+ "Programming Language :: Python :: 3.13",
29
+ "Programming Language :: Python :: 3.14",
30
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
31
+ "Topic :: Software Development :: Libraries :: Python Modules",
32
+ "Topic :: System :: Networking",
33
+ ]
34
+
35
+ [project.urls]
36
+ Homepage = "https://nmp-protocol.org"
37
+ Documentation = "https://nmp-protocol.org"
38
+ Repository = "https://github.com/AIP-Labs/nmp-protocol"
39
+ Specification = "https://nmp-protocol.org"
40
+ "Bug Tracker" = "https://github.com/AIP-Labs/nmp-protocol/issues"
41
+
42
+ [tool.hatch.build.targets.sdist]
43
+ include = ["nmp/"]
44
+
45
+ [tool.hatch.build.targets.wheel]
46
+ packages = ["nmp"]
47
+
48
+ [tool.pytest.ini_options]
49
+ testpaths = ["tests"]