hermescures1 commited on
Commit
948a05a
·
verified ·
1 Parent(s): 0e3d4b8

Upload folder using huggingface_hub

Browse files
tests/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ """Tests for SplitBit LLM."""
tests/test_connectors_vision.py ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Test connectors, image generation, self-refinement, and always-on daemon."""
2
+
3
+ import sys
4
+ import os
5
+ import tempfile
6
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
7
+
8
+ from splitbit_llm.connectors.api_client import RESTClient, APIConfig, ConnectorRegistry
9
+ from splitbit_llm.connectors.services import ServiceManager, HTTPFetcher
10
+ from splitbit_llm.connectors.webhooks import WebhookManager
11
+ from splitbit_llm.vision.image_gen import ImageGenerator
12
+ from splitbit_llm.agents.self_refine import SelfRefinementEngine
13
+
14
+
15
+ def test_rest_client_config():
16
+ """Test REST client configuration."""
17
+ config = APIConfig(name="test", base_url="https://api.example.com", api_key="secret")
18
+ client = RESTClient(config)
19
+ headers = client._build_headers()
20
+ assert "X-API-Key" in headers, f"API key header missing: {headers}"
21
+ assert headers["X-API-Key"] == "secret"
22
+ print(f" Headers: {list(headers.keys())}")
23
+
24
+ # Test bearer auth
25
+ config2 = APIConfig(name="test2", base_url="https://api.example.com", api_key="tok", auth_type="bearer")
26
+ client2 = RESTClient(config2)
27
+ headers2 = client2._build_headers()
28
+ assert headers2["Authorization"] == "Bearer tok"
29
+ print(f" Bearer auth: OK")
30
+
31
+
32
+ def test_connector_registry():
33
+ """Test connector registry."""
34
+ registry = ConnectorRegistry()
35
+ config = APIConfig(name="github", base_url="https://api.github.com", auth_type="none")
36
+ registry.register("github", config)
37
+ assert "github" in registry._connectors
38
+ stats = registry.get_stats()
39
+ assert stats["total_connectors"] == 1
40
+ print(f" Connectors: {stats['total_connectors']}")
41
+
42
+ # Test calling unknown connector
43
+ result = registry.call("unknown", "GET", "/test")
44
+ assert not result.success
45
+ print(f" Unknown connector handled: OK")
46
+
47
+
48
+ def test_service_manager():
49
+ """Test service manager."""
50
+ mgr = ServiceManager()
51
+ assert "http_fetcher" in mgr.list_services()
52
+ print(f" Services: {mgr.list_services()}")
53
+
54
+ # Test calling a service method
55
+ result = mgr.call_service("http_fetcher", "fetch", "http://httpbin.org/status/200")
56
+ print(f" HTTP fetch result type: {type(result).__name__}")
57
+
58
+
59
+ def test_webhooks():
60
+ """Test webhook manager."""
61
+ mgr = WebhookManager()
62
+
63
+ # Register an endpoint (no secret for simple test)
64
+ def handler(body):
65
+ return {"received": body.get("event", "unknown")}
66
+
67
+ mgr.register_endpoint("/test", handler)
68
+ assert "/test" in mgr.list_endpoints()
69
+ print(f" Endpoints: {mgr.list_endpoints()}")
70
+
71
+ # Handle a request
72
+ result = mgr.handle_request("/test", {"event": "test_event"})
73
+ assert result["received"] == "test_event"
74
+ print(f" Webhook handled: {result}")
75
+
76
+ # Test unknown endpoint
77
+ result = mgr.handle_request("/unknown", {})
78
+ assert "error" in result
79
+ print(f" Unknown endpoint: OK")
80
+
81
+
82
+ def test_image_generator():
83
+ """Test image generation."""
84
+ gen = ImageGenerator(default_size=(64, 64))
85
+
86
+ # Test BMP generation
87
+ result = gen.generate("sunset over the ocean", width=64, height=64)
88
+ assert result["width"] == 64
89
+ assert result["height"] == 64
90
+ assert len(result["base64"]) > 0
91
+ assert "sunset" in result["palette"][0].__str__() or True # palette extracted
92
+ print(f" Image: {result['width']}x{result['height']}, pattern={result['pattern']}, size={result['size_bytes']}b")
93
+
94
+ # Test SVG generation
95
+ svg = gen.generate_svg("geometric shapes", width=100, height=100)
96
+ assert "<svg" in svg
97
+ assert "</svg>" in svg
98
+ print(f" SVG: {len(svg)} chars")
99
+
100
+ # Test ASCII art
101
+ ascii_art = gen.generate_ascii("noise texture", width=30, height=10)
102
+ assert len(ascii_art) > 0
103
+ lines = ascii_art.strip().split("\n")
104
+ assert len(lines) == 10
105
+ print(f" ASCII: {len(lines)} lines")
106
+
107
+ # Test palette extraction
108
+ palette = gen._extract_palette("fire and flames")
109
+ assert palette[0][0] > 200 # red dominant
110
+ print(f" Fire palette: {palette[0]}")
111
+
112
+ stats = gen.get_stats()
113
+ assert stats["images_generated"] >= 1
114
+ print(f" Stats: {stats['images_generated']} images generated")
115
+
116
+
117
+ def test_self_refinement():
118
+ """Test self-refinement engine."""
119
+ engine = SelfRefinementEngine(harness=None)
120
+ stats = engine.get_stats()
121
+ assert stats["refinement_cycles"] == 0
122
+ print(f" Initial stats: {stats}")
123
+
124
+ # Test refine_once without harness (should return error gracefully)
125
+ result = engine.refine_once()
126
+ assert "error" in result
127
+ print(f" No harness handled: OK")
128
+
129
+
130
+ def test_image_patterns():
131
+ """Test all image pattern types."""
132
+ gen = ImageGenerator(default_size=(32, 32))
133
+
134
+ for pattern in ["gradient", "radial", "noise", "fractal", "geometric", "waves"]:
135
+ result = gen.generate("test", width=32, height=32, pattern=pattern)
136
+ assert result["pattern"] == pattern
137
+ assert len(result["base64"]) > 0
138
+ print(f" Pattern '{pattern}': OK ({result['size_bytes']}b)")
139
+
140
+
141
+ if __name__ == "__main__":
142
+ print("Running connectors, image gen, and refinement tests...")
143
+ test_rest_client_config()
144
+ print(" ✓ test_rest_client_config")
145
+ test_connector_registry()
146
+ print(" ✓ test_connector_registry")
147
+ test_service_manager()
148
+ print(" ✓ test_service_manager")
149
+ test_webhooks()
150
+ print(" ✓ test_webhooks")
151
+ test_image_generator()
152
+ print(" ✓ test_image_generator")
153
+ test_self_refinement()
154
+ print(" ✓ test_self_refinement")
155
+ test_image_patterns()
156
+ print(" ✓ test_image_patterns")
157
+ print("\nAll connectors & vision tests passed!")
tests/test_conversation_mesh.py ADDED
@@ -0,0 +1,278 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Test multi-LLM conversation mesh, skill building pools, and auto category adder."""
2
+
3
+ import sys
4
+ import os
5
+ import tempfile
6
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
7
+
8
+ from splitbit_llm.agents.conversation_mesh import (
9
+ ConversationMesh, AutoCategoryManager, SkillPool, ConversationMessage,
10
+ )
11
+
12
+
13
+ def test_auto_category_manager():
14
+ """Test auto skill category adder — discovers new categories dynamically."""
15
+ mgr = AutoCategoryManager()
16
+
17
+ # Should start with seed categories
18
+ cats = mgr.get_categories()
19
+ assert "conversation" in cats
20
+ assert "code" in cats
21
+ assert "speed" in cats
22
+ print(f" Seed categories: {len(cats)} — {cats[:5]}...")
23
+
24
+ # Discover categories from a topic
25
+ discovered = mgr.discover_from_topic("How to improve database query optimization")
26
+ assert len(discovered) > 0
27
+ print(f" Discovered from 'database query optimization': {discovered}")
28
+
29
+ # Should have auto-added new categories
30
+ auto_cats = mgr.get_auto_categories()
31
+ assert len(auto_cats) > 0, f"Expected auto categories, got: {auto_cats}"
32
+ print(f" Auto-added categories: {auto_cats}")
33
+
34
+ # Discover from another topic
35
+ discovered2 = mgr.discover_from_topic("Building better network security protocols")
36
+ print(f" Discovered from 'network security protocols': {discovered2}")
37
+
38
+ # Stats
39
+ stats = mgr.get_stats()
40
+ assert stats["categories_total"] > len(mgr.SEED_CATEGORIES)
41
+ assert stats["categories_auto_added"] > 0
42
+ print(f" Stats: {stats['categories_total']} total, {stats['categories_auto_added']} auto-added")
43
+
44
+
45
+ def test_category_matching():
46
+ """Test that similar keywords map to existing categories."""
47
+ mgr = AutoCategoryManager()
48
+
49
+ # First discovery creates the category
50
+ mgr.discover_from_topic("optimization techniques")
51
+ assert "optimization" in mgr.get_categories()
52
+
53
+ # Similar keyword should map to existing category
54
+ mgr.discover_from_topic("optimize performance")
55
+ # "optimize" should map to "optimization" via prefix matching
56
+ cats = mgr.get_categories()
57
+ print(f" Categories after 'optimize': {cats}")
58
+
59
+ stats = mgr.get_stats()
60
+ print(f" Keywords indexed: {stats['keywords_indexed']}")
61
+
62
+
63
+ def test_conversation_mesh():
64
+ """Test multi-LLM conversation mesh — agents converse to build skills."""
65
+ mesh = ConversationMesh(harness=None)
66
+
67
+ # Run a round-robin conversation
68
+ result = mesh.run_conversation(mode="round_robin", topic="How to improve code quality")
69
+ assert result["mode"] == "round_robin"
70
+ assert result["topic"] == "How to improve code quality"
71
+ assert result["messages"] > 0
72
+ assert len(result["categories"]) > 0
73
+ print(f" Round-robin: {result['messages']} messages, categories: {result['categories']}")
74
+
75
+ # Run a brainstorm
76
+ result2 = mesh.run_conversation(mode="brainstorm", topic="Efficient algorithms for pattern matching")
77
+ assert result2["mode"] == "brainstorm"
78
+ assert result2["messages"] > 0
79
+ print(f" Brainstorm: {result2['messages']} messages, categories: {result2['categories']}")
80
+
81
+ # Run a debate
82
+ result3 = mesh.run_conversation(mode="debate", topic="Best approaches to data compression")
83
+ assert result3["mode"] == "debate"
84
+ print(f" Debate: {result3['messages']} messages")
85
+
86
+ # Run a teaching session
87
+ result4 = mesh.run_conversation(mode="teaching", topic="Methods for adaptive learning")
88
+ assert result4["mode"] == "teaching"
89
+ print(f" Teaching: {result4['messages']} messages")
90
+
91
+ # Run a pairwise discussion
92
+ result5 = mesh.run_conversation(mode="pairwise", topic="Strategies for error handling")
93
+ assert result5["mode"] == "pairwise"
94
+ print(f" Pairwise: {result5['messages']} messages")
95
+
96
+
97
+ def test_skill_building_pools():
98
+ """Test skill building pools — collaborative skills from multiple agents."""
99
+ mesh = ConversationMesh(harness=None)
100
+
101
+ # Run multiple conversations to build pools
102
+ for i in range(5):
103
+ mesh.run_conversation(topic=f"Optimizing memory usage in system {i}")
104
+
105
+ pools = mesh.get_skill_pools()
106
+ assert len(pools) > 0, f"Expected skill pools, got {len(pools)}"
107
+ print(f" Skill pools created: {len(pools)}")
108
+
109
+ # Check pool structure
110
+ pool = pools[0]
111
+ assert "id" in pool
112
+ assert "name" in pool
113
+ assert "category" in pool
114
+ assert "contributors" in pool
115
+ assert "confidence" in pool
116
+ print(f" Pool: {pool['name']} (category: {pool['category']}, contributors: {pool['contributors']})")
117
+
118
+ # Stats
119
+ stats = mesh.get_stats()
120
+ assert stats["skills_pooled"] > 0
121
+ assert stats["cross_agent_skills"] > 0, "Expected cross-agent skills"
122
+ print(f" Stats: {stats['skills_pooled']} pooled, {stats['cross_agent_skills']} cross-agent")
123
+
124
+
125
+ def test_mesh_auto_categories():
126
+ """Test that mesh conversations auto-discover new categories."""
127
+ mesh = ConversationMesh(harness=None)
128
+
129
+ # Run conversations on diverse topics
130
+ topics = [
131
+ "Improving database query performance",
132
+ "Building neural network architectures",
133
+ "Optimizing cache invalidation strategies",
134
+ "Enhancing cryptographic security measures",
135
+ "Implementing blockchain consensus algorithms",
136
+ ]
137
+
138
+ all_cats = set()
139
+ for topic in topics:
140
+ result = mesh.run_conversation(topic=topic)
141
+ all_cats.update(result["categories"])
142
+
143
+ cats = mesh.get_categories()
144
+ auto_cats = mesh.get_auto_categories()
145
+
146
+ assert len(auto_cats) > 0, f"Expected auto categories: {auto_cats}"
147
+ print(f" Total categories: {len(cats)}")
148
+ print(f" Auto-discovered: {auto_cats}")
149
+
150
+ # Verify some expected categories were discovered
151
+ # (at least some of: database, neural, cache, cryptographic, blockchain)
152
+ discovered_lower = [c.lower() for c in auto_cats]
153
+ print(f" Discovered categories: {discovered_lower}")
154
+
155
+
156
+ def test_mesh_stats():
157
+ """Test mesh stats tracking."""
158
+ mesh = ConversationMesh(harness=None)
159
+
160
+ # Run a few conversations
161
+ mesh.run_conversation(topic="Testing mesh statistics tracking")
162
+ mesh.run_conversation(topic="Another topic for skill building")
163
+
164
+ stats = mesh.get_stats()
165
+ assert stats["conversations_total"] >= 2
166
+ assert stats["messages_exchanged"] > 0
167
+ cat_stats = stats["categories"]
168
+ assert cat_stats["categories_total"] > 0
169
+ print(f" Conversations: {stats['conversations_total']}")
170
+ print(f" Messages: {stats['messages_exchanged']}")
171
+ print(f" Skills pooled: {stats['skills_pooled']}")
172
+ print(f" Categories: {cat_stats['categories_total']}")
173
+
174
+
175
+ def test_random_mode_selection():
176
+ """Test that random mode selection works."""
177
+ mesh = ConversationMesh(harness=None)
178
+
179
+ modes_used = set()
180
+ for _ in range(10):
181
+ result = mesh.run_conversation() # no mode specified → random
182
+ modes_used.add(result["mode"])
183
+
184
+ # Should have used at least 2 different modes in 10 random runs
185
+ assert len(modes_used) >= 2, f"Expected variety of modes, got: {modes_used}"
186
+ print(f" Modes used: {modes_used}")
187
+
188
+
189
+ def test_skill_cascade():
190
+ """Test that building a skill pool cascades into building related skill pools.
191
+
192
+ When a skill pool is created on topic X, the system should automatically
193
+ generate related skill pools on similar topics (advanced techniques,
194
+ best practices, pitfalls, testing, integration, etc.)
195
+ """
196
+ mesh = ConversationMesh(harness=None)
197
+
198
+ # Run a single conversation — should cascade into multiple related ones
199
+ result = mesh.run_conversation(topic="Optimizing database query performance")
200
+
201
+ # The original conversation should have cascaded
202
+ cascaded = result.get("cascaded", [])
203
+ assert len(cascaded) > 0, f"Expected cascade results, got: {cascaded}"
204
+ print(f" Original topic: 'Optimizing database query performance'")
205
+ print(f" Cascaded into {len(cascaded)} related conversations:")
206
+ for c in cascaded:
207
+ print(f" → {c['topic'][:60]} (mode: {c['mode']}, pool: {c['pool_id'] is not None})")
208
+
209
+ # Should have created multiple skill pools (original + cascaded)
210
+ pools = mesh.get_skill_pools()
211
+ assert len(pools) > 1, f"Expected multiple pools from cascade, got {len(pools)}"
212
+ print(f" Total skill pools: {len(pools)}")
213
+
214
+ # Stats should show cascade activity
215
+ stats = mesh.get_stats()
216
+ assert stats["cascade_pools_created"] > 0, "Expected cascade pools created"
217
+ assert stats["cascade_conversations"] > 0, "Expected cascade conversations"
218
+ print(f" Cascade stats: {stats['cascade_pools_created']} pools, "
219
+ f"{stats['cascade_conversations']} conversations, depth {stats['cascade_depth']}")
220
+
221
+
222
+ def test_cascade_related_topics():
223
+ """Test that related topics are generated correctly from categories."""
224
+ mesh = ConversationMesh(harness=None)
225
+
226
+ # Generate related topics from a topic and categories
227
+ related = mesh._generate_related_topics(
228
+ "Optimizing database performance",
229
+ ["database", "performance", "optimization"]
230
+ )
231
+
232
+ assert len(related) > 0, "Expected related topics"
233
+ assert len(related) <= 5, f"Should limit to 5, got {len(related)}"
234
+ print(f" Related topics for 'database, performance, optimization':")
235
+ for t in related:
236
+ print(f" → {t}")
237
+
238
+ # Should include category-based variations (first category always included)
239
+ assert any("database" in t.lower() for t in related)
240
+
241
+
242
+ def test_cascade_depth_limit():
243
+ """Test that cascade depth is limited to prevent infinite recursion."""
244
+ mesh = ConversationMesh(harness=None)
245
+
246
+ # Run a conversation — cascade should be limited to depth 3
247
+ result = mesh.run_conversation(topic="Building neural network architectures")
248
+
249
+ stats = mesh.get_stats()
250
+ assert stats["cascade_depth"] <= 2, f"Cascade depth should be <= 2, got {stats['cascade_depth']}"
251
+ print(f" Cascade depth: {stats['cascade_depth']} (max 2)")
252
+ print(f" Total pools: {stats['skill_pools']}")
253
+ print(f" Total conversations: {stats['conversations_total']}")
254
+
255
+
256
+ if __name__ == "__main__":
257
+ print("Running conversation mesh, skill pools, and auto category tests...")
258
+ test_auto_category_manager()
259
+ print(" ✓ test_auto_category_manager")
260
+ test_category_matching()
261
+ print(" ✓ test_category_matching")
262
+ test_conversation_mesh()
263
+ print(" ✓ test_conversation_mesh")
264
+ test_skill_building_pools()
265
+ print(" ✓ test_skill_building_pools")
266
+ test_mesh_auto_categories()
267
+ print(" ✓ test_mesh_auto_categories")
268
+ test_mesh_stats()
269
+ print(" ✓ test_mesh_stats")
270
+ test_random_mode_selection()
271
+ print(" ✓ test_random_mode_selection")
272
+ test_skill_cascade()
273
+ print(" ✓ test_skill_cascade")
274
+ test_cascade_related_topics()
275
+ print(" ✓ test_cascade_related_topics")
276
+ test_cascade_depth_limit()
277
+ print(" ✓ test_cascade_depth_limit")
278
+ print("\nAll conversation mesh tests passed!")
tests/test_fast_reply_identity.py ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Test fast reply cache, first-run naming, and 100-project mode."""
2
+
3
+ import sys
4
+ import os
5
+ import tempfile
6
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
7
+
8
+ from splitbit_llm.memory.fast_cache import FastReplyCache
9
+ from splitbit_llm.identity import FirstRunManager
10
+
11
+
12
+ def test_fast_reply_cache():
13
+ """Test fast reply cache — store, lookup, cache hit."""
14
+ with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
15
+ db_path = f.name
16
+
17
+ cache = FastReplyCache(db_path=db_path)
18
+
19
+ # Store a response
20
+ cache.store("What is Python?", "Python is a programming language.", confidence=0.96, response_time_s=0.5)
21
+
22
+ # Exact match lookup
23
+ result = cache.lookup("What is Python?")
24
+ assert result is not None, "Expected cache hit"
25
+ assert result["cache_hit"] is True
26
+ assert result["cache_type"] == "exact"
27
+ assert "Python is a programming language" in result["response"]
28
+ print(f" Exact hit: {result['cache_type']} (confidence: {result['confidence']:.2f})")
29
+
30
+ # Miss for different query
31
+ result2 = cache.lookup("What is JavaScript?")
32
+ assert result2 is None, "Expected cache miss"
33
+ print(f" Miss for different query: OK")
34
+
35
+ # Store more entries
36
+ cache.store("What is JavaScript?", "JavaScript is a web programming language.", confidence=0.95)
37
+ cache.store("How do I code?", "Write code in a text editor and run it.", confidence=0.90)
38
+ cache.store("What is AI?", "AI is artificial intelligence.", confidence=0.97)
39
+
40
+ # Semantic match (similar words)
41
+ result3 = cache.lookup("What is Python programming?")
42
+ # May or may not hit depending on threshold, but should not crash
43
+ print(f" Semantic lookup: {'hit' if result3 else 'miss'} (OK)")
44
+
45
+ # Stats
46
+ stats = cache.get_stats()
47
+ assert stats["entries_stored"] >= 4
48
+ assert stats["total_lookups"] >= 3
49
+ print(f" Stats: {stats['entries_stored']} entries, hit rate: {stats['hit_rate']:.1%}")
50
+
51
+ # Persistence
52
+ cache2 = FastReplyCache(db_path=db_path)
53
+ stats2 = cache2.get_stats()
54
+ assert stats2["entries_stored"] >= 4, f"Cache not persisted: {stats2}"
55
+ print(f" Persisted: {stats2['entries_stored']} entries")
56
+
57
+
58
+ def test_fast_cache_normalization():
59
+ """Test query normalization for cache."""
60
+ with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
61
+ db_path = f.name
62
+
63
+ cache = FastReplyCache(db_path=db_path)
64
+ cache.store("What is Python?", "Answer", confidence=0.96)
65
+
66
+ # Normalized version should also hit
67
+ result = cache.lookup("what is python?")
68
+ assert result is not None, "Case difference should still hit"
69
+ print(f" Case-insensitive hit: OK")
70
+
71
+ result2 = cache.lookup("What is Python?")
72
+ assert result2 is not None, "Extra spaces should still hit"
73
+ print(f" Whitespace-normalized hit: OK")
74
+
75
+ result3 = cache.lookup("What is Python!")
76
+ assert result3 is not None, "Punctuation difference should still hit"
77
+ print(f" Punctuation-normalized hit: OK")
78
+
79
+
80
+ def test_first_run_naming():
81
+ """Test first-run naming flow."""
82
+ with tempfile.TemporaryDirectory() as tmpdir:
83
+ # First run — no identity file
84
+ identity = FirstRunManager(data_dir=tmpdir)
85
+ assert identity.is_first_run() is True
86
+ greeting = identity.get_greeting()
87
+ assert "Incentives Inc." in greeting
88
+ assert "name me" in greeting.lower()
89
+ print(f" First run greeting: {greeting}")
90
+
91
+ # Set name
92
+ identity.set_name("Jarvis")
93
+ assert identity.is_first_run() is False
94
+ assert identity.get_name() == "Jarvis"
95
+ print(f" Named: {identity.get_name()}")
96
+
97
+ # System prompt suffix
98
+ suffix = identity.get_system_prompt_suffix()
99
+ assert "Jarvis" in suffix
100
+ assert "Incentives Inc." in suffix
101
+ print(f" System prompt suffix: {suffix.strip()}")
102
+
103
+ # Welcome back greeting
104
+ greeting2 = identity.get_greeting()
105
+ assert "Jarvis" in greeting2
106
+ print(f" Welcome back: {greeting2}")
107
+
108
+ # Persistence — load again
109
+ identity2 = FirstRunManager(data_dir=tmpdir)
110
+ assert identity2.is_first_run() is False
111
+ assert identity2.get_name() == "Jarvis"
112
+ print(f" Persisted name: {identity2.get_name()}")
113
+
114
+
115
+ def test_first_run_no_name():
116
+ """Test first-run when user doesn't provide a name."""
117
+ with tempfile.TemporaryDirectory() as tmpdir:
118
+ identity = FirstRunManager(data_dir=tmpdir)
119
+ assert identity.is_first_run()
120
+
121
+ # Default name when not set
122
+ assert identity.get_name() == "Incentives Inc. LLM"
123
+ print(f" Default name: {identity.get_name()}")
124
+
125
+ # System prompt without name
126
+ suffix = identity.get_system_prompt_suffix()
127
+ assert "Incentives Inc." in suffix
128
+ assert "Your name is" not in suffix
129
+ print(f" Default suffix: {suffix.strip()}")
130
+
131
+
132
+ def test_cache_hit_rate():
133
+ """Test cache hit rate calculation."""
134
+ with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
135
+ db_path = f.name
136
+
137
+ cache = FastReplyCache(db_path=db_path)
138
+
139
+ # Store some entries
140
+ cache.store("Q1", "A1", confidence=0.96)
141
+ cache.store("Q2", "A2", confidence=0.96)
142
+ cache.store("Q3", "A3", confidence=0.96)
143
+
144
+ # 3 hits, 2 misses = 60% hit rate
145
+ cache.lookup("Q1") # hit
146
+ cache.lookup("Q2") # hit
147
+ cache.lookup("Q3") # hit
148
+ cache.lookup("Q4") # miss
149
+ cache.lookup("Q5") # miss
150
+
151
+ rate = cache.get_hit_rate()
152
+ assert 0.5 < rate < 0.7, f"Hit rate should be ~0.6, got {rate}"
153
+ print(f" Hit rate: {rate:.1%} (5 lookups, 3 hits)")
154
+
155
+ # Not fast ready yet (too few entries)
156
+ assert cache.is_fast_ready() is False
157
+ print(f" Fast ready: {cache.is_fast_ready()} (needs 50+ entries)")
158
+
159
+
160
+ if __name__ == "__main__":
161
+ print("Running fast cache, identity, and 100-project tests...")
162
+ test_fast_reply_cache()
163
+ print(" ✓ test_fast_reply_cache")
164
+ test_fast_cache_normalization()
165
+ print(" ✓ test_fast_cache_normalization")
166
+ test_first_run_naming()
167
+ print(" ✓ test_first_run_naming")
168
+ test_first_run_no_name()
169
+ print(" ✓ test_first_run_no_name")
170
+ test_cache_hit_rate()
171
+ print(" ✓ test_cache_hit_rate")
172
+ print("\nAll fast reply & identity tests passed!")
tests/test_harness.py ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Test harness: chat, tools, skills, stats."""
2
+
3
+ import sys
4
+ import os
5
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
6
+
7
+ from splitbit_llm.harness.harness import SplitBitHarness
8
+ from splitbit_llm.harness.tools import ToolRegistry, get_default_tools, tool_loop, parse_tool_calls
9
+
10
+
11
+ def test_harness_chat():
12
+ """Test harness chat functionality."""
13
+ harness = SplitBitHarness()
14
+ result = harness.chat("Hello there", channel="cli")
15
+
16
+ assert "response" in result, "No response in result"
17
+ assert "elapsed_s" in result, "No elapsed time"
18
+ assert "stats" in result, "No stats"
19
+ print(f" Response: {result['response'][:60]}")
20
+ print(f" Elapsed: {result['elapsed_s']}s")
21
+
22
+
23
+ def test_harness_stats():
24
+ """Test harness stats aggregation."""
25
+ harness = SplitBitHarness()
26
+ stats = harness.get_stats()
27
+
28
+ assert "model" in stats, "No model stats"
29
+ assert "skills" in stats, "No skills stats"
30
+ assert "recursive_links" in stats, "No link stats"
31
+ assert "auto_sizer" in stats, "No auto_sizer stats"
32
+ print(f" Model params: {stats['model']['param_count']:,}")
33
+ print(f" Tier: {stats['auto_sizer']['tier']}")
34
+
35
+
36
+ def test_tool_registry():
37
+ """Test tool registry and execution."""
38
+ registry = ToolRegistry()
39
+ for tool in get_default_tools():
40
+ registry.register(tool)
41
+
42
+ assert len(registry.list_tools()) == 9, f"Wrong tool count: {len(registry.list_tools())}"
43
+ print(f" Tools: {len(registry.list_tools())}")
44
+
45
+ # Test calculate tool
46
+ result = registry.execute("calculate", "2 + 2")
47
+ assert result.success, f"Calculate failed: {result.error}"
48
+ assert "4" in result.output, f"Wrong result: {result.output}"
49
+ print(f" calculate(2+2) = {result.output}")
50
+
51
+
52
+ def test_tool_loop():
53
+ """Test tool execution loop."""
54
+ registry = ToolRegistry()
55
+ for tool in get_default_tools():
56
+ registry.register(tool)
57
+
58
+ text = "Let me calculate: [TOOL: calculate(3 * 7)]"
59
+ final_text, results = tool_loop(text, registry)
60
+
61
+ assert len(results) == 1, f"Expected 1 result, got {len(results)}"
62
+ assert results[0].success, "Tool execution failed"
63
+ assert "21" in results[0].output, f"Wrong output: {results[0].output}"
64
+ print(f" Tool loop: {len(results)} calls, output: {results[0].output}")
65
+
66
+
67
+ def test_parse_tool_calls():
68
+ """Test parsing tool calls from text."""
69
+ text = "I will [TOOL: calculate(1 + 1)] and then [TOOL: calculate(2 + 2)]"
70
+ calls = parse_tool_calls(text)
71
+ assert len(calls) == 2, f"Expected 2 calls, got {len(calls)}"
72
+ assert calls[0][0] == "calculate", f"Wrong tool name: {calls[0][0]}"
73
+ print(f" Parsed {len(calls)} tool calls")
74
+
75
+
76
+ if __name__ == "__main__":
77
+ print("Running harness tests...")
78
+ test_harness_chat()
79
+ print(" ✓ test_harness_chat")
80
+ test_harness_stats()
81
+ print(" ✓ test_harness_stats")
82
+ test_tool_registry()
83
+ print(" ✓ test_tool_registry")
84
+ test_tool_loop()
85
+ print(" ✓ test_tool_loop")
86
+ test_parse_tool_calls()
87
+ print(" ✓ test_parse_tool_calls")
88
+ print("\nAll harness tests passed!")
tests/test_memory_agents.py ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Test persistent memory, goal memory, and agents."""
2
+
3
+ import sys
4
+ import os
5
+ import tempfile
6
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
7
+
8
+ from splitbit_llm.memory.persistent import PersistentMemory
9
+ from splitbit_llm.memory.goal_memory import GoalMemory, Goal
10
+ from splitbit_llm.agents.agent_manager import AgentManager
11
+ from splitbit_llm.agents.planner_agent import PlannerAgent
12
+ from splitbit_llm.agents.coder_agent import CoderAgent
13
+ from splitbit_llm.agents.researcher_agent import ResearcherAgent
14
+ from splitbit_llm.agents.reviewer_agent import ReviewerAgent
15
+ from splitbit_llm.agents.executor_agent import ExecutorAgent
16
+
17
+
18
+ def test_persistent_memory():
19
+ """Test persistent memory storage and recall."""
20
+ with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
21
+ db_path = f.name
22
+
23
+ mem = PersistentMemory(db_path=db_path)
24
+ mem.set_session("test-session")
25
+
26
+ # Store episodic
27
+ mem.add_episodic("user", "What is Python?", importance=0.7)
28
+ mem.add_episodic("assistant", "Python is a programming language.", importance=0.8)
29
+
30
+ # Store semantic
31
+ mem.add_semantic("Python is an interpreted programming language", confidence=0.9)
32
+
33
+ # Recall
34
+ episodic = mem.recall_episodic("Python", max_results=3)
35
+ assert len(episodic) >= 1, "No episodic memories recalled"
36
+ print(f" Episodic recalled: {len(episodic)}")
37
+
38
+ semantic = mem.recall_semantic("Python", max_results=3)
39
+ assert len(semantic) >= 1, "No semantic memories recalled"
40
+ print(f" Semantic recalled: {len(semantic)}")
41
+
42
+ # Context injection
43
+ context = mem.get_context("Python programming")
44
+ assert "Python" in context or "programming" in context, f"Context empty: {context}"
45
+ print(f" Context: {context[:80]}")
46
+
47
+ # Persistence test
48
+ mem2 = PersistentMemory(db_path=db_path)
49
+ stats = mem2.get_stats()
50
+ assert stats["episodic_total"] >= 2, f"Episodic not persisted: {stats}"
51
+ assert stats["semantic_total"] >= 1, f"Semantic not persisted: {stats}"
52
+ print(f" Persisted: {stats['episodic_total']} episodic, {stats['semantic_total']} semantic")
53
+
54
+
55
+ def test_goal_memory():
56
+ """Test goal creation, planning, and execution."""
57
+ with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
58
+ db_path = f.name
59
+
60
+ gm = GoalMemory(db_path=db_path)
61
+
62
+ # Create goal
63
+ goal = gm.create_goal("Build a web app", "Create a simple Flask web application", priority="high")
64
+ assert goal.status == "pending"
65
+ print(f" Created goal: {goal.title} ({goal.id[:8]})")
66
+
67
+ # Plan goal
68
+ result = gm.plan_goal(goal.id, steps=[
69
+ {"title": "Setup", "description": "Create project structure"},
70
+ {"title": "Code", "description": "Write the Flask app"},
71
+ {"title": "Test", "description": "Test the application"},
72
+ ])
73
+ assert result["success"]
74
+ goal = gm.get_goal(goal.id)
75
+ assert goal.status == "in_progress"
76
+ assert len(goal.steps) == 3
77
+ print(f" Planned: {len(goal.steps)} steps, status: {goal.status}")
78
+
79
+ # Execute steps
80
+ r1 = gm.execute_step(goal.id, "Project structure created", success=True)
81
+ assert r1["success"]
82
+ r2 = gm.execute_step(goal.id, "Flask app written", success=True)
83
+ assert r2["success"]
84
+ r3 = gm.execute_step(goal.id, "Tests passed", success=True)
85
+ assert r3["success"]
86
+
87
+ goal = gm.get_goal(goal.id)
88
+ assert goal.status == "completed"
89
+ assert goal.progress() == 1.0
90
+ print(f" Completed: {goal.progress():.0%}")
91
+
92
+ # Persistence
93
+ gm2 = GoalMemory(db_path=db_path)
94
+ stats = gm2.get_stats()
95
+ assert stats["total"] == 1
96
+ assert stats["completed"] == 1
97
+ print(f" Persisted: {stats}")
98
+
99
+
100
+ def test_agent_manager():
101
+ """Test agent manager with 5 agents."""
102
+ with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
103
+ goal_db = f.name
104
+ with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
105
+ mem_db = f.name
106
+
107
+ gm = GoalMemory(db_path=goal_db)
108
+ pm = PersistentMemory(db_path=mem_db)
109
+
110
+ # Mock generate function
111
+ def mock_generate(prompt):
112
+ if "planning" in prompt.lower():
113
+ return '{"steps": [{"title": "Step 1", "description": "Do thing 1"}], "sub_goals": []}'
114
+ return "Mock response for: " + prompt[:50]
115
+
116
+ manager = AgentManager(gm, pm, generate_fn=mock_generate)
117
+ assert len(manager.agents) == 5
118
+ print(f" Agents: {list(manager.agents.keys())}")
119
+
120
+ # Create a project
121
+ goal = manager.create_project("Test project", "A test project for validation", priority="high")
122
+ print(f" Created project: {goal.id[:8]}")
123
+
124
+ # Get status
125
+ status = manager.get_agent_status()
126
+ assert len(status) == 5
127
+ for name, info in status.items():
128
+ assert info["name"] == name
129
+ assert info["running"] == False # not started yet
130
+ print(f" All 5 agents present: {list(status.keys())}")
131
+
132
+ # Get stats
133
+ stats = manager.get_stats()
134
+ assert "agents" in stats
135
+ assert "goals" in stats
136
+ print(f" Stats: {stats['goals']}")
137
+
138
+
139
+
140
+ def test_agent_specialization():
141
+ """Test that agents have correct specializations."""
142
+ with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
143
+ db_path = f.name
144
+
145
+ gm = GoalMemory(db_path=db_path)
146
+
147
+ planner = PlannerAgent(gm)
148
+ coder = CoderAgent(gm)
149
+ researcher = ResearcherAgent(gm)
150
+ reviewer = ReviewerAgent(gm)
151
+ executor = ExecutorAgent(gm)
152
+
153
+ # Test coder can handle code goals
154
+ code_goal = gm.create_goal("Write code", "Implement a Python function to sort data")
155
+ assert coder._can_handle(code_goal), "Coder should handle code goals"
156
+ assert not researcher._can_handle(code_goal), "Researcher should not handle code goals"
157
+ print(" Coder handles code goals: OK")
158
+
159
+ # Test researcher can handle research goals
160
+ research_goal = gm.create_goal("Research topic", "Research the best sorting algorithms")
161
+ assert researcher._can_handle(research_goal), "Researcher should handle research goals"
162
+ print(" Researcher handles research goals: OK")
163
+
164
+ # Test executor can handle execution goals
165
+ exec_goal = gm.create_goal("Run tests", "Execute the test suite and deploy")
166
+ assert executor._can_handle(exec_goal), "Executor should handle execution goals"
167
+ print(" Executor handles execution goals: OK")
168
+
169
+
170
+
171
+ if __name__ == "__main__":
172
+ print("Running memory & agents tests...")
173
+ test_persistent_memory()
174
+ print(" ✓ test_persistent_memory")
175
+ test_goal_memory()
176
+ print(" ✓ test_goal_memory")
177
+ test_agent_manager()
178
+ print(" ✓ test_agent_manager")
179
+ test_agent_specialization()
180
+ print(" ✓ test_agent_specialization")
181
+ print("\nAll memory & agents tests passed!")
tests/test_model.py ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Test model: forward pass, generation, save/load."""
2
+
3
+ import sys
4
+ import os
5
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
6
+
7
+ import numpy as np
8
+ from splitbit_llm.config import get_model_config, HardwareTier
9
+ from splitbit_llm.model.model import SplitBitLLM
10
+ from splitbit_llm.model.tokenizer import BPETokenizer
11
+
12
+
13
+ def test_model_forward():
14
+ """Test model forward pass."""
15
+ cfg = get_model_config(HardwareTier.MOBILE)
16
+ cfg.vocab_size = 256
17
+ model = SplitBitLLM(config=cfg)
18
+
19
+ token_ids = np.array([[1, 5, 10, 15, 20]], dtype=np.int64)
20
+ logits = model.forward(token_ids)
21
+
22
+ assert logits.shape == (1, 5, 256), f"Wrong shape: {logits.shape}"
23
+ print(f" Logits shape: {logits.shape}")
24
+ print(f" Param count: {model.param_count:,}")
25
+
26
+
27
+ def test_model_generate():
28
+ """Test text generation."""
29
+ cfg = get_model_config(HardwareTier.MOBILE)
30
+ cfg.vocab_size = 256
31
+ model = SplitBitLLM(config=cfg)
32
+
33
+ output = model.generate("Hello", max_tokens=10, temperature=0.7)
34
+ assert isinstance(output, str), f"Expected str, got {type(output)}"
35
+ assert len(output) > 0, "Empty output"
36
+ print(f" Generated: {repr(output[:50])}")
37
+
38
+
39
+ def test_model_generate_stream():
40
+ """Test streaming generation."""
41
+ cfg = get_model_config(HardwareTier.MOBILE)
42
+ cfg.vocab_size = 256
43
+ model = SplitBitLLM(config=cfg)
44
+
45
+ chunks = list(model.generate_stream("Hello", max_tokens=10, temperature=0.7))
46
+ assert len(chunks) > 0, "No chunks generated"
47
+ print(f" Chunks: {len(chunks)}")
48
+
49
+
50
+ def test_model_with_tokenizer():
51
+ """Test model with trained tokenizer."""
52
+ tok = BPETokenizer(vocab_size=256)
53
+ tok.train("Hello world! This is a test. Hello world again. The quick brown fox jumps.")
54
+
55
+ cfg = get_model_config(HardwareTier.MOBILE)
56
+ cfg.vocab_size = 256
57
+ model = SplitBitLLM(config=cfg, tokenizer=tok)
58
+
59
+ output = model.generate("Hello", max_tokens=10, temperature=0.7)
60
+ assert isinstance(output, str)
61
+ print(f" Generated with tokenizer: {repr(output[:50])}")
62
+
63
+
64
+ def test_model_truncation():
65
+ """Test that long inputs are truncated to max_seq_len."""
66
+ cfg = get_model_config(HardwareTier.MOBILE)
67
+ cfg.vocab_size = 256
68
+ cfg.max_seq_len = 32
69
+ model = SplitBitLLM(config=cfg)
70
+
71
+ # Input longer than max_seq_len
72
+ long_input = np.array([[i for i in range(100)]], dtype=np.int64)
73
+ logits = model.forward(long_input)
74
+ assert logits.shape[1] == 32, f"Should truncate to 32, got {logits.shape[1]}"
75
+ print(f" Truncated to: {logits.shape[1]}")
76
+
77
+
78
+ if __name__ == "__main__":
79
+ print("Running model tests...")
80
+ test_model_forward()
81
+ print(" ✓ test_model_forward")
82
+ test_model_generate()
83
+ print(" ✓ test_model_generate")
84
+ test_model_generate_stream()
85
+ print(" ✓ test_model_generate_stream")
86
+ test_model_with_tokenizer()
87
+ print(" ✓ test_model_with_tokenizer")
88
+ test_model_truncation()
89
+ print(" ✓ test_model_truncation")
90
+ print("\nAll model tests passed!")
tests/test_quantization.py ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Test SplitBit quantization: ternary, 4-bit, 8-bit, round-trip accuracy."""
2
+
3
+ import sys
4
+ import os
5
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
6
+
7
+ import numpy as np
8
+ from splitbit_llm.model.quantization import SplitBitQuantizer, bits_per_weight, compression_ratio
9
+
10
+
11
+ def test_ternary_quantization():
12
+ """Test ternary quantization round-trip."""
13
+ quantizer = SplitBitQuantizer(format="ternary")
14
+ weights = np.random.randn(64, 32).astype(np.float32) * 0.1
15
+
16
+ packed = quantizer.quantize(weights)
17
+ dequant = quantizer.dequantize(packed)
18
+
19
+ # Ternary loses precision but preserves sign and approximate magnitude
20
+ correlation = np.corrcoef(weights.flatten(), dequant.flatten())[0, 1]
21
+ assert correlation > 0.5, f"Ternary correlation too low: {correlation}"
22
+ print(f" Ternary correlation: {correlation:.3f}")
23
+ print(f" BPW: {quantizer.bpw:.3f}, Compression: {compression_ratio('ternary'):.1f}x")
24
+
25
+
26
+ def test_4bit_quantization():
27
+ """Test 4-bit quantization round-trip."""
28
+ quantizer = SplitBitQuantizer(format="q4_k_m")
29
+ weights = np.random.randn(128, 64).astype(np.float32) * 0.1
30
+
31
+ packed = quantizer.quantize(weights)
32
+ dequant = quantizer.dequantize(packed)
33
+
34
+ # 4-bit should be closer to original
35
+ max_error = np.max(np.abs(weights - dequant))
36
+ rel_error = max_error / np.max(np.abs(weights))
37
+ assert rel_error < 0.2, f"4-bit relative error too high: {rel_error}"
38
+ print(f" 4-bit max relative error: {rel_error:.4f}")
39
+ print(f" BPW: {quantizer.bpw:.1f}, Compression: {compression_ratio('q4_k_m'):.1f}x")
40
+
41
+
42
+ def test_8bit_quantization():
43
+ """Test 8-bit quantization round-trip."""
44
+ quantizer = SplitBitQuantizer(format="q8_0")
45
+ weights = np.random.randn(256, 128).astype(np.float32) * 0.1
46
+
47
+ packed = quantizer.quantize(weights)
48
+ dequant = quantizer.dequantize(packed)
49
+
50
+ # 8-bit should be very close
51
+ max_error = np.max(np.abs(weights - dequant))
52
+ rel_error = max_error / np.max(np.abs(weights))
53
+ assert rel_error < 0.02, f"8-bit relative error too high: {rel_error}"
54
+ print(f" 8-bit max relative error: {rel_error:.5f}")
55
+
56
+
57
+ def test_fp16_passthrough():
58
+ """Test fp16 passthrough (no quantization)."""
59
+ quantizer = SplitBitQuantizer(format="fp16")
60
+ weights = np.random.randn(64, 32).astype(np.float32)
61
+
62
+ packed = quantizer.quantize(weights)
63
+ dequant = quantizer.dequantize(packed)
64
+
65
+ # fp16 should be nearly identical
66
+ max_error = np.max(np.abs(weights - dequant))
67
+ assert max_error < 0.01, f"fp16 error too high: {max_error}"
68
+ print(f" fp16 max error: {max_error:.6f}")
69
+
70
+
71
+ def test_bpw_table():
72
+ """Test bits per weight table."""
73
+ assert bits_per_weight("ternary") > 1.5
74
+ assert bits_per_weight("q4_k_m") == 4.0
75
+ assert bits_per_weight("q8_0") == 8.0
76
+ assert bits_per_weight("fp16") == 16.0
77
+ assert compression_ratio("ternary") > 10.0
78
+ print(f" Ternary BPW: {bits_per_weight('ternary'):.3f}")
79
+ print(f" Ternary compression: {compression_ratio('ternary'):.1f}x")
80
+
81
+
82
+ if __name__ == "__main__":
83
+ print("Running quantization tests...")
84
+ test_ternary_quantization()
85
+ print(" ✓ test_ternary_quantization")
86
+ test_4bit_quantization()
87
+ print(" ✓ test_4bit_quantization")
88
+ test_8bit_quantization()
89
+ print(" ✓ test_8bit_quantization")
90
+ test_fp16_passthrough()
91
+ print(" ✓ test_fp16_passthrough")
92
+ test_bpw_table()
93
+ print(" ✓ test_bpw_table")
94
+ print("\nAll quantization tests passed!")
tests/test_splitbit.py ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Test recursive link graph: context storage, linking, traversal."""
2
+
3
+ import sys
4
+ import os
5
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
6
+
7
+ from splitbit_llm.splitbit.recursive_link import RecursiveLinkGraph
8
+ from splitbit_llm.splitbit.universal_link import UniversalLinkManager
9
+ from splitbit_llm.splitbit.splitbit_tokens import SplitBitTokenizer, SplitBitTokenConfig
10
+
11
+
12
+ def test_recursive_link_basic():
13
+ """Test basic context storage and retrieval."""
14
+ graph = RecursiveLinkGraph()
15
+ ctx_id = graph.add_context("Hello world", "Hi there!")
16
+ assert ctx_id in graph._contexts
17
+ print(f" Context stored: {ctx_id[:8]}")
18
+
19
+
20
+ def test_recursive_link_related():
21
+ """Test finding related contexts."""
22
+ graph = RecursiveLinkGraph()
23
+ graph.add_context("Hello world programming", "Programming is fun!")
24
+ graph.add_context("Hello world greeting", "Hi there!")
25
+ graph.add_context("Python code example", "Here is some code.")
26
+
27
+ related = graph.find_related("Hello world")
28
+ assert len(related) > 0, "No related contexts found"
29
+ print(f" Found {len(related)} related contexts")
30
+
31
+
32
+ def test_recursive_link_injection():
33
+ """Test context injection for inference."""
34
+ graph = RecursiveLinkGraph()
35
+ graph.add_context("What is Python?", "Python is a programming language.")
36
+ graph.add_context("How to code in Python?", "You can write Python code in any editor.")
37
+
38
+ injection = graph.get_injection_context("Tell me about Python")
39
+ assert "Python" in injection, f"Injection missing context: {injection}"
40
+ print(f" Injection: {injection[:80]}")
41
+
42
+
43
+ def test_universal_link():
44
+ """Test universal link manager."""
45
+ mgr = UniversalLinkManager(instance_id="test-instance")
46
+ learning_id = mgr.share_learning("conversation", {"message": "test"})
47
+ assert learning_id is not None
48
+ stats = mgr.get_stats()
49
+ assert stats["learnings_shared"] == 1
50
+ print(f" Shared learning: {learning_id[:8]}")
51
+
52
+
53
+ def test_splitbit_tokens():
54
+ """Test SplitBit token encoding/decoding."""
55
+ tok = SplitBitTokenizer(SplitBitTokenConfig(format="q4_k_m"))
56
+ token_ids = [1, 2, 3, 4, 5, 10, 20, 30, 40, 50]
57
+ encoded = tok.encode(token_ids)
58
+ decoded = tok.decode(encoded, len(token_ids))
59
+
60
+ assert len(encoded) < len(token_ids) * 4, "No compression achieved"
61
+ print(f" Encoded {len(token_ids)} tokens to {len(encoded)} bytes")
62
+ print(f" Compression: {len(token_ids) * 4 / len(encoded):.1f}x")
63
+
64
+
65
+ if __name__ == "__main__":
66
+ print("Running splitbit tests...")
67
+ test_recursive_link_basic()
68
+ print(" ✓ test_recursive_link_basic")
69
+ test_recursive_link_related()
70
+ print(" ✓ test_recursive_link_related")
71
+ test_recursive_link_injection()
72
+ print(" ✓ test_recursive_link_injection")
73
+ test_universal_link()
74
+ print(" ✓ test_universal_link")
75
+ test_splitbit_tokens()
76
+ print(" ✓ test_splitbit_tokens")
77
+ print("\nAll splitbit tests passed!")
tests/test_subscription.py ADDED
@@ -0,0 +1,247 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Test subscription manager — $1/month subscription with free trial."""
2
+
3
+ import sys
4
+ import os
5
+ import tempfile
6
+ import time
7
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
8
+
9
+ from splitbit_llm.subscription import SubscriptionManager, MONTHLY_PRICE
10
+
11
+
12
+ def test_no_subscription_blocks_access():
13
+ """Test that no subscription blocks access."""
14
+ with tempfile.TemporaryDirectory() as tmpdir:
15
+ sub = SubscriptionManager(data_dir=tmpdir)
16
+
17
+ access = sub.check_access()
18
+ assert not access["has_access"]
19
+ assert access["status"] == "none"
20
+ print(f" No subscription: access blocked — '{access['message']}'")
21
+
22
+
23
+ def test_free_trial():
24
+ """Test 7-day free trial."""
25
+ with tempfile.TemporaryDirectory() as tmpdir:
26
+ sub = SubscriptionManager(data_dir=tmpdir)
27
+
28
+ # Start trial
29
+ result = sub.start_trial()
30
+ assert result["success"]
31
+ assert result["status"] == "trial"
32
+ assert result["days_remaining"] == 7
33
+ print(f" Trial started: {result['days_remaining']} days")
34
+
35
+ # Access should be granted
36
+ access = sub.check_access()
37
+ assert access["has_access"]
38
+ assert access["status"] == "trial"
39
+ print(f" Trial access: granted — '{access['message']}'")
40
+
41
+ # Can't start trial again
42
+ result2 = sub.start_trial()
43
+ assert not result2["success"]
44
+ print(f" Second trial: blocked — '{result2['error']}'")
45
+
46
+
47
+ def test_subscribe():
48
+ """Test $1/month subscription."""
49
+ with tempfile.TemporaryDirectory() as tmpdir:
50
+ sub = SubscriptionManager(data_dir=tmpdir)
51
+
52
+ # Subscribe
53
+ result = sub.subscribe()
54
+ assert result["success"]
55
+ assert result["status"] == "active"
56
+ assert result["price"] == MONTHLY_PRICE
57
+ assert result["days_remaining"] >= 29
58
+ print(f" Subscribed: ${result['price']:.2f}/month, {result['days_remaining']} days")
59
+
60
+ # Access granted
61
+ access = sub.check_access()
62
+ assert access["has_access"]
63
+ assert access["status"] == "active"
64
+ print(f" Access: granted — '{access['message']}'")
65
+
66
+ # Stats
67
+ stats = sub.get_stats()
68
+ assert stats["months_subscribed"] == 1
69
+ assert stats["total_paid"] == MONTHLY_PRICE
70
+ assert stats["auto_renew"] is True
71
+ assert len(stats["payment_history"]) == 1
72
+ print(f" Stats: {stats['months_subscribed']} month, ${stats['total_paid']:.2f} paid")
73
+
74
+
75
+ def test_subscribe_extends():
76
+ """Test that subscribing while active extends the subscription."""
77
+ with tempfile.TemporaryDirectory() as tmpdir:
78
+ sub = SubscriptionManager(data_dir=tmpdir)
79
+
80
+ # First subscription
81
+ result1 = sub.subscribe()
82
+ assert result1["success"]
83
+ assert result1["days_remaining"] >= 29
84
+
85
+ # Second subscription should extend
86
+ result2 = sub.subscribe()
87
+ assert result2["success"]
88
+ assert result2["days_remaining"] >= 59 # 30 + 30
89
+ print(f" Extended: {result2['days_remaining']} days (30 + 30)")
90
+
91
+ stats = sub.get_stats()
92
+ assert stats["months_subscribed"] == 2
93
+ assert stats["total_paid"] == MONTHLY_PRICE * 2
94
+ print(f" Total: {stats['months_subscribed']} months, ${stats['total_paid']:.2f}")
95
+
96
+
97
+ def test_unsubscribe():
98
+ """Test cancelling subscription."""
99
+ with tempfile.TemporaryDirectory() as tmpdir:
100
+ sub = SubscriptionManager(data_dir=tmpdir)
101
+ sub.subscribe()
102
+
103
+ # Unsubscribe
104
+ result = sub.unsubscribe()
105
+ assert result["success"]
106
+ assert result["status"] == "cancelled"
107
+ print(f" Unsubscribed: {result['message']}")
108
+
109
+ # Access still works until expiration
110
+ access = sub.check_access()
111
+ assert access["has_access"] # still has access until expiration
112
+ print(f" Access after cancel: still granted until expiration")
113
+
114
+
115
+ def test_expired_blocks_access():
116
+ """Test that expired subscription blocks access."""
117
+ with tempfile.TemporaryDirectory() as tmpdir:
118
+ sub = SubscriptionManager(data_dir=tmpdir)
119
+
120
+ # Subscribe
121
+ sub.subscribe()
122
+
123
+ # Manually expire the subscription
124
+ sub._state["expiration_date"] = time.time() - 1 # expired 1 second ago
125
+ sub._state["auto_renew"] = False
126
+ sub._save_state()
127
+
128
+ # Should enter grace period first
129
+ access = sub.check_access()
130
+ assert access["has_access"] # grace period
131
+ assert access["status"] == "grace"
132
+ print(f" Grace period: {access['days_remaining']} days — '{access['message']}'")
133
+
134
+ # Expire grace period
135
+ sub._state["expiration_date"] = time.time() - (4 * 86400) # 4 days ago (past 3-day grace)
136
+ sub._save_state()
137
+
138
+ access2 = sub.check_access()
139
+ assert not access2["has_access"]
140
+ assert access2["status"] == "expired"
141
+ print(f" Expired: access blocked — '{access2['message']}'")
142
+
143
+
144
+ def test_auto_renew():
145
+ """Test auto-renewal when subscription expires."""
146
+ with tempfile.TemporaryDirectory() as tmpdir:
147
+ sub = SubscriptionManager(data_dir=tmpdir)
148
+ sub.subscribe()
149
+
150
+ months_before = sub._state["months_subscribed"]
151
+
152
+ # Simulate expiration with auto-renew on
153
+ sub._state["expiration_date"] = time.time() - 1
154
+ sub._state["auto_renew"] = True
155
+ sub._save_state()
156
+
157
+ access = sub.check_access()
158
+ assert access["has_access"]
159
+ assert access["status"] == "active" # auto-renewed
160
+ print(f" Auto-renewed: {access['days_remaining']} days — '{access['message']}'")
161
+
162
+ stats = sub.get_stats()
163
+ assert stats["months_subscribed"] == months_before + 1
164
+ print(f" Months subscribed: {stats['months_subscribed']} (auto-renewed)")
165
+
166
+
167
+ def test_trial_persistence():
168
+ """Test that subscription state persists across restarts."""
169
+ with tempfile.TemporaryDirectory() as tmpdir:
170
+ sub1 = SubscriptionManager(data_dir=tmpdir)
171
+ sub1.subscribe()
172
+
173
+ # Create new instance — should load state
174
+ sub2 = SubscriptionManager(data_dir=tmpdir)
175
+ stats = sub2.get_stats()
176
+ assert stats["status"] == "active"
177
+ assert stats["months_subscribed"] == 1
178
+ print(f" Persisted: status={stats['status']}, months={stats['months_subscribed']}")
179
+
180
+ access = sub2.check_access()
181
+ assert access["has_access"]
182
+ print(f" Access after restart: granted")
183
+
184
+
185
+ def test_trial_then_subscribe():
186
+ """Test using trial first, then subscribing."""
187
+ with tempfile.TemporaryDirectory() as tmpdir:
188
+ sub = SubscriptionManager(data_dir=tmpdir)
189
+
190
+ # Start trial
191
+ trial = sub.start_trial()
192
+ assert trial["success"]
193
+ print(f" Trial: {trial['days_remaining']} days")
194
+
195
+ # Subscribe while on trial
196
+ result = sub.subscribe()
197
+ assert result["success"]
198
+ assert result["status"] == "active"
199
+ print(f" Subscribed during trial: {result['days_remaining']} days")
200
+
201
+ # Can't start trial again
202
+ trial2 = sub.start_trial()
203
+ assert not trial2["success"]
204
+ print(f" Trial after subscribe: blocked")
205
+
206
+
207
+ def test_payment_history():
208
+ """Test payment history tracking."""
209
+ with tempfile.TemporaryDirectory() as tmpdir:
210
+ sub = SubscriptionManager(data_dir=tmpdir)
211
+
212
+ # Multiple payments
213
+ sub.subscribe(payment_reference="stripe-001")
214
+ sub.subscribe(payment_reference="stripe-002")
215
+ sub.subscribe(payment_reference="stripe-003")
216
+
217
+ stats = sub.get_stats()
218
+ assert stats["months_subscribed"] == 3
219
+ assert stats["total_paid"] == MONTHLY_PRICE * 3
220
+ assert len(stats["payment_history"]) == 3
221
+ print(f" Payments: {stats['months_subscribed']} months, ${stats['total_paid']:.2f}")
222
+ print(f" History: {len(stats['payment_history'])} records")
223
+
224
+
225
+ if __name__ == "__main__":
226
+ print("Running subscription manager tests...")
227
+ test_no_subscription_blocks_access()
228
+ print(" ✓ test_no_subscription_blocks_access")
229
+ test_free_trial()
230
+ print(" ✓ test_free_trial")
231
+ test_subscribe()
232
+ print(" ✓ test_subscribe")
233
+ test_subscribe_extends()
234
+ print(" ✓ test_subscribe_extends")
235
+ test_unsubscribe()
236
+ print(" ✓ test_unsubscribe")
237
+ test_expired_blocks_access()
238
+ print(" ✓ test_expired_blocks_access")
239
+ test_auto_renew()
240
+ print(" ✓ test_auto_renew")
241
+ test_trial_persistence()
242
+ print(" ✓ test_trial_persistence")
243
+ test_trial_then_subscribe()
244
+ print(" ✓ test_trial_then_subscribe")
245
+ test_payment_history()
246
+ print(" ✓ test_payment_history")
247
+ print("\nAll subscription tests passed!")
tests/test_tokenizer.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Test tokenizer: BPE training, encoding, decoding, round-trip."""
2
+
3
+ import sys
4
+ import os
5
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
6
+
7
+ from splitbit_llm.model.tokenizer import BPETokenizer, BOS_ID, EOS_ID, PAD_ID, UNK_ID
8
+
9
+
10
+ def test_tokenizer_basic():
11
+ """Test basic tokenizer training and encoding."""
12
+ tok = BPETokenizer(vocab_size=128)
13
+ text = "Hello world! This is a test. Hello world again. The quick brown fox."
14
+ tok.train(text, verbose=False)
15
+
16
+ assert tok.actual_vocab_size > 10, f"Vocab too small: {tok.actual_vocab_size}"
17
+ assert tok.actual_vocab_size <= 128, f"Vocab too large: {tok.actual_vocab_size}"
18
+ print(f" Vocab size: {tok.actual_vocab_size}")
19
+
20
+
21
+ def test_tokenizer_roundtrip():
22
+ """Test encode → decode round-trip."""
23
+ tok = BPETokenizer(vocab_size=256)
24
+ text = "Hello world! This is a test. Hello world again. The quick brown fox jumps over the lazy dog."
25
+ tok.train(text, verbose=False)
26
+
27
+ ids = tok.encode("Hello world!", add_bos=True, add_eos=True)
28
+ decoded = tok.decode(ids)
29
+
30
+ assert "Hello world!" in decoded, f"Round-trip failed: {decoded}"
31
+ print(f" Encoded: {ids}")
32
+ print(f" Decoded: {decoded}")
33
+
34
+
35
+ def test_tokenizer_special_tokens():
36
+ """Test special token IDs."""
37
+ assert BOS_ID == 1
38
+ assert EOS_ID == 2
39
+ assert PAD_ID == 0
40
+ assert UNK_ID == 3
41
+
42
+
43
+ def test_tokenizer_save_load(tmp_path=None):
44
+ """Test save and load."""
45
+ import tempfile
46
+ tok = BPETokenizer(vocab_size=128)
47
+ tok.train("Hello world test test test. Foo bar baz.", verbose=False)
48
+
49
+ with tempfile.NamedTemporaryFile(suffix=".json", delete=False, mode="w") as f:
50
+ path = f.name
51
+ tok.save(path)
52
+
53
+ tok2 = BPETokenizer.load(path)
54
+ assert tok2.actual_vocab_size == tok.actual_vocab_size
55
+ assert tok2.merges == tok.merges
56
+
57
+ # Clean up
58
+ os.unlink(path)
59
+ print(f" Save/load OK ({tok.actual_vocab_size} tokens)")
60
+
61
+
62
+ if __name__ == "__main__":
63
+ print("Running tokenizer tests...")
64
+ test_tokenizer_basic()
65
+ print(" ✓ test_tokenizer_basic")
66
+ test_tokenizer_roundtrip()
67
+ print(" ✓ test_tokenizer_roundtrip")
68
+ test_tokenizer_special_tokens()
69
+ print(" ✓ test_tokenizer_special_tokens")
70
+ test_tokenizer_save_load()
71
+ print(" ✓ test_tokenizer_save_load")
72
+ print("\nAll tokenizer tests passed!")
tests/test_tools_vault.py ADDED
@@ -0,0 +1,251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Test zero-limitation tools, code editing, and storage vault."""
2
+
3
+ import sys
4
+ import os
5
+ import tempfile
6
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
7
+
8
+ from splitbit_llm.harness.tools import (
9
+ ToolRegistry, get_default_tools, tool_loop, parse_tool_calls,
10
+ _tool_shell_exec, _tool_write_file, _tool_read_file,
11
+ _tool_code_edit, _tool_list_dir, _tool_make_dir, _tool_delete_file,
12
+ )
13
+ from splitbit_llm.storage.vault import StorageVault
14
+
15
+
16
+ def test_full_terminal_control():
17
+ """Test that shell_exec has zero limitations — can run any command."""
18
+ # Test a basic command
19
+ result = _tool_shell_exec("echo hello world")
20
+ assert "hello world" in result, f"Expected 'hello world' in result: {result}"
21
+ print(f" echo: {result.strip()}")
22
+
23
+ # Test that no commands are blocked
24
+ result2 = _tool_shell_exec("python --version")
25
+ assert "Error: dangerous command blocked" not in result2
26
+ print(f" python --version: {result2.strip()}")
27
+
28
+ # Test pip command (would be blocked by old safety filter)
29
+ result3 = _tool_shell_exec("echo pip install numpy")
30
+ assert "dangerous" not in result3.lower()
31
+ print(f" pip command: not blocked")
32
+
33
+
34
+ def test_code_edit_tool():
35
+ """Test code_edit tool — can modify files including its own framework."""
36
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
37
+ f.write("value = 'old'\nprint(value)\n")
38
+ path = f.name
39
+
40
+ try:
41
+ # Read original
42
+ content = _tool_read_file(path)
43
+ assert "old" in content
44
+
45
+ # Edit the file
46
+ result = _tool_code_edit(f'"{path}" "old" "new"')
47
+ assert "Replaced" in result, f"Expected replacement: {result}"
48
+ print(f" Edit result: {result}")
49
+
50
+ # Verify change
51
+ content = _tool_read_file(path)
52
+ assert "new" in content
53
+ assert "old" not in content
54
+ print(f" Verified: old → new")
55
+
56
+ finally:
57
+ os.unlink(path)
58
+
59
+
60
+ def test_list_dir_tool():
61
+ """Test list_dir tool."""
62
+ with tempfile.TemporaryDirectory() as tmpdir:
63
+ # Create some files
64
+ open(os.path.join(tmpdir, "file1.txt"), "w").close()
65
+ open(os.path.join(tmpdir, "file2.py"), "w").close()
66
+ os.makedirs(os.path.join(tmpdir, "subdir"))
67
+
68
+ result = _tool_list_dir(tmpdir)
69
+ assert "file1.txt" in result
70
+ assert "file2.py" in result
71
+ assert "subdir/" in result
72
+ print(f" Listed: {result.strip()}")
73
+
74
+
75
+ def test_make_dir_tool():
76
+ """Test make_dir tool."""
77
+ with tempfile.TemporaryDirectory() as tmpdir:
78
+ new_dir = os.path.join(tmpdir, "new_project", "src")
79
+ result = _tool_make_dir(new_dir)
80
+ assert "Created" in result
81
+ assert os.path.exists(new_dir)
82
+ print(f" Created: {new_dir}")
83
+
84
+
85
+ def test_delete_file_tool():
86
+ """Test delete_file tool."""
87
+ with tempfile.TemporaryDirectory() as tmpdir:
88
+ # Test file deletion
89
+ file_path = os.path.join(tmpdir, "temp.txt")
90
+ open(file_path, "w").close()
91
+ assert os.path.exists(file_path)
92
+ result = _tool_delete_file(file_path)
93
+ assert "Deleted file" in result
94
+ assert not os.path.exists(file_path)
95
+
96
+ # Test directory deletion
97
+ dir_path = os.path.join(tmpdir, "temp_dir")
98
+ os.makedirs(dir_path)
99
+ result2 = _tool_delete_file(dir_path)
100
+ assert "Deleted directory" in result2
101
+ assert not os.path.exists(dir_path)
102
+ print(f" Deleted file and directory: OK")
103
+
104
+
105
+ def test_zero_limitation_tools_count():
106
+ """Test that all zero-limitation tools are registered."""
107
+ tools = get_default_tools()
108
+ tool_names = [t.name for t in tools]
109
+ assert "shell_exec" in tool_names
110
+ assert "write_file" in tool_names
111
+ assert "code_edit" in tool_names
112
+ assert "list_dir" in tool_names
113
+ assert "make_dir" in tool_names
114
+ assert "delete_file" in tool_names
115
+ assert "read_file" in tool_names
116
+ assert "calculate" in tool_names
117
+ print(f" Tools: {len(tools)} registered — {tool_names}")
118
+
119
+ # Check shell_exec description says full control
120
+ shell_tool = next(t for t in tools if t.name == "shell_exec")
121
+ assert "full control" in shell_tool.description.lower() or "no restriction" in shell_tool.description.lower()
122
+ print(f" shell_exec: {shell_tool.description}")
123
+
124
+
125
+ def test_storage_vault():
126
+ """Test mass storage vault — auto-resizing storage."""
127
+ with tempfile.TemporaryDirectory() as tmpdir:
128
+ vault = StorageVault(data_dir=tmpdir)
129
+
130
+ # Check directories created
131
+ assert os.path.exists(vault.db_dir)
132
+ assert os.path.exists(vault.cache_dir)
133
+ assert os.path.exists(vault.archive_dir)
134
+ assert os.path.exists(vault.artifacts_dir)
135
+ print(f" Directories: db, cache, archive, artifacts — all created")
136
+
137
+ # Store and load artifact
138
+ path = vault.store_artifact("test_code.py", b"print('hello')")
139
+ assert os.path.exists(path)
140
+ loaded = vault.load_artifact("test_code.py")
141
+ assert loaded == b"print('hello')"
142
+ print(f" Artifact stored and loaded: {loaded}")
143
+
144
+ # Store text artifact
145
+ path2 = vault.store_artifact_text("notes.txt", "Important notes")
146
+ loaded2 = vault.load_artifact("notes.txt")
147
+ assert loaded2 == b"Important notes"
148
+ print(f" Text artifact: {loaded2}")
149
+
150
+ # List artifacts
151
+ artifacts = vault.list_artifacts()
152
+ assert len(artifacts) == 2
153
+ names = [a["name"] for a in artifacts]
154
+ assert "test_code.py" in names
155
+ assert "notes.txt" in names
156
+ print(f" Artifacts listed: {names}")
157
+
158
+ # Delete artifact
159
+ deleted = vault.delete_artifact("test_code.py")
160
+ assert deleted is True
161
+ artifacts = vault.list_artifacts()
162
+ assert len(artifacts) == 1
163
+ print(f" Deleted: test_code.py ({len(artifacts)} remaining)")
164
+
165
+ # Stats
166
+ stats = vault.get_stats()
167
+ assert stats["disk_total_gb"] > 0
168
+ assert "total_storage_mb" in stats
169
+ assert "disk_usage_percent" in stats
170
+ print(f" Stats: {stats['total_storage_mb']}MB used, "
171
+ f"{stats['disk_free_gb']}GB free, {stats['disk_usage_percent']:.1%} disk usage")
172
+
173
+
174
+ def test_vault_auto_resize():
175
+ """Test vault auto-resize and cleanup."""
176
+ with tempfile.TemporaryDirectory() as tmpdir:
177
+ vault = StorageVault(data_dir=tmpdir)
178
+
179
+ # Store many artifacts
180
+ for i in range(20):
181
+ vault.store_artifact_text(f"artifact_{i}.txt", f"content {i}" * 100)
182
+
183
+ stats = vault.get_stats()
184
+ assert stats["artifacts_storage_bytes"] > 0
185
+ print(f" Stored 20 artifacts: {stats['artifacts_storage_bytes']} bytes")
186
+
187
+ # Force cleanup (should not delete recent files)
188
+ result = vault.force_cleanup()
189
+ assert "cleanups_performed" in result
190
+ # Recent files should still be there
191
+ artifacts = vault.list_artifacts()
192
+ assert len(artifacts) == 20 # nothing deleted (all recent)
193
+ print(f" Cleanup: {result['cleanups_performed']} cycles, {len(artifacts)} artifacts remain")
194
+
195
+
196
+ def test_vault_db_paths():
197
+ """Test vault DB path management."""
198
+ with tempfile.TemporaryDirectory() as tmpdir:
199
+ vault = StorageVault(data_dir=tmpdir)
200
+
201
+ db_path = vault.get_db_path("memory")
202
+ assert db_path.endswith("memory.db")
203
+ assert "db" in db_path
204
+
205
+ cache_path = vault.get_cache_path("test.cache")
206
+ assert "cache" in cache_path
207
+
208
+ artifact_path = vault.get_artifact_path("code.py")
209
+ assert "artifacts" in artifact_path
210
+ print(f" DB path: {db_path}")
211
+ print(f" Cache path: {cache_path}")
212
+ print(f" Artifact path: {artifact_path}")
213
+
214
+
215
+ def test_tool_loop_with_new_tools():
216
+ """Test that tool_loop works with the new tools."""
217
+ registry = ToolRegistry()
218
+ for tool in get_default_tools():
219
+ registry.register(tool)
220
+
221
+ # Test list_dir via tool loop
222
+ text = '[TOOL: list_dir(".")]'
223
+ result_text, results = tool_loop(text, registry)
224
+ assert len(results) > 0
225
+ assert results[0].success
226
+ print(f" Tool loop: {results[0].name} → {results[0].output[:50]}...")
227
+
228
+
229
+ if __name__ == "__main__":
230
+ print("Running zero-limitation tools and storage vault tests...")
231
+ test_full_terminal_control()
232
+ print(" ✓ test_full_terminal_control")
233
+ test_code_edit_tool()
234
+ print(" ✓ test_code_edit_tool")
235
+ test_list_dir_tool()
236
+ print(" ✓ test_list_dir_tool")
237
+ test_make_dir_tool()
238
+ print(" ✓ test_make_dir_tool")
239
+ test_delete_file_tool()
240
+ print(" ✓ test_delete_file_tool")
241
+ test_zero_limitation_tools_count()
242
+ print(" ✓ test_zero_limitation_tools_count")
243
+ test_storage_vault()
244
+ print(" ✓ test_storage_vault")
245
+ test_vault_auto_resize()
246
+ print(" ✓ test_vault_auto_resize")
247
+ test_vault_db_paths()
248
+ print(" ✓ test_vault_db_paths")
249
+ test_tool_loop_with_new_tools()
250
+ print(" ✓ test_tool_loop_with_new_tools")
251
+ print("\nAll zero-limitation & vault tests passed!")
tests/test_voice.py ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Test voice components: TTS formatter, voice adapter, self-improvement."""
2
+
3
+ import sys
4
+ import os
5
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
6
+
7
+ from splitbit_llm.voice.tts_output import TTSOutputFormatter, number_to_words
8
+ from splitbit_llm.voice.voice_adapter import VoiceAdapter
9
+ from splitbit_llm.voice.self_improve import SelfImprovementEngine
10
+
11
+
12
+ def test_tts_formatter():
13
+ """Test TTS output formatting."""
14
+ fmt = TTSOutputFormatter()
15
+
16
+ # Test markdown stripping
17
+ result = fmt.format("**Hello** world! This is *italic* text.")
18
+ assert "**" not in result, f"Bold not stripped: {result}"
19
+ assert "*" not in result, f"Italic not stripped: {result}"
20
+ print(f" Markdown stripped: {result}")
21
+
22
+ # Test code block stripping
23
+ result = fmt.format("Here is code: `print('hello')` and ```python\nx=1\n```")
24
+ assert "```" not in result, f"Code block not stripped: {result}"
25
+ print(f" Code stripped: {result}")
26
+
27
+ # Test abbreviation expansion
28
+ result = fmt.format("The API and URL are working.")
29
+ assert "A P I" in result, f"Abbreviation not expanded: {result}"
30
+ print(f" Abbreviations expanded: {result}")
31
+
32
+
33
+ def test_number_to_words():
34
+ """Test number to words conversion."""
35
+ assert number_to_words(0) == "zero"
36
+ assert number_to_words(1) == "one"
37
+ assert number_to_words(10) == "ten"
38
+ assert number_to_words(15) == "fifteen"
39
+ assert number_to_words(42) == "forty two"
40
+ assert number_to_words(100) == "one hundred"
41
+ assert "one thousand" in number_to_words(1000)
42
+ print(f" 42 → '{number_to_words(42)}'")
43
+ print(f" 100 → '{number_to_words(100)}'")
44
+
45
+
46
+ def test_number_normalization():
47
+ """Test number normalization in TTS formatter."""
48
+ fmt = TTSOutputFormatter()
49
+ result = fmt.format("I have 100 apples and 42 oranges.")
50
+ assert "one hundred" in result, f"Number not normalized: {result}"
51
+ assert "forty two" in result, f"Number not normalized: {result}"
52
+ print(f" Numbers normalized: {result}")
53
+
54
+
55
+ def test_voice_adapter():
56
+ """Test voice adapter sentence splitting."""
57
+ adapter = VoiceAdapter()
58
+
59
+ def mock_stream():
60
+ yield "Hello "
61
+ yield "there. "
62
+ yield "How are "
63
+ yield "you? "
64
+ yield "I am fine!"
65
+
66
+ sentences = list(adapter.stream_sentences(mock_stream()))
67
+ assert len(sentences) >= 2, f"Expected 2+ sentences, got {len(sentences)}"
68
+ print(f" Sentences: {sentences}")
69
+
70
+
71
+ def test_self_improvement():
72
+ """Test self-improvement engine."""
73
+ engine = SelfImprovementEngine(model=None, tokenizer=None)
74
+
75
+ # Record conversations
76
+ engine.record_conversation("Hello", "Hi there!", confidence=0.8)
77
+ engine.record_conversation("What is AI?", "AI is artificial intelligence.", confidence=0.6)
78
+ engine.record_conversation("How to code?", "Start with Python basics.", confidence=0.7)
79
+
80
+ stats = engine.get_stats()
81
+ assert stats["conversations_learned"] == 3, f"Wrong count: {stats['conversations_learned']}"
82
+ assert stats["avg_confidence"] > 0.5, f"Low confidence: {stats['avg_confidence']}"
83
+ print(f" Conversations: {stats['conversations_learned']}")
84
+ print(f" Avg confidence: {stats['avg_confidence']:.2f}")
85
+
86
+
87
+ def test_self_talk():
88
+ """Test self-talk generation."""
89
+ engine = SelfImprovementEngine(model=None, tokenizer=None)
90
+
91
+ def mock_generate(prompt):
92
+ if "Ask a question" in prompt:
93
+ return "What is machine learning?"
94
+ return "Machine learning is a subset of AI that learns from data."
95
+
96
+ pairs = engine.self_talk(mock_generate, max_rounds=1)
97
+ assert len(pairs) >= 0, "Self-talk returned None"
98
+ print(f" Self-talk pairs: {len(pairs)}")
99
+
100
+
101
+ if __name__ == "__main__":
102
+ print("Running voice tests...")
103
+ test_tts_formatter()
104
+ print(" ✓ test_tts_formatter")
105
+ test_number_to_words()
106
+ print(" ✓ test_number_to_words")
107
+ test_number_normalization()
108
+ print(" ✓ test_number_normalization")
109
+ test_voice_adapter()
110
+ print(" ✓ test_voice_adapter")
111
+ test_self_improvement()
112
+ print(" ✓ test_self_improvement")
113
+ test_self_talk()
114
+ print(" ✓ test_self_talk")
115
+ print("\nAll voice tests passed!")