iflow-mcp_hulupeep_ruvscan-mcp 0.5.0__py3-none-any.whl

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.
mcp/storage/db.py ADDED
@@ -0,0 +1,216 @@
1
+ """
2
+ Database layer for RuvScan
3
+ Handles SQLite storage for repos, leverage cards, and FACT cache
4
+ """
5
+
6
+ import sqlite3
7
+ import json
8
+ from typing import Optional, List, Dict, Any
9
+ from datetime import datetime
10
+ import hashlib
11
+ import logging
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+ class RuvScanDB:
16
+ """SQLite database manager for RuvScan"""
17
+
18
+ def __init__(self, db_path: str = "data/ruvscan.db"):
19
+ self.db_path = db_path
20
+ self.conn = None
21
+ self._init_db()
22
+
23
+ def _init_db(self):
24
+ """Initialize database connection and create tables"""
25
+ self.conn = sqlite3.connect(self.db_path, check_same_thread=False)
26
+ self.conn.row_factory = sqlite3.Row
27
+ self._create_tables()
28
+
29
+ def _create_tables(self):
30
+ """Create database schema"""
31
+ cursor = self.conn.cursor()
32
+
33
+ # Repos table
34
+ cursor.execute("""
35
+ CREATE TABLE IF NOT EXISTS repos (
36
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
37
+ name TEXT NOT NULL,
38
+ org TEXT NOT NULL,
39
+ full_name TEXT UNIQUE NOT NULL,
40
+ description TEXT,
41
+ topics TEXT,
42
+ readme TEXT,
43
+ embedding BLOB,
44
+ sublinear_hash TEXT,
45
+ stars INTEGER DEFAULT 0,
46
+ language TEXT,
47
+ last_scan TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
48
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
49
+ UNIQUE(org, name)
50
+ )
51
+ """)
52
+
53
+ # Leverage cards table
54
+ cursor.execute("""
55
+ CREATE TABLE IF NOT EXISTS leverage_cards (
56
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
57
+ repo_id INTEGER NOT NULL,
58
+ capabilities TEXT NOT NULL,
59
+ summary TEXT NOT NULL,
60
+ reasoning TEXT NOT NULL,
61
+ integration_hint TEXT,
62
+ relevance_score REAL NOT NULL,
63
+ runtime_complexity TEXT,
64
+ query_intent TEXT,
65
+ cached BOOLEAN DEFAULT 1,
66
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
67
+ FOREIGN KEY (repo_id) REFERENCES repos(id)
68
+ )
69
+ """)
70
+
71
+ # FACT cache table
72
+ cursor.execute("""
73
+ CREATE TABLE IF NOT EXISTS fact_cache (
74
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
75
+ hash TEXT UNIQUE NOT NULL,
76
+ prompt TEXT NOT NULL,
77
+ response TEXT NOT NULL,
78
+ version TEXT DEFAULT '0.5.0',
79
+ metadata TEXT,
80
+ timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
81
+ )
82
+ """)
83
+
84
+ # Create indexes
85
+ cursor.execute("CREATE INDEX IF NOT EXISTS idx_repos_org ON repos(org)")
86
+ cursor.execute("CREATE INDEX IF NOT EXISTS idx_repos_full_name ON repos(full_name)")
87
+ cursor.execute("CREATE INDEX IF NOT EXISTS idx_cards_repo ON leverage_cards(repo_id)")
88
+ cursor.execute("CREATE INDEX IF NOT EXISTS idx_cards_score ON leverage_cards(relevance_score)")
89
+ cursor.execute("CREATE INDEX IF NOT EXISTS idx_fact_hash ON fact_cache(hash)")
90
+
91
+ self.conn.commit()
92
+ logger.info("Database tables created successfully")
93
+
94
+ def add_repo(self, repo_data: Dict[str, Any]) -> int:
95
+ """Add or update repository"""
96
+ cursor = self.conn.cursor()
97
+
98
+ cursor.execute("""
99
+ INSERT OR REPLACE INTO repos
100
+ (name, org, full_name, description, topics, readme, stars, language, last_scan)
101
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
102
+ """, (
103
+ repo_data.get('name'),
104
+ repo_data.get('org'),
105
+ repo_data.get('full_name'),
106
+ repo_data.get('description'),
107
+ json.dumps(repo_data.get('topics', [])),
108
+ repo_data.get('readme'),
109
+ repo_data.get('stars', 0),
110
+ repo_data.get('language'),
111
+ datetime.utcnow()
112
+ ))
113
+
114
+ self.conn.commit()
115
+ return cursor.lastrowid
116
+
117
+ def get_repo(self, full_name: str) -> Optional[Dict[str, Any]]:
118
+ """Get repository by full name"""
119
+ cursor = self.conn.cursor()
120
+ cursor.execute("SELECT * FROM repos WHERE full_name = ?", (full_name,))
121
+ row = cursor.fetchone()
122
+
123
+ if row:
124
+ return dict(row)
125
+ return None
126
+
127
+ def add_leverage_card(self, card_data: Dict[str, Any]) -> int:
128
+ """Add leverage card"""
129
+ cursor = self.conn.cursor()
130
+
131
+ cursor.execute("""
132
+ INSERT INTO leverage_cards
133
+ (repo_id, capabilities, summary, reasoning, integration_hint,
134
+ relevance_score, runtime_complexity, query_intent)
135
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
136
+ """, (
137
+ card_data.get('repo_id'),
138
+ json.dumps(card_data.get('capabilities', [])),
139
+ card_data.get('summary'),
140
+ card_data.get('reasoning'),
141
+ card_data.get('integration_hint'),
142
+ card_data.get('relevance_score'),
143
+ card_data.get('runtime_complexity'),
144
+ card_data.get('query_intent')
145
+ ))
146
+
147
+ self.conn.commit()
148
+ return cursor.lastrowid
149
+
150
+ def get_leverage_cards(
151
+ self,
152
+ limit: int = 50,
153
+ min_score: float = 0.0,
154
+ cached_only: bool = False
155
+ ) -> List[Dict[str, Any]]:
156
+ """Get leverage cards with filters"""
157
+ cursor = self.conn.cursor()
158
+
159
+ query = """
160
+ SELECT lc.*, r.full_name as repo
161
+ FROM leverage_cards lc
162
+ JOIN repos r ON lc.repo_id = r.id
163
+ WHERE lc.relevance_score >= ?
164
+ """
165
+ params = [min_score]
166
+
167
+ if cached_only:
168
+ query += " AND lc.cached = 1"
169
+
170
+ query += " ORDER BY lc.relevance_score DESC, lc.created_at DESC LIMIT ?"
171
+ params.append(limit)
172
+
173
+ cursor.execute(query, params)
174
+ rows = cursor.fetchall()
175
+
176
+ return [dict(row) for row in rows]
177
+
178
+ def add_fact_cache(self, prompt: str, response: str, metadata: Optional[Dict] = None) -> str:
179
+ """Add entry to FACT cache"""
180
+ # Generate deterministic hash
181
+ cache_hash = hashlib.sha256(prompt.encode()).hexdigest()
182
+
183
+ cursor = self.conn.cursor()
184
+ cursor.execute("""
185
+ INSERT OR REPLACE INTO fact_cache
186
+ (hash, prompt, response, metadata)
187
+ VALUES (?, ?, ?, ?)
188
+ """, (
189
+ cache_hash,
190
+ prompt,
191
+ response,
192
+ json.dumps(metadata) if metadata else None
193
+ ))
194
+
195
+ self.conn.commit()
196
+ return cache_hash
197
+
198
+ def get_fact_cache(self, prompt: str) -> Optional[Dict[str, Any]]:
199
+ """Get cached response from FACT"""
200
+ cache_hash = hashlib.sha256(prompt.encode()).hexdigest()
201
+
202
+ cursor = self.conn.cursor()
203
+ cursor.execute("SELECT * FROM fact_cache WHERE hash = ?", (cache_hash,))
204
+ row = cursor.fetchone()
205
+
206
+ if row:
207
+ result = dict(row)
208
+ if result.get('metadata'):
209
+ result['metadata'] = json.loads(result['metadata'])
210
+ return result
211
+ return None
212
+
213
+ def close(self):
214
+ """Close database connection"""
215
+ if self.conn:
216
+ self.conn.close()
mcp/storage/models.py ADDED
@@ -0,0 +1,77 @@
1
+ """
2
+ Pydantic models for RuvScan data structures
3
+ """
4
+
5
+ from pydantic import BaseModel, Field
6
+ from typing import List, Optional, Dict, Any
7
+ from datetime import datetime
8
+
9
+ class Repository(BaseModel):
10
+ """Repository model"""
11
+ id: Optional[int] = None
12
+ name: str
13
+ org: str
14
+ full_name: str
15
+ description: Optional[str] = None
16
+ topics: List[str] = []
17
+ readme: Optional[str] = None
18
+ embedding: Optional[bytes] = None
19
+ sublinear_hash: Optional[str] = None
20
+ stars: int = 0
21
+ language: Optional[str] = None
22
+ last_scan: Optional[datetime] = None
23
+ created_at: Optional[datetime] = None
24
+
25
+ class LeverageCard(BaseModel):
26
+ """Leverage card model"""
27
+ id: Optional[int] = None
28
+ repo_id: int
29
+ repo: Optional[str] = None
30
+ capabilities: List[str]
31
+ summary: str
32
+ reasoning: str = Field(..., alias='outside_box_reasoning')
33
+ integration_hint: Optional[str] = None
34
+ relevance_score: float = Field(..., ge=0.0, le=1.0)
35
+ runtime_complexity: Optional[str] = None
36
+ query_intent: Optional[str] = None
37
+ cached: bool = True
38
+ created_at: Optional[datetime] = None
39
+
40
+ class Config:
41
+ populate_by_name = True
42
+
43
+ class FACTCacheEntry(BaseModel):
44
+ """FACT cache entry model"""
45
+ id: Optional[int] = None
46
+ hash: str
47
+ prompt: str
48
+ response: str
49
+ version: str = "0.5.0"
50
+ metadata: Optional[Dict[str, Any]] = None
51
+ timestamp: Optional[datetime] = None
52
+
53
+ class ScanJob(BaseModel):
54
+ """Scan job configuration"""
55
+ source_type: str = Field(..., pattern="^(org|user|topic)$")
56
+ source_name: str
57
+ limit: int = Field(50, gt=0, le=1000)
58
+ status: str = "pending"
59
+ repos_found: int = 0
60
+ repos_processed: int = 0
61
+
62
+ class SublinearComparison(BaseModel):
63
+ """Result of sublinear comparison"""
64
+ repo_a: str
65
+ repo_b: str
66
+ similarity_score: float = Field(..., ge=0.0, le=1.0)
67
+ complexity: str = "O(log n)"
68
+ method_used: str = "sublinear_neumann"
69
+ computation_time_ms: Optional[float] = None
70
+
71
+ class ReasoningTrace(BaseModel):
72
+ """Reasoning trace for FACT replay"""
73
+ repo: str
74
+ steps: List[Dict[str, Any]] = []
75
+ final_reasoning: str
76
+ cached: bool = False
77
+ confidence_score: float = Field(..., ge=0.0, le=1.0)