Skip to content

Commit b3d29cd

Browse files
committed
Add integration examples, tests, and update .gitignore
This commit adds comprehensive examples and tests for the app_meshed platform: ## Integration Examples (examples/) - ju_schema_example.py: Demonstrates JSON Schema generation from function signatures * Shows how i2 + ju enable automatic form generation * Examples of various parameter types and their schemas * API usage patterns for RJSF integration - dol_browser_example.py: Demonstrates source-keyed data access with dol * Unified Mapping interface across all stores * Examples of storing/retrieving DAGs, configs * Bulk operations and store statistics * HTTP API mapping - stream_slicing_example.py: Demonstrates creek-inspired [bt:tt] time-based slicing * Single stream time-based access * Multi-channel synchronized slicing * Sample data generation for audio and sensors * Different slicing patterns and use cases ## Tests (tests/) - test_function_registry.py: 11 tests for function registration and metadata * Registration, retrieval, override, unregister * Metadata extraction with i2 * Error handling for edge cases - test_schema_service.py: 9 tests for schema generation * Function to schema conversion * Object to schema conversion * Type mapping validation * Default values and docstrings - test_dag_service.py: 6 tests for DAG operations * Simple and chained DAG execution * Validation of configurations * Error handling ## Configuration - Updated .gitignore to exclude app_meshed runtime data: * data/ directory (dol stores) * *.npy (numpy stream data) * *.pkl (pickled functions) * Audio files (*.wav, *.mp3) All examples are runnable and demonstrate the three-phase architecture: Phase I (dol), Phase II (meshed), and Phase III (creek).
1 parent 6e627cc commit b3d29cd

File tree

8 files changed

+1009
-0
lines changed

8 files changed

+1009
-0
lines changed

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@ wads_configs.json
22
data/wads_configs.json
33
wads/data/wads_configs.json
44

5+
# app_meshed specific
6+
data/
7+
*.npy
8+
*.pkl
9+
*.wav
10+
*.mp3
11+
512
# Byte-compiled / optimized / DLL files
613
__pycache__/
714
*.py[cod]

