Skip to content

Commit 672f366

Browse files
author
Sergey Klein
committed
feat: add Vocabulary system and message validation
- Implemented complete Vocabulary class with 120+ semantic concepts - 10 categories: ENT, ACT, PROP, REL, LOG, MATH, TIME, SPACE, DATA, META - Concept validation, search, and category management - Detailed descriptions and examples for each concept - Implemented MessageValidator with three-stage validation: - Envelope validation (structure, required fields, formats) - Content validation (semantic concepts from vocabulary) - Timestamp validation (freshness, replay protection) - Integrated validation into PulseMessage: - Automatic validation on creation (can be disabled) - Manual validation method - Vocabulary-based concept checking - Added comprehensive test suites: - test_vocabulary.py: 40+ tests for vocabulary functionality - test_validator.py: 35+ tests for validation logic - Full coverage of validation edge cases and error handling - Created vocabulary_validation example: - Demonstrates vocabulary search and categories - Shows validation in action with valid/invalid messages - Provides helpful error messages with suggestions This completes core Week 1 functionality: ✓ PulseMessage class with JSON serialization ✓ Vocabulary system with semantic concepts ✓ Message validation framework ✓ 70+ unit tests ✓ 2 working examples
1 parent 58daa92 commit 672f366

7 files changed

Lines changed: 1862 additions & 0 deletions

File tree

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
"""
2+
PULSE Protocol - Vocabulary and Validation Example.
3+
4+
This example demonstrates:
5+
1. Using different vocabulary concepts
6+
2. Message validation
7+
3. Handling validation errors
8+
4. Vocabulary search functionality
9+
"""
10+
11+
from pulse import PulseMessage, Vocabulary, MessageValidator, ValidationError
12+
13+
14+
def print_section(title):
15+
"""Print a section header."""
16+
print("\n" + "=" * 60)
17+
print(title)
18+
print("=" * 60)
19+
20+
21+
def example_vocabulary_categories():
22+
"""Demonstrate vocabulary categories."""
23+
print_section("1. Vocabulary Categories")
24+
25+
categories = Vocabulary.get_all_categories()
26+
print(f"Available categories: {sorted(categories)}")
27+
print()
28+
29+
counts = Vocabulary.count_by_category()
30+
print("Concepts per category:")
31+
for cat in sorted(counts.keys()):
32+
count = counts[cat]
33+
print(f" {cat}: {count} concepts")
34+
35+
print(f"\nTotal concepts: {Vocabulary.get_total_count()}")
36+
37+
38+
def example_vocabulary_search():
39+
"""Demonstrate vocabulary search."""
40+
print_section("2. Vocabulary Search")
41+
42+
# Search for sentiment-related concepts
43+
print("Searching for 'sentiment':")
44+
results = Vocabulary.search("sentiment")
45+
for concept in results:
46+
desc = Vocabulary.get_description(concept)
47+
print(f" {concept}: {desc}")
48+
49+
print("\nSearching for 'query':")
50+
results = Vocabulary.search("query")
51+
for concept in results[:5]: # Show first 5
52+
desc = Vocabulary.get_description(concept)
53+
print(f" {concept}: {desc}")
54+
55+
print("\nSearching for 'data':")
56+
results = Vocabulary.search("data")
57+
print(f" Found {len(results)} concepts containing 'data'")
58+
59+
60+
def example_valid_messages():
61+
"""Create valid messages with different concepts."""
62+
print_section("3. Creating Valid Messages")
63+
64+
examples = [
65+
{
66+
"name": "Sentiment Analysis",
67+
"action": "ACT.ANALYZE.SENTIMENT",
68+
"target": "ENT.DATA.TEXT",
69+
"params": {"text": "I love PULSE Protocol!"},
70+
},
71+
{
72+
"name": "Data Query",
73+
"action": "ACT.QUERY.DATA",
74+
"target": "ENT.RESOURCE.DATABASE",
75+
"params": {"table": "users", "limit": 10},
76+
},
77+
{
78+
"name": "Text Generation",
79+
"action": "ACT.CREATE.TEXT",
80+
"target": "ENT.DATA.TEXT",
81+
"params": {"prompt": "Write a poem", "max_length": 100},
82+
},
83+
{
84+
"name": "Image Classification",
85+
"action": "ACT.ANALYZE.CLASSIFY",
86+
"target": "ENT.DATA.IMAGE",
87+
"params": {"model": "resnet50"},
88+
},
89+
]
90+
91+
for ex in examples:
92+
message = PulseMessage(
93+
action=ex["action"], target=ex["target"], parameters=ex["params"]
94+
)
95+
print(f"\n{ex['name']}:")
96+
print(f" Action: {ex['action']}")
97+
print(f" Target: {ex['target']}")
98+
print(f" Valid: {MessageValidator.validate_message(message, check_freshness=False)}")
99+
100+
101+
def example_invalid_messages():
102+
"""Demonstrate validation errors."""
103+
print_section("4. Validation Errors")
104+
105+
print("\nTrying to create message with invalid action:")
106+
try:
107+
message = PulseMessage(action="INVALID.ACTION")
108+
except ValidationError as e:
109+
print(f"✗ ValidationError: {e}")
110+
111+
print("\nTrying to create message with invalid target:")
112+
try:
113+
message = PulseMessage(action="ACT.QUERY.DATA", target="INVALID.TARGET")
114+
except ValidationError as e:
115+
print(f"✗ ValidationError: {e}")
116+
117+
print("\nSkipping validation on creation, then validating manually:")
118+
message = PulseMessage(action="ACT.QUERY.DATA", target="INVALID.TARGET", validate=False)
119+
print("✓ Message created (validation skipped)")
120+
121+
try:
122+
message.validate()
123+
except ValidationError as e:
124+
print(f"✗ Manual validation failed: {e}")
125+
126+
127+
def example_concept_details():
128+
"""Show detailed concept information."""
129+
print_section("5. Concept Details")
130+
131+
concept = "ACT.ANALYZE.SENTIMENT"
132+
print(f"Concept: {concept}")
133+
print(f" Category: {Vocabulary.get_category(concept)}")
134+
print(f" Description: {Vocabulary.get_description(concept)}")
135+
print(f" Examples: {', '.join(Vocabulary.get_examples(concept))}")
136+
137+
print(f"\nConcept: ENT.DATA.TEXT")
138+
print(f" Category: {Vocabulary.get_category('ENT.DATA.TEXT')}")
139+
print(f" Description: {Vocabulary.get_description('ENT.DATA.TEXT')}")
140+
print(f" Examples: {', '.join(Vocabulary.get_examples('ENT.DATA.TEXT'))}")
141+
142+
143+
def example_list_by_category():
144+
"""List concepts by category."""
145+
print_section("6. List Concepts by Category")
146+
147+
# List action concepts
148+
actions = Vocabulary.list_by_category("ACT")
149+
print(f"ACT (Actions) - {len(actions)} concepts:")
150+
for concept in sorted(actions)[:10]: # Show first 10
151+
print(f" {concept}")
152+
if len(actions) > 10:
153+
print(f" ... and {len(actions) - 10} more")
154+
155+
# List entity concepts
156+
print(f"\nENT (Entities) - {len(Vocabulary.list_by_category('ENT'))} concepts:")
157+
entities = Vocabulary.list_by_category("ENT")
158+
for concept in sorted(entities)[:10]: # Show first 10
159+
print(f" {concept}")
160+
if len(entities) > 10:
161+
print(f" ... and {len(entities) - 10} more")
162+
163+
164+
def main():
165+
"""Run all examples."""
166+
print("=" * 60)
167+
print("PULSE Protocol - Vocabulary & Validation Demo")
168+
print("=" * 60)
169+
170+
example_vocabulary_categories()
171+
example_vocabulary_search()
172+
example_valid_messages()
173+
example_invalid_messages()
174+
example_concept_details()
175+
example_list_by_category()
176+
177+
print("\n" + "=" * 60)
178+
print("✓ Demo completed successfully!")
179+
print("=" * 60)
180+
print("\nKey Takeaways:")
181+
print(" • PULSE has 120+ semantic concepts across 10 categories")
182+
print(" • All concepts are validated automatically")
183+
print(" • Invalid concepts provide helpful suggestions")
184+
print(" • You can search vocabulary by keyword")
185+
print(" • Validation can be skipped if needed")
186+
print()
187+
188+
189+
if __name__ == "__main__":
190+
main()

