ruvnet-kb-first 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +674 -0
- package/SKILL.md +740 -0
- package/bin/kb-first.js +123 -0
- package/install/init-project.sh +435 -0
- package/install/install-global.sh +257 -0
- package/install/kb-first-autodetect.sh +108 -0
- package/install/kb-first-command.md +80 -0
- package/install/kb-first-skill.md +262 -0
- package/package.json +87 -0
- package/phases/00-assessment.md +529 -0
- package/phases/01-storage.md +194 -0
- package/phases/01.5-hooks-setup.md +521 -0
- package/phases/02-kb-creation.md +413 -0
- package/phases/03-persistence.md +125 -0
- package/phases/04-visualization.md +170 -0
- package/phases/05-integration.md +114 -0
- package/phases/06-scaffold.md +130 -0
- package/phases/07-build.md +493 -0
- package/phases/08-verification.md +597 -0
- package/phases/09-security.md +512 -0
- package/phases/10-documentation.md +613 -0
- package/phases/11-deployment.md +670 -0
- package/phases/testing.md +713 -0
- package/scripts/1.5-hooks-verify.sh +252 -0
- package/scripts/8.1-code-scan.sh +58 -0
- package/scripts/8.2-import-check.sh +42 -0
- package/scripts/8.3-source-returns.sh +52 -0
- package/scripts/8.4-startup-verify.sh +65 -0
- package/scripts/8.5-fallback-check.sh +63 -0
- package/scripts/8.6-attribution.sh +56 -0
- package/scripts/8.7-confidence.sh +56 -0
- package/scripts/8.8-gap-logging.sh +70 -0
- package/scripts/9-security-audit.sh +202 -0
- package/scripts/init-project.sh +395 -0
- package/scripts/verify-enforcement.sh +167 -0
- package/src/commands/hooks.js +361 -0
- package/src/commands/init.js +315 -0
- package/src/commands/phase.js +372 -0
- package/src/commands/score.js +380 -0
- package/src/commands/status.js +193 -0
- package/src/commands/verify.js +286 -0
- package/src/index.js +56 -0
- package/src/mcp-server.js +412 -0
- package/templates/attention-router.ts +534 -0
- package/templates/code-analysis.ts +683 -0
- package/templates/federated-kb-learner.ts +649 -0
- package/templates/gnn-engine.ts +1091 -0
- package/templates/intentions.md +277 -0
- package/templates/kb-client.ts +905 -0
- package/templates/schema.sql +303 -0
- package/templates/sona-config.ts +312 -0
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
-- =============================================================================
|
|
2
|
+
-- KB-First v2.9 Database Schema
|
|
3
|
+
-- =============================================================================
|
|
4
|
+
--
|
|
5
|
+
-- Run this against your PostgreSQL database to set up the KB schema.
|
|
6
|
+
-- Works with both ruvector-postgres and standard pgvector.
|
|
7
|
+
--
|
|
8
|
+
-- Usage:
|
|
9
|
+
-- psql $DATABASE_URL -f schema.sql
|
|
10
|
+
--
|
|
11
|
+
-- =============================================================================
|
|
12
|
+
|
|
13
|
+
-- Enable required extensions
|
|
14
|
+
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
|
15
|
+
CREATE EXTENSION IF NOT EXISTS pg_trgm; -- Text similarity
|
|
16
|
+
|
|
17
|
+
-- Try ruvector first (enhanced features), fall back to pgvector
|
|
18
|
+
DO $$
|
|
19
|
+
BEGIN
|
|
20
|
+
CREATE EXTENSION IF NOT EXISTS ruvector;
|
|
21
|
+
RAISE NOTICE 'Using ruvector extension (enhanced features available)';
|
|
22
|
+
EXCEPTION WHEN OTHERS THEN
|
|
23
|
+
BEGIN
|
|
24
|
+
CREATE EXTENSION IF NOT EXISTS vector;
|
|
25
|
+
RAISE NOTICE 'Using pgvector extension (standard features)';
|
|
26
|
+
EXCEPTION WHEN OTHERS THEN
|
|
27
|
+
RAISE NOTICE 'No vector extension available - text search only';
|
|
28
|
+
END;
|
|
29
|
+
END $$;
|
|
30
|
+
|
|
31
|
+
-- =============================================================================
|
|
32
|
+
-- CORE TABLES
|
|
33
|
+
-- =============================================================================
|
|
34
|
+
|
|
35
|
+
-- Knowledge Base Nodes
|
|
36
|
+
-- Each row is a piece of curated knowledge with expert attribution
|
|
37
|
+
CREATE TABLE IF NOT EXISTS kb_nodes (
|
|
38
|
+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
39
|
+
|
|
40
|
+
-- Organization
|
|
41
|
+
namespace TEXT NOT NULL DEFAULT 'default', -- Separate KBs per client/project
|
|
42
|
+
path TEXT NOT NULL, -- Hierarchical path: "retirement/withdrawal/4-percent-rule"
|
|
43
|
+
|
|
44
|
+
-- Content
|
|
45
|
+
title TEXT NOT NULL,
|
|
46
|
+
content TEXT, -- The actual knowledge content
|
|
47
|
+
|
|
48
|
+
-- Attribution (REQUIRED - this is KB-First, not just RAG)
|
|
49
|
+
source_expert TEXT NOT NULL, -- "Wade Pfau, PhD" - who said this?
|
|
50
|
+
source_url TEXT NOT NULL, -- Link to original source
|
|
51
|
+
|
|
52
|
+
-- Quality
|
|
53
|
+
confidence REAL DEFAULT 1.0
|
|
54
|
+
CHECK (confidence >= 0 AND confidence <= 1), -- 0.0 to 1.0
|
|
55
|
+
|
|
56
|
+
-- Metadata
|
|
57
|
+
metadata JSONB DEFAULT '{}', -- Flexible additional data
|
|
58
|
+
|
|
59
|
+
-- Vector embedding (384 dims for all-MiniLM-L6-v2)
|
|
60
|
+
embedding vector(384),
|
|
61
|
+
|
|
62
|
+
-- Usage tracking
|
|
63
|
+
access_count INTEGER DEFAULT 0,
|
|
64
|
+
last_accessed TIMESTAMPTZ,
|
|
65
|
+
|
|
66
|
+
-- Timestamps
|
|
67
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
68
|
+
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
69
|
+
|
|
70
|
+
-- Constraints
|
|
71
|
+
UNIQUE(namespace, path)
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
COMMENT ON TABLE kb_nodes IS 'Core knowledge base content with expert attribution';
|
|
75
|
+
COMMENT ON COLUMN kb_nodes.source_expert IS 'Required: Name of expert source (e.g., "Wade Pfau, PhD")';
|
|
76
|
+
COMMENT ON COLUMN kb_nodes.source_url IS 'Required: URL to original source material';
|
|
77
|
+
COMMENT ON COLUMN kb_nodes.confidence IS 'Quality score 0.0-1.0 (high=0.8+, medium=0.5-0.8, low=<0.5)';
|
|
78
|
+
|
|
79
|
+
-- =============================================================================
|
|
80
|
+
-- GAP DETECTION
|
|
81
|
+
-- =============================================================================
|
|
82
|
+
|
|
83
|
+
-- Track queries that couldn't be answered from the KB
|
|
84
|
+
-- Use this to identify what content to add
|
|
85
|
+
CREATE TABLE IF NOT EXISTS kb_gaps (
|
|
86
|
+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
87
|
+
|
|
88
|
+
query TEXT NOT NULL, -- The unanswered query
|
|
89
|
+
reason TEXT, -- Why it couldn't be answered
|
|
90
|
+
namespace TEXT DEFAULT 'default',
|
|
91
|
+
|
|
92
|
+
-- Tracking
|
|
93
|
+
first_seen TIMESTAMPTZ DEFAULT NOW(),
|
|
94
|
+
last_seen TIMESTAMPTZ DEFAULT NOW(),
|
|
95
|
+
occurrence_count INTEGER DEFAULT 1, -- How many times this gap appeared
|
|
96
|
+
|
|
97
|
+
-- Resolution
|
|
98
|
+
resolved BOOLEAN DEFAULT false,
|
|
99
|
+
resolved_at TIMESTAMPTZ,
|
|
100
|
+
resolved_by_node UUID REFERENCES kb_nodes(id),
|
|
101
|
+
|
|
102
|
+
UNIQUE(query, namespace)
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
COMMENT ON TABLE kb_gaps IS 'Queries that could not be answered - use for KB improvement';
|
|
106
|
+
|
|
107
|
+
-- =============================================================================
|
|
108
|
+
-- REASONING BANK (Learning)
|
|
109
|
+
-- =============================================================================
|
|
110
|
+
|
|
111
|
+
-- Store successful query-response patterns for future reference
|
|
112
|
+
CREATE TABLE IF NOT EXISTS reasoning_bank (
|
|
113
|
+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
114
|
+
|
|
115
|
+
-- Pattern
|
|
116
|
+
query_pattern TEXT UNIQUE NOT NULL, -- The query that worked
|
|
117
|
+
successful_response TEXT NOT NULL, -- The response that was good
|
|
118
|
+
kb_nodes_used UUID[] DEFAULT '{}', -- Which nodes contributed
|
|
119
|
+
|
|
120
|
+
-- Quality
|
|
121
|
+
feedback_score REAL DEFAULT 0.5
|
|
122
|
+
CHECK (feedback_score >= 0 AND feedback_score <= 1),
|
|
123
|
+
use_count INTEGER DEFAULT 1, -- Times this pattern was used
|
|
124
|
+
|
|
125
|
+
-- Vector for similarity matching
|
|
126
|
+
embedding vector(384),
|
|
127
|
+
|
|
128
|
+
-- Timestamps
|
|
129
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
130
|
+
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
COMMENT ON TABLE reasoning_bank IS 'Successful patterns for learning - improves over time';
|
|
134
|
+
|
|
135
|
+
-- =============================================================================
|
|
136
|
+
-- ANALYTICS
|
|
137
|
+
-- =============================================================================
|
|
138
|
+
|
|
139
|
+
-- Track all KB queries for analysis
|
|
140
|
+
CREATE TABLE IF NOT EXISTS kb_analytics (
|
|
141
|
+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
142
|
+
|
|
143
|
+
event_type TEXT NOT NULL, -- 'search', 'gap', 'feedback'
|
|
144
|
+
query TEXT,
|
|
145
|
+
namespace TEXT DEFAULT 'default',
|
|
146
|
+
|
|
147
|
+
-- Results
|
|
148
|
+
results_count INTEGER DEFAULT 0,
|
|
149
|
+
top_confidence REAL,
|
|
150
|
+
gap_detected BOOLEAN DEFAULT false,
|
|
151
|
+
|
|
152
|
+
-- Performance
|
|
153
|
+
latency_ms INTEGER,
|
|
154
|
+
|
|
155
|
+
-- Session
|
|
156
|
+
session_id TEXT,
|
|
157
|
+
|
|
158
|
+
-- Timestamps
|
|
159
|
+
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
COMMENT ON TABLE kb_analytics IS 'Query analytics for monitoring and improvement';
|
|
163
|
+
|
|
164
|
+
-- =============================================================================
|
|
165
|
+
-- INDEXES
|
|
166
|
+
-- =============================================================================
|
|
167
|
+
|
|
168
|
+
-- Primary lookups
|
|
169
|
+
CREATE INDEX IF NOT EXISTS kb_nodes_namespace_idx ON kb_nodes(namespace);
|
|
170
|
+
CREATE INDEX IF NOT EXISTS kb_nodes_path_idx ON kb_nodes(path);
|
|
171
|
+
CREATE INDEX IF NOT EXISTS kb_nodes_confidence_idx ON kb_nodes(confidence DESC);
|
|
172
|
+
|
|
173
|
+
-- Text search
|
|
174
|
+
CREATE INDEX IF NOT EXISTS kb_nodes_title_trgm_idx ON kb_nodes USING gin(title gin_trgm_ops);
|
|
175
|
+
CREATE INDEX IF NOT EXISTS kb_nodes_content_trgm_idx ON kb_nodes USING gin(content gin_trgm_ops);
|
|
176
|
+
|
|
177
|
+
-- Full text search
|
|
178
|
+
CREATE INDEX IF NOT EXISTS kb_nodes_fts_idx ON kb_nodes USING gin(
|
|
179
|
+
to_tsvector('english', COALESCE(title, '') || ' ' || COALESCE(content, ''))
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
-- Vector search (HNSW for fast approximate search)
|
|
183
|
+
-- This will fail gracefully if vector extension not available
|
|
184
|
+
DO $$ BEGIN
|
|
185
|
+
CREATE INDEX IF NOT EXISTS kb_nodes_embedding_idx ON kb_nodes
|
|
186
|
+
USING hnsw (embedding vector_cosine_ops)
|
|
187
|
+
WITH (m = 16, ef_construction = 64);
|
|
188
|
+
EXCEPTION WHEN OTHERS THEN
|
|
189
|
+
RAISE NOTICE 'Vector index not created - extension not available';
|
|
190
|
+
END $$;
|
|
191
|
+
|
|
192
|
+
-- Gap tracking
|
|
193
|
+
CREATE INDEX IF NOT EXISTS kb_gaps_namespace_idx ON kb_gaps(namespace);
|
|
194
|
+
CREATE INDEX IF NOT EXISTS kb_gaps_count_idx ON kb_gaps(occurrence_count DESC);
|
|
195
|
+
CREATE INDEX IF NOT EXISTS kb_gaps_unresolved_idx ON kb_gaps(namespace) WHERE NOT resolved;
|
|
196
|
+
|
|
197
|
+
-- Analytics
|
|
198
|
+
CREATE INDEX IF NOT EXISTS kb_analytics_type_idx ON kb_analytics(event_type);
|
|
199
|
+
CREATE INDEX IF NOT EXISTS kb_analytics_time_idx ON kb_analytics(created_at DESC);
|
|
200
|
+
CREATE INDEX IF NOT EXISTS kb_analytics_session_idx ON kb_analytics(session_id);
|
|
201
|
+
|
|
202
|
+
-- =============================================================================
|
|
203
|
+
-- FUNCTIONS
|
|
204
|
+
-- =============================================================================
|
|
205
|
+
|
|
206
|
+
-- Update timestamp trigger
|
|
207
|
+
CREATE OR REPLACE FUNCTION update_updated_at()
|
|
208
|
+
RETURNS TRIGGER AS $$
|
|
209
|
+
BEGIN
|
|
210
|
+
NEW.updated_at = NOW();
|
|
211
|
+
RETURN NEW;
|
|
212
|
+
END;
|
|
213
|
+
$$ LANGUAGE plpgsql;
|
|
214
|
+
|
|
215
|
+
-- Apply to tables
|
|
216
|
+
DROP TRIGGER IF EXISTS kb_nodes_updated_at ON kb_nodes;
|
|
217
|
+
CREATE TRIGGER kb_nodes_updated_at
|
|
218
|
+
BEFORE UPDATE ON kb_nodes
|
|
219
|
+
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
|
220
|
+
|
|
221
|
+
DROP TRIGGER IF EXISTS reasoning_bank_updated_at ON reasoning_bank;
|
|
222
|
+
CREATE TRIGGER reasoning_bank_updated_at
|
|
223
|
+
BEFORE UPDATE ON reasoning_bank
|
|
224
|
+
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
|
225
|
+
|
|
226
|
+
-- =============================================================================
|
|
227
|
+
-- HELPFUL VIEWS
|
|
228
|
+
-- =============================================================================
|
|
229
|
+
|
|
230
|
+
-- Top gaps to address
|
|
231
|
+
CREATE OR REPLACE VIEW kb_top_gaps AS
|
|
232
|
+
SELECT
|
|
233
|
+
query,
|
|
234
|
+
reason,
|
|
235
|
+
namespace,
|
|
236
|
+
occurrence_count,
|
|
237
|
+
last_seen,
|
|
238
|
+
first_seen
|
|
239
|
+
FROM kb_gaps
|
|
240
|
+
WHERE NOT resolved
|
|
241
|
+
ORDER BY occurrence_count DESC, last_seen DESC
|
|
242
|
+
LIMIT 100;
|
|
243
|
+
|
|
244
|
+
COMMENT ON VIEW kb_top_gaps IS 'Top unresolved gaps - prioritize these for KB improvement';
|
|
245
|
+
|
|
246
|
+
-- KB health summary
|
|
247
|
+
CREATE OR REPLACE VIEW kb_health AS
|
|
248
|
+
SELECT
|
|
249
|
+
namespace,
|
|
250
|
+
COUNT(*) as node_count,
|
|
251
|
+
COUNT(*) FILTER (WHERE content IS NOT NULL) as nodes_with_content,
|
|
252
|
+
AVG(confidence) as avg_confidence,
|
|
253
|
+
COUNT(*) FILTER (WHERE confidence >= 0.8) as high_confidence_count,
|
|
254
|
+
COUNT(*) FILTER (WHERE last_accessed > NOW() - INTERVAL '7 days') as recently_accessed,
|
|
255
|
+
MAX(updated_at) as last_updated
|
|
256
|
+
FROM kb_nodes
|
|
257
|
+
GROUP BY namespace;
|
|
258
|
+
|
|
259
|
+
COMMENT ON VIEW kb_health IS 'KB health metrics per namespace';
|
|
260
|
+
|
|
261
|
+
-- =============================================================================
|
|
262
|
+
-- SAMPLE DATA (Remove in production)
|
|
263
|
+
-- =============================================================================
|
|
264
|
+
|
|
265
|
+
-- Uncomment to add sample data for testing:
|
|
266
|
+
/*
|
|
267
|
+
INSERT INTO kb_nodes (namespace, path, title, content, source_expert, source_url, confidence)
|
|
268
|
+
VALUES
|
|
269
|
+
('demo', 'retirement/withdrawal/4-percent-rule',
|
|
270
|
+
'The 4% Rule',
|
|
271
|
+
'The 4% rule suggests withdrawing 4% of your portfolio in the first year of retirement, then adjusting for inflation each subsequent year.',
|
|
272
|
+
'William Bengen',
|
|
273
|
+
'https://www.financialplanningassociation.org/article/journal/OCT94-determining-withdrawal-rates-using-historical-data',
|
|
274
|
+
0.95),
|
|
275
|
+
('demo', 'retirement/withdrawal/guardrails',
|
|
276
|
+
'Guardrails Strategy',
|
|
277
|
+
'The guardrails approach adjusts withdrawals based on portfolio performance, increasing spending when markets are up and decreasing when down.',
|
|
278
|
+
'Jonathan Guyton',
|
|
279
|
+
'https://www.financialplanningassociation.org/article/journal/MAR06-decision-rules-and-portfolio-management-retirees-when-change-becomes-necessary',
|
|
280
|
+
0.90)
|
|
281
|
+
ON CONFLICT (namespace, path) DO NOTHING;
|
|
282
|
+
*/
|
|
283
|
+
|
|
284
|
+
-- =============================================================================
|
|
285
|
+
-- VERIFICATION
|
|
286
|
+
-- =============================================================================
|
|
287
|
+
|
|
288
|
+
DO $$
|
|
289
|
+
DECLARE
|
|
290
|
+
ext_name TEXT;
|
|
291
|
+
BEGIN
|
|
292
|
+
-- Check which extension is active
|
|
293
|
+
SELECT extname INTO ext_name FROM pg_extension WHERE extname IN ('ruvector', 'vector') LIMIT 1;
|
|
294
|
+
|
|
295
|
+
IF ext_name IS NOT NULL THEN
|
|
296
|
+
RAISE NOTICE 'KB-First schema ready with % extension', ext_name;
|
|
297
|
+
ELSE
|
|
298
|
+
RAISE NOTICE 'KB-First schema ready (text search only - no vector extension)';
|
|
299
|
+
END IF;
|
|
300
|
+
|
|
301
|
+
RAISE NOTICE 'Tables created: kb_nodes, kb_gaps, reasoning_bank, kb_analytics';
|
|
302
|
+
RAISE NOTICE 'Views created: kb_top_gaps, kb_health';
|
|
303
|
+
END $$;
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SONA Engine Configuration Template
|
|
3
|
+
* Self-Optimizing Neural Architecture for KB-First Applications
|
|
4
|
+
*
|
|
5
|
+
* Use this template for Scenario Learning and Continuous Optimization patterns.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Pool } from 'pg';
|
|
9
|
+
|
|
10
|
+
// ============================================
|
|
11
|
+
// CONFIGURATION INTERFACES
|
|
12
|
+
// ============================================
|
|
13
|
+
|
|
14
|
+
export interface SonaConfig {
|
|
15
|
+
// Core architecture
|
|
16
|
+
hidden_dim: number; // Hidden layer dimension (128, 256, 512)
|
|
17
|
+
pattern_clusters: number; // Number of pattern clusters (32, 64, 128)
|
|
18
|
+
|
|
19
|
+
// Learning parameters
|
|
20
|
+
ewc_lambda: number; // Elastic Weight Consolidation (0.2-0.6)
|
|
21
|
+
base_lora_rank: number; // LoRA rank for base learning (4-16)
|
|
22
|
+
micro_lora_rank: number; // LoRA rank for instant learning (2-4)
|
|
23
|
+
|
|
24
|
+
// Pattern storage
|
|
25
|
+
pattern_quality_threshold: number; // Min quality to store (0.6-0.8)
|
|
26
|
+
max_patterns_per_cluster: number; // Prevent unbounded growth
|
|
27
|
+
|
|
28
|
+
// Trajectory tracking
|
|
29
|
+
trajectory_timeout_seconds: number; // Auto-end incomplete trajectories
|
|
30
|
+
trajectory_consolidate_seconds: number; // Background consolidation interval
|
|
31
|
+
|
|
32
|
+
// Database
|
|
33
|
+
connectionString: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ============================================
|
|
37
|
+
// PRESET CONFIGURATIONS BY DOMAIN
|
|
38
|
+
// ============================================
|
|
39
|
+
|
|
40
|
+
export const PRESETS = {
|
|
41
|
+
/**
|
|
42
|
+
* STABLE DOMAINS (Finance, Law, Medicine)
|
|
43
|
+
* - High anti-forgetting (ewc_lambda)
|
|
44
|
+
* - Deep pattern learning (high lora_rank)
|
|
45
|
+
* - High quality threshold
|
|
46
|
+
*/
|
|
47
|
+
stable: {
|
|
48
|
+
hidden_dim: 256,
|
|
49
|
+
pattern_clusters: 64,
|
|
50
|
+
ewc_lambda: 0.6,
|
|
51
|
+
base_lora_rank: 16,
|
|
52
|
+
micro_lora_rank: 4,
|
|
53
|
+
pattern_quality_threshold: 0.8,
|
|
54
|
+
max_patterns_per_cluster: 1000,
|
|
55
|
+
trajectory_timeout_seconds: 86400, // 24 hours
|
|
56
|
+
trajectory_consolidate_seconds: 3600, // 1 hour
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* MODERATE DOMAINS (Business, Travel, Education)
|
|
61
|
+
* - Balanced anti-forgetting
|
|
62
|
+
* - Moderate pattern learning
|
|
63
|
+
* - Moderate quality threshold
|
|
64
|
+
*/
|
|
65
|
+
moderate: {
|
|
66
|
+
hidden_dim: 256,
|
|
67
|
+
pattern_clusters: 64,
|
|
68
|
+
ewc_lambda: 0.4,
|
|
69
|
+
base_lora_rank: 12,
|
|
70
|
+
micro_lora_rank: 3,
|
|
71
|
+
pattern_quality_threshold: 0.7,
|
|
72
|
+
max_patterns_per_cluster: 500,
|
|
73
|
+
trajectory_timeout_seconds: 43200, // 12 hours
|
|
74
|
+
trajectory_consolidate_seconds: 1800, // 30 minutes
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* FAST-CHANGING DOMAINS (SEO, Markets, Social Media)
|
|
79
|
+
* - Low anti-forgetting (rapid adaptation)
|
|
80
|
+
* - Fast pattern learning (low lora_rank)
|
|
81
|
+
* - Lower quality threshold (learn from modest wins)
|
|
82
|
+
*/
|
|
83
|
+
fastChanging: {
|
|
84
|
+
hidden_dim: 128,
|
|
85
|
+
pattern_clusters: 32,
|
|
86
|
+
ewc_lambda: 0.2,
|
|
87
|
+
base_lora_rank: 4,
|
|
88
|
+
micro_lora_rank: 2,
|
|
89
|
+
pattern_quality_threshold: 0.6,
|
|
90
|
+
max_patterns_per_cluster: 200,
|
|
91
|
+
trajectory_timeout_seconds: 7200, // 2 hours
|
|
92
|
+
trajectory_consolidate_seconds: 300, // 5 minutes
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// ============================================
|
|
97
|
+
// SONA ENGINE CLASS
|
|
98
|
+
// ============================================
|
|
99
|
+
|
|
100
|
+
export class SonaEngine {
|
|
101
|
+
private config: SonaConfig;
|
|
102
|
+
private pool: Pool;
|
|
103
|
+
|
|
104
|
+
constructor(config: Partial<SonaConfig> & { connectionString: string }) {
|
|
105
|
+
this.config = {
|
|
106
|
+
...PRESETS.moderate, // Default to moderate
|
|
107
|
+
...config
|
|
108
|
+
};
|
|
109
|
+
this.pool = new Pool({ connectionString: this.config.connectionString });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ------------------------------------------
|
|
113
|
+
// PATTERN STORAGE
|
|
114
|
+
// ------------------------------------------
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Store a successful pattern for future recall
|
|
118
|
+
*/
|
|
119
|
+
async storePattern(pattern: {
|
|
120
|
+
embedding: number[];
|
|
121
|
+
pattern: Record<string, unknown>;
|
|
122
|
+
quality: number;
|
|
123
|
+
namespace?: string;
|
|
124
|
+
}): Promise<string> {
|
|
125
|
+
// Only store if meets quality threshold
|
|
126
|
+
if (pattern.quality < this.config.pattern_quality_threshold) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const result = await this.pool.query(`
|
|
131
|
+
INSERT INTO reasoning_bank (
|
|
132
|
+
namespace, embedding, pattern_data, quality_score, created_at
|
|
133
|
+
) VALUES ($1, $2, $3, $4, NOW())
|
|
134
|
+
RETURNING id
|
|
135
|
+
`, [
|
|
136
|
+
pattern.namespace || 'default',
|
|
137
|
+
JSON.stringify(pattern.embedding),
|
|
138
|
+
JSON.stringify(pattern.pattern),
|
|
139
|
+
pattern.quality
|
|
140
|
+
]);
|
|
141
|
+
|
|
142
|
+
return result.rows[0].id;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Recall patterns similar to a profile/query
|
|
147
|
+
*/
|
|
148
|
+
async recallPatterns(
|
|
149
|
+
embedding: number[],
|
|
150
|
+
options: {
|
|
151
|
+
namespace?: string;
|
|
152
|
+
limit?: number;
|
|
153
|
+
minQuality?: number;
|
|
154
|
+
} = {}
|
|
155
|
+
): Promise<Array<{
|
|
156
|
+
id: string;
|
|
157
|
+
pattern: Record<string, unknown>;
|
|
158
|
+
quality: number;
|
|
159
|
+
similarity: number;
|
|
160
|
+
}>> {
|
|
161
|
+
const { namespace = 'default', limit = 10, minQuality = 0.5 } = options;
|
|
162
|
+
|
|
163
|
+
const result = await this.pool.query(`
|
|
164
|
+
SELECT id, pattern_data, quality_score,
|
|
165
|
+
1.0 / (1.0 + (embedding::vector <=> $1::vector)) as similarity
|
|
166
|
+
FROM reasoning_bank
|
|
167
|
+
WHERE namespace = $2
|
|
168
|
+
AND quality_score >= $3
|
|
169
|
+
ORDER BY embedding::vector <=> $1::vector
|
|
170
|
+
LIMIT $4
|
|
171
|
+
`, [
|
|
172
|
+
JSON.stringify(embedding),
|
|
173
|
+
namespace,
|
|
174
|
+
minQuality,
|
|
175
|
+
limit
|
|
176
|
+
]);
|
|
177
|
+
|
|
178
|
+
return result.rows.map(row => ({
|
|
179
|
+
id: row.id,
|
|
180
|
+
pattern: row.pattern_data,
|
|
181
|
+
quality: row.quality_score,
|
|
182
|
+
similarity: row.similarity
|
|
183
|
+
}));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ------------------------------------------
|
|
187
|
+
// TRAJECTORY TRACKING
|
|
188
|
+
// ------------------------------------------
|
|
189
|
+
|
|
190
|
+
private activeTrajectories = new Map<string, {
|
|
191
|
+
startTime: Date;
|
|
192
|
+
embedding: number[];
|
|
193
|
+
steps: Array<{ action: string; timestamp: Date }>;
|
|
194
|
+
}>();
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Start tracking a trajectory (for learning from outcomes)
|
|
198
|
+
*/
|
|
199
|
+
startTrajectory(id: string, embedding: number[]): void {
|
|
200
|
+
this.activeTrajectories.set(id, {
|
|
201
|
+
startTime: new Date(),
|
|
202
|
+
embedding,
|
|
203
|
+
steps: []
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// Auto-timeout
|
|
207
|
+
setTimeout(() => {
|
|
208
|
+
if (this.activeTrajectories.has(id)) {
|
|
209
|
+
this.endTrajectory(id, 0); // End with zero score if timed out
|
|
210
|
+
}
|
|
211
|
+
}, this.config.trajectory_timeout_seconds * 1000);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Add a step to an active trajectory
|
|
216
|
+
*/
|
|
217
|
+
addTrajectoryStep(id: string, action: string): void {
|
|
218
|
+
const trajectory = this.activeTrajectories.get(id);
|
|
219
|
+
if (trajectory) {
|
|
220
|
+
trajectory.steps.push({ action, timestamp: new Date() });
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* End a trajectory and optionally store as pattern
|
|
226
|
+
*/
|
|
227
|
+
async endTrajectory(id: string, outcomeScore: number): Promise<void> {
|
|
228
|
+
const trajectory = this.activeTrajectories.get(id);
|
|
229
|
+
if (!trajectory) return;
|
|
230
|
+
|
|
231
|
+
this.activeTrajectories.delete(id);
|
|
232
|
+
|
|
233
|
+
// Store as pattern if outcome was good
|
|
234
|
+
if (outcomeScore >= this.config.pattern_quality_threshold) {
|
|
235
|
+
await this.storePattern({
|
|
236
|
+
embedding: trajectory.embedding,
|
|
237
|
+
pattern: {
|
|
238
|
+
steps: trajectory.steps,
|
|
239
|
+
duration: Date.now() - trajectory.startTime.getTime(),
|
|
240
|
+
outcome: outcomeScore
|
|
241
|
+
},
|
|
242
|
+
quality: outcomeScore
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// ------------------------------------------
|
|
248
|
+
// HEALTH & DIAGNOSTICS
|
|
249
|
+
// ------------------------------------------
|
|
250
|
+
|
|
251
|
+
async getStats(namespace = 'default'): Promise<{
|
|
252
|
+
patternCount: number;
|
|
253
|
+
avgQuality: number;
|
|
254
|
+
clusterDistribution: Record<string, number>;
|
|
255
|
+
}> {
|
|
256
|
+
const result = await this.pool.query(`
|
|
257
|
+
SELECT
|
|
258
|
+
COUNT(*) as pattern_count,
|
|
259
|
+
AVG(quality_score) as avg_quality
|
|
260
|
+
FROM reasoning_bank
|
|
261
|
+
WHERE namespace = $1
|
|
262
|
+
`, [namespace]);
|
|
263
|
+
|
|
264
|
+
return {
|
|
265
|
+
patternCount: parseInt(result.rows[0].pattern_count),
|
|
266
|
+
avgQuality: parseFloat(result.rows[0].avg_quality) || 0,
|
|
267
|
+
clusterDistribution: {} // Would need clustering analysis
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// ============================================
|
|
273
|
+
// FACTORY FUNCTION
|
|
274
|
+
// ============================================
|
|
275
|
+
|
|
276
|
+
export function createSonaEngine(
|
|
277
|
+
domainType: 'stable' | 'moderate' | 'fastChanging',
|
|
278
|
+
connectionString: string,
|
|
279
|
+
overrides: Partial<SonaConfig> = {}
|
|
280
|
+
): SonaEngine {
|
|
281
|
+
return new SonaEngine({
|
|
282
|
+
...PRESETS[domainType],
|
|
283
|
+
...overrides,
|
|
284
|
+
connectionString
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// ============================================
|
|
289
|
+
// USAGE EXAMPLE
|
|
290
|
+
// ============================================
|
|
291
|
+
|
|
292
|
+
/*
|
|
293
|
+
// For a retirement planning app (stable domain):
|
|
294
|
+
const sona = createSonaEngine('stable', process.env.DATABASE_URL);
|
|
295
|
+
|
|
296
|
+
// Store a successful pattern
|
|
297
|
+
await sona.storePattern({
|
|
298
|
+
embedding: await embedProfile(userProfile),
|
|
299
|
+
pattern: {
|
|
300
|
+
profile: { age: 55, risk: 'moderate', goal: 'early_retirement' },
|
|
301
|
+
recommendation: 'delayed_ss_roth_conversion',
|
|
302
|
+
outcome: 'user_followed_85%'
|
|
303
|
+
},
|
|
304
|
+
quality: 0.85
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
// Recall for new user
|
|
308
|
+
const similar = await sona.recallPatterns(
|
|
309
|
+
await embedProfile(newUserProfile),
|
|
310
|
+
{ limit: 5, minQuality: 0.7 }
|
|
311
|
+
);
|
|
312
|
+
*/
|