examples/dol_browser_example.py

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
"""Example demonstrating dol-based store browsing.
2+
3+
This example shows how the unified root store provides a flat,
4+
key-value interface to different types of data storage.
5+
"""
6+
7+
import json
8+
import tempfile
9+
from pathlib import Path
10+
11+
from app_meshed.stores.root_store import create_default_root_store
12+
13+
14+
def main():
15+
"""Run the dol browser example."""
16+
print("=" * 70)
17+
print("DOL Store Browser Example")
18+
print("=" * 70)
19+
print()
20+
21+
# Create a temporary directory for this example
22+
with tempfile.TemporaryDirectory() as tmpdir:
23+
print(f"Using temporary directory: {tmpdir}")
24+
print()
25+
26+
# Create the root store
27+
root = create_default_root_store(base_path=tmpdir)
28+
29+
# Example 1: Storing and retrieving DAG configurations
30+
print("Example 1: DAG Configuration Storage (meshes store)")
31+
print("-" * 70)
32+
print()
33+
34+
dag_config = {
35+
"name": "example_dag",
36+
"nodes": [
37+
{"id": "node1", "function": "add"},
38+
{"id": "node2", "function": "multiply"},
39+
],
40+
"edges": [
41+
{
42+
"source": "node1",
43+
"target": "node2",
44+
"sourceOutput": "node1",
45+
"targetInput": "a",
46+
}
47+
],
48+
}
49+
50+
# Store the DAG (automatic JSON serialization)
51+
root.meshes["my_first_dag"] = dag_config
52+
print("✓ Stored DAG configuration: 'my_first_dag'")
53+
print()
54+
55+
# Retrieve it
56+
retrieved = root.meshes["my_first_dag"]
57+
print("Retrieved DAG:")
58+
print(json.dumps(retrieved, indent=2))
59+
print()
60+
61+
# List all meshes
62+
print(f"All meshes in store: {list(root.meshes.keys())}")
63+
print()
64+
65+
# Example 2: Application configurations
66+
print("=" * 70)
67+
print("Example 2: Application Configuration (configs store)")
68+
print("-" * 70)
69+
print()
70+
71+
app_config = {
72+
"server": {"host": "0.0.0.0", "port": 8000},
73+
"data_path": "/data",
74+
"features": {
75+
"enable_streaming": True,
76+
"max_upload_size": 104857600, # 100MB
77+
},
78+
}
79+
80+
root.configs["app_settings"] = app_config
81+
print("✓ Stored configuration: 'app_settings'")
82+
print()
83+
84+
# Retrieve and display
85+
settings = root.configs["app_settings"]
86+
print("Retrieved settings:")
87+
print(json.dumps(settings, indent=2))
88+
print()
89+
90+
# Example 3: Working with multiple items
91+
print("=" * 70)
92+
print("Example 3: Bulk Operations")
93+
print("-" * 70)
94+
print()
95+
96+
# Store multiple DAGs
97+
dags = {
98+
"simple_add": {
99+
"name": "simple_add",
100+
"nodes": [{"id": "add", "function": "add"}],
101+
"edges": [],
102+
},
103+
"simple_multiply": {
104+
"name": "simple_multiply",
105+
"nodes": [{"id": "mul", "function": "multiply"}],
106+
"edges": [],
107+
},
108+
"chained": {
109+
"name": "chained",
110+
"nodes": [
111+
{"id": "step1", "function": "add"},
112+
{"id": "step2", "function": "multiply"},
113+
],
114+
"edges": [
115+
{
116+
"source": "step1",
117+
"target": "step2",
118+
"sourceOutput": "step1",
119+
"targetInput": "a",
120+
}
121+
],
122+
},
123+
}
124+
125+
for name, config in dags.items():
126+
root.meshes[name] = config
127+
print(f"✓ Stored DAG: '{name}'")
128+
129+
print()
130+
print(f"Total DAGs in store: {len(list(root.meshes.keys()))}")
131+
print(f"DAG names: {list(root.meshes.keys())}")
132+
print()
133+
134+
# Example 4: Store statistics
135+
print("=" * 70)
136+
print("Example 4: Store Statistics")
137+
print("-" * 70)
138+
print()
139+
140+
all_keys = root.list_all_keys()
141+
print("Items in each store:")
142+
for store_name, keys in all_keys.items():
143+
print(f" {store_name}: {len(keys)} items")
144+
if keys:
145+
print(f" Keys: {keys}")
146+
print()
147+
148+
# Example 5: Source-keyed architecture in action
149+
print("=" * 70)
150+
print("Example 5: Source-Keyed Architecture")
151+
print("-" * 70)
152+
print()
153+
154+
print("Key insight: All stores use the same dict-like interface!")
155+
print()
156+
print("Code examples:")
157+
print(" root.meshes['my_dag'] = {...} # Store a DAG")
158+
print(" root.configs['settings'] = {...} # Store config")
159+
print(" dag = root.meshes['my_dag'] # Retrieve DAG")
160+
print(" 'my_dag' in root.meshes # Check existence")
161+
print(" del root.meshes['my_dag'] # Delete DAG")
162+
print(" list(root.meshes.keys()) # List all keys")
163+
print()
164+
165+
# Demonstrate the interface
166+
print("Live demonstration:")
167+
print(f" 'simple_add' in root.meshes -> {('simple_add' in root.meshes)}")
168+
print(f" 'nonexistent' in root.meshes -> {('nonexistent' in root.meshes)}")
169+
print()
170+
171+
# Example 6: How this maps to HTTP API
172+
print("=" * 70)
173+
print("Example 6: HTTP API Mapping")
174+
print("-" * 70)
175+
print()
176+
177+
print("Python code -> HTTP API:")
178+
print()
179+
print(" root.meshes['my_dag'] = config")
180+
print(" ↓")
181+
print(" PUT /store/meshes/my_dag")
182+
print(" Body: config (JSON)")
183+
print()
184+
print(" dag = root.meshes['my_dag']")
185+
print(" ↓")
186+
print(" GET /store/meshes/my_dag")
187+
print()
188+
print(" list(root.meshes.keys())")
189+
print(" ↓")
190+
print(" GET /store/meshes/keys")
191+
print()
192+
print(" del root.meshes['my_dag']")
193+
print(" ↓")
194+
print(" DELETE /store/meshes/my_dag")
195+
print()
196+
197+
print("=" * 70)
198+
print("DOL Browser Example Complete!")
199+
print("=" * 70)
200+
print()
201+
print("Key Benefits of dol:")
202+
print(" ✓ Uniform interface: Everything is a Mapping")
203+
print(" ✓ Automatic serialization: JSON, pickle, etc.")
204+
print(" ✓ Backend agnostic: Files, S3, databases, etc.")
205+
print(" ✓ Easy to extend: Add new stores as needed")
206+
print(" ✓ API-ready: Direct mapping to HTTP endpoints")
207+
print()
208+
209+
210+
if __name__ == "__main__":
211+
main()

