osmp 2.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.
- osmp-2.0.0/PKG-INFO +140 -0
- osmp-2.0.0/README.md +114 -0
- osmp-2.0.0/osmp/__init__.py +146 -0
- osmp-2.0.0/osmp/core.py +124 -0
- osmp-2.0.0/osmp/data/OSMP-semantic-dictionary-v14.csv +575 -0
- osmp-2.0.0/osmp/protocol.py +2920 -0
- osmp-2.0.0/osmp/wire.py +942 -0
- osmp-2.0.0/osmp.egg-info/PKG-INFO +140 -0
- osmp-2.0.0/osmp.egg-info/SOURCES.txt +13 -0
- osmp-2.0.0/osmp.egg-info/dependency_links.txt +1 -0
- osmp-2.0.0/osmp.egg-info/entry_points.txt +2 -0
- osmp-2.0.0/osmp.egg-info/requires.txt +3 -0
- osmp-2.0.0/osmp.egg-info/top_level.txt +1 -0
- osmp-2.0.0/pyproject.toml +45 -0
- osmp-2.0.0/setup.cfg +4 -0
osmp-2.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: osmp
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: OSMP -- Octid Semantic Mesh Protocol. Deterministic agentic instruction encoding.
|
|
5
|
+
Author-email: Clay Holberg <clay@octid.io>
|
|
6
|
+
Project-URL: Homepage, https://github.com/Octid-io/cloudless-sky
|
|
7
|
+
Project-URL: Documentation, https://github.com/Octid-io/cloudless-sky/blob/main/protocol/spec/OSMP-SPEC-v1.md
|
|
8
|
+
Project-URL: Repository, https://github.com/Octid-io/cloudless-sky
|
|
9
|
+
Project-URL: Issues, https://github.com/Octid-io/cloudless-sky/issues
|
|
10
|
+
Keywords: osmp,agentic,mesh,protocol,semantic,encoding,lora,ai
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Communications
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
Provides-Extra: dpack
|
|
25
|
+
Requires-Dist: zstandard>=0.21; extra == "dpack"
|
|
26
|
+
|
|
27
|
+
# OSMP Python SDK
|
|
28
|
+
|
|
29
|
+
Reference implementation of the Octid Semantic Mesh Protocol. Encodes, decodes, and validates agentic AI instructions using SAL (Semantic Assembly Language). 342 opcodes across 26 namespaces. Inference-free decode by table lookup.
|
|
30
|
+
|
|
31
|
+
## Install
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
pip install osmp
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Zero dependencies beyond Python standard library (optional `zstandard` for D:PACK).
|
|
38
|
+
|
|
39
|
+
## Tier 1: Two Functions, Zero Setup
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from osmp import encode, decode
|
|
43
|
+
|
|
44
|
+
sal = encode(["H:HR@NODE1>120", "H:CASREP", "M:EVA@*"])
|
|
45
|
+
# "H:HR@NODE1>120;H:CASREP;M:EVA@*"
|
|
46
|
+
|
|
47
|
+
text = decode("H:HR@NODE1>120;H:CASREP;M:EVA@*")
|
|
48
|
+
# "heart_rate at NODE1 priority 120; casualty_report; evacuation at broadcast"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Three lines. No instantiation. Module-level singleton, cached on first call.
|
|
52
|
+
|
|
53
|
+
### Additional Tier 1 Functions
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from osmp import validate, lookup, byte_size
|
|
57
|
+
|
|
58
|
+
result = validate("R:MOV@BOT1⚠")
|
|
59
|
+
print(result.valid) # False -- ⚠ requires I:§ precondition
|
|
60
|
+
|
|
61
|
+
definition = lookup("R:WPT")
|
|
62
|
+
# "waypoint"
|
|
63
|
+
|
|
64
|
+
print(byte_size("H:HR@NODE1>120"))
|
|
65
|
+
# 15
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Tier 2: Class-Based Interface
|
|
69
|
+
|
|
70
|
+
For configuration beyond defaults (custom ASD floor, pre-loaded dependency rules, direct ASD access):
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from osmp.core import OSMP
|
|
74
|
+
|
|
75
|
+
o = OSMP()
|
|
76
|
+
sal = o.encode(["H:HR@NODE1>120", "H:CASREP"])
|
|
77
|
+
text = o.decode(sal)
|
|
78
|
+
result = o.validate(sal)
|
|
79
|
+
definition = o.lookup("H", "HR")
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Tier 3: Full Protocol Access
|
|
83
|
+
|
|
84
|
+
Direct access to encoder, decoder, ASD, and all protocol internals:
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from osmp.protocol import SALEncoder, SALDecoder, AdaptiveSharedDictionary, validate_composition
|
|
88
|
+
|
|
89
|
+
asd = AdaptiveSharedDictionary()
|
|
90
|
+
enc = SALEncoder(asd)
|
|
91
|
+
dec = SALDecoder(asd)
|
|
92
|
+
|
|
93
|
+
sal = enc.encode_frame("R", "MOV", target="BOT1", cc="↺")
|
|
94
|
+
result = dec.decode_frame(sal)
|
|
95
|
+
# result.namespace = "R"
|
|
96
|
+
# result.opcode = "MOV"
|
|
97
|
+
# result.opcode_meaning = "move"
|
|
98
|
+
# result.consequence_class_name = "REVERSIBLE"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Composition Validation
|
|
102
|
+
|
|
103
|
+
Eight deterministic rules enforced before any instruction hits the wire:
|
|
104
|
+
|
|
105
|
+
1. **Hallucination check** -- every opcode must exist in the ASD
|
|
106
|
+
2. **Namespace-as-target** -- `@` must not be followed by `NS:OPCODE`
|
|
107
|
+
3. **R namespace consequence class** -- mandatory except `R:ESTOP`
|
|
108
|
+
4. **I:§ precondition** -- ⚠ and ⊘ require `I:§` in the chain
|
|
109
|
+
5. **Byte check** -- SAL bytes must not exceed NL bytes (exception: R safety chains)
|
|
110
|
+
6. **Slash rejection** -- `/` is not a SAL operator
|
|
111
|
+
7. **Mixed-mode check** -- no natural language embedded in SAL frames
|
|
112
|
+
8. **Regulatory dependency** -- REQUIRES rules from loaded MDR corpora
|
|
113
|
+
|
|
114
|
+
## Domain Code Resolution
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from osmp.protocol import BlockCompressor
|
|
118
|
+
|
|
119
|
+
bc = BlockCompressor()
|
|
120
|
+
bc.load("mdr/icd10cm/MDR-ICD10CM-FY2026-blk.dpack")
|
|
121
|
+
result = bc.resolve("J93.0")
|
|
122
|
+
# "Spontaneous tension pneumothorax"
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Three corpora bundled: ICD-10-CM (74,719 codes), ISO 20022 (47,835 codes), MITRE ATT&CK (1,661 codes).
|
|
126
|
+
|
|
127
|
+
## MCP Server
|
|
128
|
+
|
|
129
|
+
The MCP server is a separate package that wraps this SDK:
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
pip install osmp-mcp
|
|
133
|
+
osmp-mcp
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Nine tools for AI client integration. Connect from Claude Code (`claude mcp add osmp -- osmp-mcp`), Claude Desktop, Cursor, or any MCP-compatible client.
|
|
137
|
+
|
|
138
|
+
## License
|
|
139
|
+
|
|
140
|
+
Apache 2.0. Patent pending. Filed March 17, 2026.
|
osmp-2.0.0/README.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# OSMP Python SDK
|
|
2
|
+
|
|
3
|
+
Reference implementation of the Octid Semantic Mesh Protocol. Encodes, decodes, and validates agentic AI instructions using SAL (Semantic Assembly Language). 342 opcodes across 26 namespaces. Inference-free decode by table lookup.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
pip install osmp
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Zero dependencies beyond Python standard library (optional `zstandard` for D:PACK).
|
|
12
|
+
|
|
13
|
+
## Tier 1: Two Functions, Zero Setup
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from osmp import encode, decode
|
|
17
|
+
|
|
18
|
+
sal = encode(["H:HR@NODE1>120", "H:CASREP", "M:EVA@*"])
|
|
19
|
+
# "H:HR@NODE1>120;H:CASREP;M:EVA@*"
|
|
20
|
+
|
|
21
|
+
text = decode("H:HR@NODE1>120;H:CASREP;M:EVA@*")
|
|
22
|
+
# "heart_rate at NODE1 priority 120; casualty_report; evacuation at broadcast"
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Three lines. No instantiation. Module-level singleton, cached on first call.
|
|
26
|
+
|
|
27
|
+
### Additional Tier 1 Functions
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from osmp import validate, lookup, byte_size
|
|
31
|
+
|
|
32
|
+
result = validate("R:MOV@BOT1⚠")
|
|
33
|
+
print(result.valid) # False -- ⚠ requires I:§ precondition
|
|
34
|
+
|
|
35
|
+
definition = lookup("R:WPT")
|
|
36
|
+
# "waypoint"
|
|
37
|
+
|
|
38
|
+
print(byte_size("H:HR@NODE1>120"))
|
|
39
|
+
# 15
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Tier 2: Class-Based Interface
|
|
43
|
+
|
|
44
|
+
For configuration beyond defaults (custom ASD floor, pre-loaded dependency rules, direct ASD access):
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from osmp.core import OSMP
|
|
48
|
+
|
|
49
|
+
o = OSMP()
|
|
50
|
+
sal = o.encode(["H:HR@NODE1>120", "H:CASREP"])
|
|
51
|
+
text = o.decode(sal)
|
|
52
|
+
result = o.validate(sal)
|
|
53
|
+
definition = o.lookup("H", "HR")
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Tier 3: Full Protocol Access
|
|
57
|
+
|
|
58
|
+
Direct access to encoder, decoder, ASD, and all protocol internals:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from osmp.protocol import SALEncoder, SALDecoder, AdaptiveSharedDictionary, validate_composition
|
|
62
|
+
|
|
63
|
+
asd = AdaptiveSharedDictionary()
|
|
64
|
+
enc = SALEncoder(asd)
|
|
65
|
+
dec = SALDecoder(asd)
|
|
66
|
+
|
|
67
|
+
sal = enc.encode_frame("R", "MOV", target="BOT1", cc="↺")
|
|
68
|
+
result = dec.decode_frame(sal)
|
|
69
|
+
# result.namespace = "R"
|
|
70
|
+
# result.opcode = "MOV"
|
|
71
|
+
# result.opcode_meaning = "move"
|
|
72
|
+
# result.consequence_class_name = "REVERSIBLE"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Composition Validation
|
|
76
|
+
|
|
77
|
+
Eight deterministic rules enforced before any instruction hits the wire:
|
|
78
|
+
|
|
79
|
+
1. **Hallucination check** -- every opcode must exist in the ASD
|
|
80
|
+
2. **Namespace-as-target** -- `@` must not be followed by `NS:OPCODE`
|
|
81
|
+
3. **R namespace consequence class** -- mandatory except `R:ESTOP`
|
|
82
|
+
4. **I:§ precondition** -- ⚠ and ⊘ require `I:§` in the chain
|
|
83
|
+
5. **Byte check** -- SAL bytes must not exceed NL bytes (exception: R safety chains)
|
|
84
|
+
6. **Slash rejection** -- `/` is not a SAL operator
|
|
85
|
+
7. **Mixed-mode check** -- no natural language embedded in SAL frames
|
|
86
|
+
8. **Regulatory dependency** -- REQUIRES rules from loaded MDR corpora
|
|
87
|
+
|
|
88
|
+
## Domain Code Resolution
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from osmp.protocol import BlockCompressor
|
|
92
|
+
|
|
93
|
+
bc = BlockCompressor()
|
|
94
|
+
bc.load("mdr/icd10cm/MDR-ICD10CM-FY2026-blk.dpack")
|
|
95
|
+
result = bc.resolve("J93.0")
|
|
96
|
+
# "Spontaneous tension pneumothorax"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Three corpora bundled: ICD-10-CM (74,719 codes), ISO 20022 (47,835 codes), MITRE ATT&CK (1,661 codes).
|
|
100
|
+
|
|
101
|
+
## MCP Server
|
|
102
|
+
|
|
103
|
+
The MCP server is a separate package that wraps this SDK:
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
pip install osmp-mcp
|
|
107
|
+
osmp-mcp
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Nine tools for AI client integration. Connect from Claude Code (`claude mcp add osmp -- osmp-mcp`), Claude Desktop, Cursor, or any MCP-compatible client.
|
|
111
|
+
|
|
112
|
+
## License
|
|
113
|
+
|
|
114
|
+
Apache 2.0. Patent pending. Filed March 17, 2026.
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"""
|
|
2
|
+
OSMP — Octid Semantic Mesh Protocol
|
|
3
|
+
Tier 1 API: Two functions. Zero setup.
|
|
4
|
+
|
|
5
|
+
from osmp import encode, decode
|
|
6
|
+
|
|
7
|
+
sal = encode(["H:HR@NODE1>120", "H:CASREP", "M:EVA@*"])
|
|
8
|
+
print(sal) # "H:HR@NODE1>120;H:CASREP;M:EVA@*"
|
|
9
|
+
|
|
10
|
+
text = decode("H:HR@NODE1>120;H:CASREP;M:EVA@*")
|
|
11
|
+
print(text) # "heart_rate at NODE1 priority 120; casualty_report; evacuation at broadcast"
|
|
12
|
+
|
|
13
|
+
Patent: OSMP-001-UTIL (pending) — inventor Clay Holberg
|
|
14
|
+
License: Apache 2.0
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
# Lazy-loaded singleton. The protocol module is heavy (~2700 lines).
|
|
20
|
+
# We don't import it until the first call, and we cache the instances.
|
|
21
|
+
_asd = None
|
|
22
|
+
_encoder = None
|
|
23
|
+
_decoder = None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _init():
|
|
27
|
+
"""Initialize singleton ASD, encoder, and decoder on first use."""
|
|
28
|
+
global _asd, _encoder, _decoder
|
|
29
|
+
if _asd is not None:
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
# Import from wherever the protocol internals live.
|
|
33
|
+
# This supports both the current osmp_mcp.osmp layout and
|
|
34
|
+
# the future osmp.protocol layout.
|
|
35
|
+
try:
|
|
36
|
+
from osmp.protocol import AdaptiveSharedDictionary, SALEncoder, SALDecoder
|
|
37
|
+
except ImportError:
|
|
38
|
+
try:
|
|
39
|
+
from osmp_mcp.osmp import AdaptiveSharedDictionary, SALEncoder, SALDecoder
|
|
40
|
+
except ImportError:
|
|
41
|
+
# Fallback: same package, different module name
|
|
42
|
+
from .osmp import AdaptiveSharedDictionary, SALEncoder, SALDecoder
|
|
43
|
+
|
|
44
|
+
_asd = AdaptiveSharedDictionary()
|
|
45
|
+
_encoder = SALEncoder(_asd)
|
|
46
|
+
_decoder = SALDecoder(_asd)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def encode(input_data) -> str:
|
|
50
|
+
"""Encode to SAL.
|
|
51
|
+
|
|
52
|
+
Accepts:
|
|
53
|
+
list[str] — opcode strings, joined with ; (sequence operator)
|
|
54
|
+
str — if it looks like SAL (contains :), validates and returns as-is
|
|
55
|
+
if it looks like natural language, returns as NL_PASSTHROUGH
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
SAL string ready for transmission.
|
|
59
|
+
|
|
60
|
+
Examples:
|
|
61
|
+
encode(["H:HR@NODE1>120", "H:CASREP", "M:EVA@*"])
|
|
62
|
+
→ "H:HR@NODE1>120;H:CASREP;M:EVA@*"
|
|
63
|
+
|
|
64
|
+
encode("H:HR@NODE1>120;H:CASREP;M:EVA@*")
|
|
65
|
+
→ "H:HR@NODE1>120;H:CASREP;M:EVA@*" (passthrough)
|
|
66
|
+
"""
|
|
67
|
+
_init()
|
|
68
|
+
|
|
69
|
+
if isinstance(input_data, list):
|
|
70
|
+
return _encoder.encode_sequence(input_data)
|
|
71
|
+
|
|
72
|
+
if isinstance(input_data, str):
|
|
73
|
+
# If it contains namespace:opcode patterns, treat as pre-formatted SAL
|
|
74
|
+
if ":" in input_data and any(
|
|
75
|
+
c in input_data for c in "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
76
|
+
):
|
|
77
|
+
# Looks like SAL already — return as-is
|
|
78
|
+
return input_data
|
|
79
|
+
# Natural language — return as NL passthrough
|
|
80
|
+
# (Future: NL-to-SAL conversion when composition engine is available)
|
|
81
|
+
return input_data
|
|
82
|
+
|
|
83
|
+
raise TypeError(
|
|
84
|
+
f"encode() accepts str or list[str], got {type(input_data).__name__}. "
|
|
85
|
+
f"Example: encode(['H:HR@NODE1>120', 'H:CASREP', 'M:EVA@*'])"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def decode(sal: str) -> str:
|
|
90
|
+
"""Decode SAL to natural language description.
|
|
91
|
+
|
|
92
|
+
Accepts:
|
|
93
|
+
str — a SAL instruction string (single frame or ;-separated sequence)
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
Human-readable natural language expansion of the SAL instruction,
|
|
97
|
+
resolved by ASD dictionary lookup. Zero inference.
|
|
98
|
+
|
|
99
|
+
Examples:
|
|
100
|
+
decode("H:HR@NODE1>120;H:CASREP;M:EVA@*")
|
|
101
|
+
→ "H:heart_rate →NODE1>120; H:casualty_report; M:evacuation →*"
|
|
102
|
+
"""
|
|
103
|
+
_init()
|
|
104
|
+
frames = [f.strip() for f in sal.split(";") if f.strip()]
|
|
105
|
+
if len(frames) <= 1:
|
|
106
|
+
return _decoder.decode_natural_language(sal)
|
|
107
|
+
return "; ".join(_decoder.decode_natural_language(f) for f in frames)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def validate(sal: str, nl: str = "", dependency_rules=None):
|
|
111
|
+
"""Validate a composed SAL instruction against all eight rules.
|
|
112
|
+
|
|
113
|
+
Returns a CompositionResult with .valid (bool) and .issues (list).
|
|
114
|
+
"""
|
|
115
|
+
_init()
|
|
116
|
+
try:
|
|
117
|
+
from osmp.protocol import validate_composition
|
|
118
|
+
except ImportError:
|
|
119
|
+
try:
|
|
120
|
+
from osmp_mcp.osmp import validate_composition
|
|
121
|
+
except ImportError:
|
|
122
|
+
from .osmp import validate_composition
|
|
123
|
+
|
|
124
|
+
return validate_composition(sal, nl, _asd, dependency_rules=dependency_rules)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def lookup(namespace_opcode: str) -> str | None:
|
|
128
|
+
"""Look up an opcode definition in the ASD.
|
|
129
|
+
|
|
130
|
+
Accepts: "H:HR" or namespace="H", opcode="HR"
|
|
131
|
+
Returns: definition string or None if not found.
|
|
132
|
+
"""
|
|
133
|
+
_init()
|
|
134
|
+
if ":" in namespace_opcode:
|
|
135
|
+
parts = namespace_opcode.split(":", 1)
|
|
136
|
+
return _asd.lookup(parts[0], parts[1])
|
|
137
|
+
return None
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def byte_size(sal: str) -> int:
|
|
141
|
+
"""Return UTF-8 byte count of a SAL string."""
|
|
142
|
+
return len(sal.encode("utf-8"))
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
# Version
|
|
146
|
+
__version__ = "2.0.0"
|
osmp-2.0.0/osmp/core.py
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""
|
|
2
|
+
OSMP -- Octid Semantic Mesh Protocol
|
|
3
|
+
Tier 2 API: Class-based interface for advanced use.
|
|
4
|
+
|
|
5
|
+
from osmp.core import OSMP
|
|
6
|
+
|
|
7
|
+
o = OSMP()
|
|
8
|
+
sal = o.encode(["H:HR@NODE1>120", "H:CASREP", "M:EVA@*"])
|
|
9
|
+
text = o.decode(sal)
|
|
10
|
+
result = o.validate(sal)
|
|
11
|
+
|
|
12
|
+
For the two-function API, use Tier 1 instead:
|
|
13
|
+
|
|
14
|
+
from osmp import encode, decode
|
|
15
|
+
|
|
16
|
+
Patent: OSMP-001-UTIL (pending) -- inventor Clay Holberg
|
|
17
|
+
License: Apache 2.0
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import TYPE_CHECKING
|
|
24
|
+
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from .protocol import (
|
|
27
|
+
AdaptiveSharedDictionary,
|
|
28
|
+
CompositionResult,
|
|
29
|
+
DependencyRule,
|
|
30
|
+
SALDecoder,
|
|
31
|
+
SALEncoder,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class OSMP:
|
|
36
|
+
"""Full-featured OSMP codec with configurable ASD and dependency rules.
|
|
37
|
+
|
|
38
|
+
For zero-setup usage, prefer the module-level functions in ``osmp``.
|
|
39
|
+
This class exposes configuration points (custom ASD floor version,
|
|
40
|
+
pre-loaded dependency rules, direct ASD access) that the Tier 1
|
|
41
|
+
functions intentionally hide.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def __init__(
|
|
45
|
+
self,
|
|
46
|
+
floor_version: str | None = None,
|
|
47
|
+
dependency_rules: list[DependencyRule] | None = None,
|
|
48
|
+
):
|
|
49
|
+
from .protocol import AdaptiveSharedDictionary, SALDecoder, SALEncoder
|
|
50
|
+
|
|
51
|
+
if floor_version is not None:
|
|
52
|
+
self._asd: AdaptiveSharedDictionary = AdaptiveSharedDictionary(floor_version)
|
|
53
|
+
else:
|
|
54
|
+
self._asd = AdaptiveSharedDictionary()
|
|
55
|
+
|
|
56
|
+
self._encoder: SALEncoder = SALEncoder(self._asd)
|
|
57
|
+
self._decoder: SALDecoder = SALDecoder(self._asd)
|
|
58
|
+
self._dependency_rules: list[DependencyRule] | None = dependency_rules
|
|
59
|
+
|
|
60
|
+
# -- Encoding --------------------------------------------------------
|
|
61
|
+
|
|
62
|
+
def encode(self, instructions: list[str]) -> str:
|
|
63
|
+
"""Encode a list of opcode strings into a SAL instruction chain."""
|
|
64
|
+
return self._encoder.encode_sequence(instructions)
|
|
65
|
+
|
|
66
|
+
def encode_frame(
|
|
67
|
+
self,
|
|
68
|
+
namespace: str,
|
|
69
|
+
opcode: str,
|
|
70
|
+
target: str | None = None,
|
|
71
|
+
query_slot: str | None = None,
|
|
72
|
+
consequence_class: str | None = None,
|
|
73
|
+
) -> str:
|
|
74
|
+
"""Encode a single SAL frame from structured fields."""
|
|
75
|
+
return self._encoder.encode_frame(
|
|
76
|
+
namespace, opcode, target, query_slot,
|
|
77
|
+
consequence_class=consequence_class,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# -- Decoding --------------------------------------------------------
|
|
81
|
+
|
|
82
|
+
def decode(self, sal: str) -> str:
|
|
83
|
+
"""Decode a SAL string to natural language. Handles sequences."""
|
|
84
|
+
frames = [f.strip() for f in sal.split(";") if f.strip()]
|
|
85
|
+
if len(frames) <= 1:
|
|
86
|
+
return self._decoder.decode_natural_language(sal)
|
|
87
|
+
return "; ".join(self._decoder.decode_natural_language(f) for f in frames)
|
|
88
|
+
|
|
89
|
+
def decode_frame(self, sal: str):
|
|
90
|
+
"""Decode a single SAL frame to a DecodedInstruction."""
|
|
91
|
+
return self._decoder.decode_frame(sal)
|
|
92
|
+
|
|
93
|
+
# -- Validation ------------------------------------------------------
|
|
94
|
+
|
|
95
|
+
def validate(self, sal: str, nl: str = ""):
|
|
96
|
+
"""Validate a SAL instruction against all eight composition rules."""
|
|
97
|
+
from .protocol import validate_composition
|
|
98
|
+
return validate_composition(
|
|
99
|
+
sal, nl, self._asd,
|
|
100
|
+
dependency_rules=self._dependency_rules,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# -- Lookup ----------------------------------------------------------
|
|
104
|
+
|
|
105
|
+
def lookup(self, namespace: str, opcode: str) -> str | None:
|
|
106
|
+
"""Look up an opcode definition in the ASD."""
|
|
107
|
+
return self._asd.lookup(namespace, opcode)
|
|
108
|
+
|
|
109
|
+
# -- Direct access ---------------------------------------------------
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def asd(self):
|
|
113
|
+
"""Direct access to the AdaptiveSharedDictionary instance."""
|
|
114
|
+
return self._asd
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def encoder(self):
|
|
118
|
+
"""Direct access to the SALEncoder instance."""
|
|
119
|
+
return self._encoder
|
|
120
|
+
|
|
121
|
+
@property
|
|
122
|
+
def decoder(self):
|
|
123
|
+
"""Direct access to the SALDecoder instance."""
|
|
124
|
+
return self._decoder
|