pulse/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
from pulse.version import __version__, __version_info__
99
from pulse.message import PulseMessage
10+
from pulse.vocabulary import Vocabulary
11+
from pulse.validator import MessageValidator
1012
from pulse.exceptions import (
1113
PulseException,
1214
ValidationError,
@@ -22,6 +24,8 @@
2224
"__version__",
2325
"__version_info__",
2426
"PulseMessage",
27+
"Vocabulary",
28+
"MessageValidator",
2529
"PulseException",
2630
"ValidationError",
2731
"EncodingError",

pulse/message.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from typing import Optional, Dict, Any
44
import uuid
55
import json
6+
from pulse.validator import MessageValidator
7+
from pulse.exceptions import ValidationError
68

79

810
class PulseMessage:
@@ -31,6 +33,7 @@ def __init__(
3133
target: Optional[str] = None,
3234
parameters: Optional[Dict[str, Any]] = None,
3335
sender: str = "default-agent",
36+
validate: bool = True,
3437
) -> None:
3538
"""
3639
Initialize a PULSE message.
@@ -40,6 +43,10 @@ def __init__(
4043
target: Target object concept (e.g., "ENT.DATA.TEXT")
4144
parameters: Additional parameters for the action
4245
sender: Agent ID of the sender
46+
validate: Whether to validate the message (default True)
47+
48+
Raises:
49+
ValidationError: If validation is enabled and message is invalid
4350
4451
Example:
4552
>>> message = PulseMessage(
@@ -56,6 +63,10 @@ def __init__(
5663
"parameters": parameters or {},
5764
}
5865

66+
# Validate message if requested
67+
if validate:
68+
self.validate()
69+
5970
def _create_envelope(self, sender: str) -> Dict[str, Any]:
6071
"""
6172
Create message envelope with metadata.
@@ -144,3 +155,23 @@ def __repr__(self) -> str:
144155
def __str__(self) -> str:
145156
"""Return human-readable string representation."""
146157
return self.to_json()
158+
159+
def validate(self, check_freshness: bool = False) -> bool:
160+
"""
161+
Validate this message.
162+
163+
Args:
164+
check_freshness: Whether to check timestamp freshness (default False)
165+
166+
Returns:
167+
True if message is valid
168+
169+
Raises:
170+
ValidationError: If message is invalid
171+
172+
Example:
173+
>>> message = PulseMessage(action="ACT.QUERY.DATA", validate=False)
174+
>>> message.validate()
175+
True
176+
"""
177+
return MessageValidator.validate_message(self, check_freshness=check_freshness)

0 commit comments

Comments
 (0)