examples/ju_schema_example.py

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
"""Example demonstrating ju-based JSON Schema generation.
2+
3+
This example shows how function signatures are automatically converted
4+
to JSON Schemas suitable for React JSON Schema Form (RJSF).
5+
"""
6+
7+
import json
8+
from app_meshed.services.schema_service import func_to_schema, object_to_schema
9+
10+
11+
def process_audio(
12+
audio_data: str,
13+
sample_rate: int = 44100,
14+
gain: float = 1.0,
15+
normalize: bool = True,
16+
) -> dict:
17+
"""Process audio data with configurable parameters.
18+
19+
Args:
20+
audio_data: Path to audio file
21+
sample_rate: Sample rate in Hz
22+
gain: Gain multiplier
23+
normalize: Whether to normalize the output
24+
25+
Returns:
26+
Processed audio metadata
27+
"""
28+
return {
29+
"processed": True,
30+
"sample_rate": sample_rate,
31+
"gain": gain,
32+
"normalized": normalize,
33+
}
34+
35+
36+
def main():
37+
"""Run the ju schema generation example."""
38+
print("=" * 70)
39+
print("JSON Schema Generation Example (ju + i2)")
40+
print("=" * 70)
41+
print()
42+
43+
# Example 1: Generate schema from function signature
44+
print("Example 1: Function to JSON Schema")
45+
print("-" * 70)
46+
print()
47+
48+
schema = func_to_schema(process_audio, title="Audio Processing")
49+
50+
print("Function signature:")
51+
print(f" {process_audio.__name__}{process_audio.__code__.co_varnames[:process_audio.__code__.co_argcount]}")
52+
print()
53+
54+
print("Generated JSON Schema (for RJSF):")
55+
print(json.dumps(schema, indent=2))
56+
print()
57+
58+
# Example 2: Schema from a configuration object
59+
print("=" * 70)
60+
print("Example 2: Object to JSON Schema")
61+
print("-" * 70)
62+
print()
63+
64+
config = {
65+
"server": {
66+
"host": "localhost",
67+
"port": 8000,
68+
"debug": True,
69+
},
70+
"data_path": "/path/to/data",
71+
"max_connections": 100,
72+
}
73+
74+
object_schema = object_to_schema(config, title="Server Configuration")
75+
76+
print("Configuration object:")
77+
print(json.dumps(config, indent=2))
78+
print()
79+
80+
print("Generated JSON Schema:")
81+
print(json.dumps(object_schema, indent=2))
82+
print()
83+
84+
# Example 3: Different parameter types
85+
print("=" * 70)
86+
print("Example 3: Various Parameter Types")
87+
print("-" * 70)
88+
print()
89+
90+
def complex_function(
91+
name: str,
92+
age: int,
93+
height: float,
94+
active: bool,
95+
tags: list,
96+
metadata: dict,
97+
optional_note: str = "default note",
98+
) -> dict:
99+
"""Function with various parameter types."""
100+
return {}
101+
102+
complex_schema = func_to_schema(complex_function, title="Complex Parameters")
103+
104+
print("Function with various types:")
105+
print(json.dumps(complex_schema, indent=2))
106+
print()
107+
108+
# Example 4: How this is used in the API
109+
print("=" * 70)
110+
print("Example 4: API Usage Pattern")
111+
print("-" * 70)
112+
print()
113+
114+
print("In the app_meshed API:")
115+
print(" 1. Frontend requests: GET /schema/function/process_audio")
116+
print(" 2. Backend generates schema using func_to_schema()")
117+
print(" 3. Frontend receives JSON Schema")
118+
print(" 4. RJSF renders a form based on the schema")
119+
print(" 5. User fills form and submits")
120+
print(" 6. Frontend sends validated data to DAG execution endpoint")
121+
print()
122+
123+
print("API Response Example:")
124+
api_response = {
125+
"endpoint": "/schema/function/process_audio",
126+
"method": "GET",
127+
"response": schema,
128+
}
129+
print(json.dumps(api_response, indent=2))
130+
print()
131+
132+
print("=" * 70)
133+
print("Schema Generation Complete!")
134+
print("=" * 70)
135+
print()
136+
print("Key Benefits:")
137+
print(" ✓ No manual form definitions needed")
138+
print(" ✓ Type-safe: schemas match actual function signatures")
139+
print(" ✓ Self-documenting: docstrings included in schemas")
140+
print(" ✓ Automatic validation in the frontend")
141+
print()
142+
143+
144+
if __name__ == "__main__":
145+
main()

0 commit comments

Comments
 (0)