rust-crate-pipeline 1.4.0__py3-none-any.whl → 1.4.1__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.
- rust_crate_pipeline/__init__.py +18 -27
- rust_crate_pipeline/__main__.py +1 -0
- rust_crate_pipeline/ai_processing.py +718 -596
- rust_crate_pipeline/analysis.py +330 -363
- rust_crate_pipeline/azure_ai_processing.py +462 -0
- rust_crate_pipeline/config.py +46 -28
- rust_crate_pipeline/core/__init__.py +19 -0
- rust_crate_pipeline/core/canon_registry.py +133 -0
- rust_crate_pipeline/core/irl_engine.py +256 -0
- rust_crate_pipeline/core/sacred_chain.py +117 -0
- rust_crate_pipeline/crate_analysis.py +54 -0
- rust_crate_pipeline/crate_list.txt +424 -0
- rust_crate_pipeline/github_token_checker.py +108 -112
- rust_crate_pipeline/main.py +329 -109
- rust_crate_pipeline/network.py +317 -308
- rust_crate_pipeline/pipeline.py +300 -375
- rust_crate_pipeline/production_config.py +24 -27
- rust_crate_pipeline/progress_monitor.py +334 -0
- rust_crate_pipeline/scraping/__init__.py +13 -0
- rust_crate_pipeline/scraping/unified_scraper.py +259 -0
- rust_crate_pipeline/unified_llm_processor.py +637 -0
- rust_crate_pipeline/unified_pipeline.py +548 -0
- rust_crate_pipeline/utils/file_utils.py +32 -5
- rust_crate_pipeline/utils/logging_utils.py +21 -16
- rust_crate_pipeline/version.py +76 -47
- rust_crate_pipeline-1.4.1.dist-info/METADATA +515 -0
- rust_crate_pipeline-1.4.1.dist-info/RECORD +31 -0
- rust_crate_pipeline-1.4.0.dist-info/METADATA +0 -585
- rust_crate_pipeline-1.4.0.dist-info/RECORD +0 -19
- {rust_crate_pipeline-1.4.0.dist-info → rust_crate_pipeline-1.4.1.dist-info}/WHEEL +0 -0
- {rust_crate_pipeline-1.4.0.dist-info → rust_crate_pipeline-1.4.1.dist-info}/entry_points.txt +0 -0
- {rust_crate_pipeline-1.4.0.dist-info → rust_crate_pipeline-1.4.1.dist-info}/licenses/LICENSE +0 -0
- {rust_crate_pipeline-1.4.0.dist-info → rust_crate_pipeline-1.4.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,133 @@
|
|
1
|
+
import hashlib
|
2
|
+
import logging
|
3
|
+
from datetime import datetime, timezone
|
4
|
+
from typing import Dict, List, Optional, Any
|
5
|
+
from dataclasses import dataclass
|
6
|
+
|
7
|
+
|
8
|
+
@dataclass
|
9
|
+
class CanonEntry:
|
10
|
+
source: str
|
11
|
+
version: str
|
12
|
+
authority_level: int
|
13
|
+
content_hash: str
|
14
|
+
last_validated: str
|
15
|
+
expiry: Optional[str] = None
|
16
|
+
|
17
|
+
def is_valid(self) -> bool:
|
18
|
+
if self.expiry:
|
19
|
+
expiry_time = datetime.fromisoformat(self.expiry)
|
20
|
+
return datetime.now(timezone.utc) < expiry_time
|
21
|
+
return True
|
22
|
+
|
23
|
+
|
24
|
+
class CanonRegistry:
|
25
|
+
|
26
|
+
def __init__(self) -> None:
|
27
|
+
self.canon_entries: Dict[str, CanonEntry] = {}
|
28
|
+
self.authority_chain: List[str] = []
|
29
|
+
self.version = "1.4.0"
|
30
|
+
self.logger = logging.getLogger(__name__)
|
31
|
+
|
32
|
+
self._initialize_default_canon()
|
33
|
+
|
34
|
+
def _initialize_default_canon(self) -> None:
|
35
|
+
default_sources = {
|
36
|
+
"crates.io": {
|
37
|
+
"authority_level": 10,
|
38
|
+
"base_url": "https://crates.io/api/v1/",
|
39
|
+
"version": "1.4.0",
|
40
|
+
"last_validated": datetime.now(timezone.utc).isoformat(),
|
41
|
+
},
|
42
|
+
"github.com": {
|
43
|
+
"authority_level": 8,
|
44
|
+
"base_url": "https://api.github.com/",
|
45
|
+
"version": "3.0",
|
46
|
+
"last_validated": datetime.now(timezone.utc).isoformat(),
|
47
|
+
},
|
48
|
+
"lib.rs": {
|
49
|
+
"authority_level": 6,
|
50
|
+
"base_url": "https://lib.rs/",
|
51
|
+
"version": "1.3.0",
|
52
|
+
"last_validated": datetime.now(timezone.utc).isoformat(),
|
53
|
+
},
|
54
|
+
"docs.rs": {
|
55
|
+
"authority_level": 7,
|
56
|
+
"base_url": "https://docs.rs/",
|
57
|
+
"version": "1.3.0",
|
58
|
+
"last_validated": datetime.now(timezone.utc).isoformat(),
|
59
|
+
},
|
60
|
+
}
|
61
|
+
|
62
|
+
for key, source_info in default_sources.items():
|
63
|
+
self.register_canon(
|
64
|
+
key=key,
|
65
|
+
source=source_info["base_url"],
|
66
|
+
content=f"Default Canon source: {key}",
|
67
|
+
authority_level=source_info["authority_level"]
|
68
|
+
)
|
69
|
+
|
70
|
+
def register_canon(
|
71
|
+
self, key: str, source: str, content: str, authority_level: int = 5
|
72
|
+
) -> bool:
|
73
|
+
try:
|
74
|
+
content_hash = hashlib.sha256(content.encode()).hexdigest()
|
75
|
+
timestamp = datetime.now(timezone.utc).isoformat()
|
76
|
+
|
77
|
+
canon_entry = CanonEntry(
|
78
|
+
source=source,
|
79
|
+
version=self.version,
|
80
|
+
authority_level=authority_level,
|
81
|
+
content_hash=content_hash,
|
82
|
+
last_validated=timestamp,
|
83
|
+
)
|
84
|
+
|
85
|
+
self.canon_entries[key] = canon_entry
|
86
|
+
self.authority_chain.append(f"{timestamp}:{key}:{authority_level}")
|
87
|
+
|
88
|
+
self.logger.info(f"Canon registered: {key} with authority {authority_level}")
|
89
|
+
return True
|
90
|
+
except Exception as e:
|
91
|
+
self.logger.error(f"Failed to register Canon {key}: {e}")
|
92
|
+
return False
|
93
|
+
|
94
|
+
def get_canon(self, key: str) -> Optional[CanonEntry]:
|
95
|
+
if key in self.canon_entries:
|
96
|
+
canon = self.canon_entries[key]
|
97
|
+
if canon.is_valid():
|
98
|
+
return canon
|
99
|
+
else:
|
100
|
+
self.logger.warning(f"Canon expired: {key}")
|
101
|
+
del self.canon_entries[key]
|
102
|
+
return None
|
103
|
+
|
104
|
+
def get_valid_canon_sources(self) -> List[str]:
|
105
|
+
valid_sources = []
|
106
|
+
for key, entry in self.canon_entries.items():
|
107
|
+
if entry.is_valid():
|
108
|
+
valid_sources.append(key)
|
109
|
+
return valid_sources
|
110
|
+
|
111
|
+
def get_authority_level(self, source: str) -> int:
|
112
|
+
canon = self.get_canon(source)
|
113
|
+
return canon.authority_level if canon else 0
|
114
|
+
|
115
|
+
def audit_trail(self) -> List[str]:
|
116
|
+
return self.authority_chain.copy()
|
117
|
+
|
118
|
+
def get_canon_summary(self) -> Dict[str, Any]:
|
119
|
+
valid_count = len(self.get_valid_canon_sources())
|
120
|
+
total_count = len(self.canon_entries)
|
121
|
+
|
122
|
+
authority_levels = {}
|
123
|
+
for key, entry in self.canon_entries.items():
|
124
|
+
level = entry.authority_level
|
125
|
+
authority_levels[level] = authority_levels.get(level, 0) + 1
|
126
|
+
|
127
|
+
return {
|
128
|
+
"total_canon_entries": total_count,
|
129
|
+
"valid_canon_entries": valid_count,
|
130
|
+
"authority_level_distribution": authority_levels,
|
131
|
+
"version": self.version,
|
132
|
+
"last_operation": self.authority_chain[-1] if self.authority_chain else None,
|
133
|
+
}
|
@@ -0,0 +1,256 @@
|
|
1
|
+
import json
|
2
|
+
import logging
|
3
|
+
import time
|
4
|
+
from typing import Dict, List, Optional, Any, Tuple
|
5
|
+
from abc import ABC, abstractmethod
|
6
|
+
|
7
|
+
from .sacred_chain import SacredChainBase, SacredChainTrace, TrustVerdict
|
8
|
+
from .canon_registry import CanonRegistry
|
9
|
+
|
10
|
+
|
11
|
+
class IRLEngine(SacredChainBase):
|
12
|
+
|
13
|
+
def __init__(self, config: Any, canon_registry: Optional[CanonRegistry] = None) -> None:
|
14
|
+
super().__init__()
|
15
|
+
self.config = config
|
16
|
+
self.canon_registry = canon_registry or CanonRegistry()
|
17
|
+
self.crawler: Optional[Any] = None
|
18
|
+
self.logger = logging.getLogger(__name__)
|
19
|
+
|
20
|
+
async def __aenter__(self) -> "IRLEngine":
|
21
|
+
try:
|
22
|
+
from crawl4ai import AsyncWebCrawler, BrowserConfig
|
23
|
+
browser_config = BrowserConfig(headless=True, browser_type="chromium")
|
24
|
+
self.crawler = AsyncWebCrawler(config=browser_config)
|
25
|
+
await self.crawler.start()
|
26
|
+
self.logger.info("IRL Engine initialized with full traceability")
|
27
|
+
except ImportError:
|
28
|
+
self.logger.warning("Crawl4AI not available - IRL Engine running in limited mode")
|
29
|
+
except Exception as e:
|
30
|
+
self.logger.warning(f"Failed to initialize crawler: {e}")
|
31
|
+
|
32
|
+
return self
|
33
|
+
|
34
|
+
async def __aexit__(self, exc_type: Optional[type], exc_val: Optional[Exception], exc_tb: Optional[Any]) -> None:
|
35
|
+
if self.crawler:
|
36
|
+
try:
|
37
|
+
await self.crawler.stop()
|
38
|
+
except Exception as e:
|
39
|
+
self.logger.warning(f"Error stopping crawler: {e}")
|
40
|
+
self._finalize_audit_log()
|
41
|
+
|
42
|
+
def _finalize_audit_log(self) -> None:
|
43
|
+
if not self.execution_log:
|
44
|
+
return
|
45
|
+
|
46
|
+
audit_file = f"sigil_audit_{int(time.time())}.json"
|
47
|
+
try:
|
48
|
+
with open(audit_file, "w") as f:
|
49
|
+
audit_data = [json.loads(trace.to_audit_log()) for trace in self.execution_log]
|
50
|
+
json.dump(audit_data, f, indent=2)
|
51
|
+
self.logger.info(f"Audit log finalized: {audit_file}")
|
52
|
+
except IOError as e:
|
53
|
+
self.logger.error(f"Failed to write audit log {audit_file}: {e}")
|
54
|
+
|
55
|
+
async def analyze_with_sacred_chain(self, input_data: str) -> SacredChainTrace:
|
56
|
+
canonical_input = self._canonicalize_input(input_data)
|
57
|
+
reasoning_steps = [f"Input canonicalized: '{input_data}' -> '{canonical_input}'"]
|
58
|
+
|
59
|
+
context_sources = await self._gather_validated_context(canonical_input)
|
60
|
+
reasoning_steps.append(f"Context gathered from {len(context_sources)} validated sources")
|
61
|
+
|
62
|
+
analysis_results = await self._execute_reasoning_chain(canonical_input, context_sources)
|
63
|
+
reasoning_steps.extend(analysis_results[0])
|
64
|
+
|
65
|
+
suggestion = self._generate_traceable_suggestion(reasoning_steps)
|
66
|
+
verdict, verdict_reason = self._make_trust_decision(
|
67
|
+
reasoning_steps, suggestion, analysis_results[5],
|
68
|
+
analysis_results[1],
|
69
|
+
analysis_results[2],
|
70
|
+
analysis_results[3],
|
71
|
+
)
|
72
|
+
reasoning_steps.append(f"Trust decision: {verdict} - {verdict_reason}")
|
73
|
+
|
74
|
+
irl_score = self._calculate_irl_score(context_sources, reasoning_steps, verdict)
|
75
|
+
reasoning_steps.append(f"IRL confidence: {irl_score:.3f}")
|
76
|
+
|
77
|
+
audit_info = {
|
78
|
+
"metadata": analysis_results[1],
|
79
|
+
"sentiment": analysis_results[2],
|
80
|
+
"ecosystem": analysis_results[3],
|
81
|
+
"quality_score": analysis_results[5],
|
82
|
+
"verdict_reason": verdict_reason,
|
83
|
+
}
|
84
|
+
|
85
|
+
return self.create_sacred_chain_trace(
|
86
|
+
input_data=canonical_input,
|
87
|
+
context_sources=context_sources,
|
88
|
+
reasoning_steps=reasoning_steps,
|
89
|
+
suggestion=suggestion,
|
90
|
+
verdict=verdict,
|
91
|
+
audit_info=audit_info,
|
92
|
+
irl_score=irl_score,
|
93
|
+
)
|
94
|
+
|
95
|
+
def _canonicalize_input(self, input_data: str) -> str:
|
96
|
+
canonical = input_data.strip().lower()
|
97
|
+
if canonical.startswith("crate:"):
|
98
|
+
canonical = canonical[6:]
|
99
|
+
if canonical.startswith("rust:"):
|
100
|
+
canonical = canonical[5:]
|
101
|
+
return canonical
|
102
|
+
|
103
|
+
async def _gather_validated_context(self, input_data: str) -> List[str]:
|
104
|
+
valid_sources = self.canon_registry.get_valid_canon_sources()
|
105
|
+
context_sources = []
|
106
|
+
|
107
|
+
for source in valid_sources:
|
108
|
+
authority_level = self.canon_registry.get_authority_level(source)
|
109
|
+
if authority_level >= 5:
|
110
|
+
context_sources.append(source)
|
111
|
+
|
112
|
+
return context_sources
|
113
|
+
|
114
|
+
async def _execute_reasoning_chain(
|
115
|
+
self, input_data: str, sources: List[str]
|
116
|
+
) -> Tuple[List[str], Dict[str, Any], Dict[str, Any], Dict[str, Any], Dict[str, Any], float]:
|
117
|
+
reasoning_steps = []
|
118
|
+
|
119
|
+
metadata = await self._extract_basic_metadata(input_data)
|
120
|
+
reasoning_steps.append(f"Metadata extracted: {len(metadata)} fields")
|
121
|
+
|
122
|
+
docs = {}
|
123
|
+
if self.crawler:
|
124
|
+
docs = await self._analyze_documentation(input_data)
|
125
|
+
reasoning_steps.append(f"Documentation analyzed: quality {docs.get('quality_score', 0):.1f}")
|
126
|
+
|
127
|
+
sentiment = await self._analyze_community_sentiment(input_data)
|
128
|
+
reasoning_steps.append(f"Sentiment analyzed: {sentiment.get('overall', 'unknown')}")
|
129
|
+
|
130
|
+
ecosystem = await self._analyze_ecosystem_position(input_data)
|
131
|
+
reasoning_steps.append(f"Ecosystem analyzed: {ecosystem.get('category', 'unknown')}")
|
132
|
+
|
133
|
+
quality_score = self._synthesize_quality_score(metadata, docs, sentiment, ecosystem)
|
134
|
+
reasoning_steps.append(f"Quality score synthesized: {quality_score:.2f}")
|
135
|
+
|
136
|
+
return reasoning_steps, metadata, docs, sentiment, ecosystem, quality_score
|
137
|
+
|
138
|
+
async def _extract_basic_metadata(self, input_data: str) -> Dict[str, Any]:
|
139
|
+
return {
|
140
|
+
"name": input_data,
|
141
|
+
"type": "rust_crate",
|
142
|
+
"source": "manual_input",
|
143
|
+
"extraction_method": "irl_engine",
|
144
|
+
}
|
145
|
+
|
146
|
+
async def _analyze_documentation(self, input_data: str) -> Dict[str, Any]:
|
147
|
+
if not self.crawler:
|
148
|
+
return {"quality_score": 5.0, "error": "No crawler available"}
|
149
|
+
|
150
|
+
try:
|
151
|
+
return {
|
152
|
+
"quality_score": 7.0,
|
153
|
+
"completeness": 0.8,
|
154
|
+
"examples_present": True,
|
155
|
+
"api_documented": True,
|
156
|
+
}
|
157
|
+
except Exception as e:
|
158
|
+
self.logger.error(f"Documentation analysis failed: {e}")
|
159
|
+
return {"quality_score": 5.0, "error": str(e)}
|
160
|
+
|
161
|
+
async def _analyze_community_sentiment(self, input_data: str) -> Dict[str, Any]:
|
162
|
+
return {
|
163
|
+
"overall": "positive",
|
164
|
+
"positive_mentions": 10,
|
165
|
+
"negative_mentions": 2,
|
166
|
+
"neutral_mentions": 5,
|
167
|
+
"total_mentions": 17,
|
168
|
+
}
|
169
|
+
|
170
|
+
async def _analyze_ecosystem_position(self, input_data: str) -> Dict[str, Any]:
|
171
|
+
return {
|
172
|
+
"category": "utilities",
|
173
|
+
"maturity": "stable",
|
174
|
+
"dependencies_count": 5,
|
175
|
+
"reverse_deps_visible": 15,
|
176
|
+
"ecosystem_score": 7.5,
|
177
|
+
}
|
178
|
+
|
179
|
+
def _synthesize_quality_score(
|
180
|
+
self,
|
181
|
+
metadata: Dict[str, Any],
|
182
|
+
docs: Dict[str, Any],
|
183
|
+
sentiment: Dict[str, Any],
|
184
|
+
ecosystem: Dict[str, Any],
|
185
|
+
) -> float:
|
186
|
+
scores = []
|
187
|
+
|
188
|
+
doc_score = docs.get("quality_score", 5.0)
|
189
|
+
scores.append(doc_score)
|
190
|
+
|
191
|
+
sentiment_score = 5.0
|
192
|
+
if sentiment.get("overall") == "positive":
|
193
|
+
sentiment_score = 8.0
|
194
|
+
elif sentiment.get("overall") == "negative":
|
195
|
+
sentiment_score = 3.0
|
196
|
+
scores.append(sentiment_score)
|
197
|
+
|
198
|
+
ecosystem_score = ecosystem.get("ecosystem_score", 5.0)
|
199
|
+
scores.append(ecosystem_score)
|
200
|
+
|
201
|
+
return sum(scores) / len(scores) if scores else 5.0
|
202
|
+
|
203
|
+
def _generate_traceable_suggestion(self, reasoning_steps: List[str]) -> str:
|
204
|
+
if not reasoning_steps:
|
205
|
+
return "DEFER: Insufficient reasoning data"
|
206
|
+
|
207
|
+
quality_indicators = [step for step in reasoning_steps if "quality" in step.lower()]
|
208
|
+
sentiment_indicators = [step for step in reasoning_steps if "sentiment" in step.lower()]
|
209
|
+
|
210
|
+
if quality_indicators and any("high" in indicator.lower() for indicator in quality_indicators):
|
211
|
+
return "ALLOW: High quality indicators detected"
|
212
|
+
elif sentiment_indicators and any("positive" in indicator.lower() for indicator in sentiment_indicators):
|
213
|
+
return "ALLOW: Positive community sentiment"
|
214
|
+
else:
|
215
|
+
return "DEFER: Requires additional analysis"
|
216
|
+
|
217
|
+
def _make_trust_decision(
|
218
|
+
self,
|
219
|
+
reasoning_steps: List[str],
|
220
|
+
suggestion: str,
|
221
|
+
quality_score: float,
|
222
|
+
docs: Dict[str, Any],
|
223
|
+
sentiment: Dict[str, Any],
|
224
|
+
ecosystem: Dict[str, Any],
|
225
|
+
) -> Tuple[TrustVerdict, str]:
|
226
|
+
if quality_score >= 8.0:
|
227
|
+
return TrustVerdict.ALLOW, "High quality score"
|
228
|
+
elif quality_score >= 6.0 and sentiment.get("overall") == "positive":
|
229
|
+
return TrustVerdict.ALLOW, "Good quality with positive sentiment"
|
230
|
+
elif quality_score < 4.0:
|
231
|
+
return TrustVerdict.DENY, "Low quality score"
|
232
|
+
elif sentiment.get("overall") == "negative":
|
233
|
+
return TrustVerdict.FLAG, "Negative community sentiment"
|
234
|
+
else:
|
235
|
+
return TrustVerdict.DEFER, "Insufficient data for decision"
|
236
|
+
|
237
|
+
def _calculate_irl_score(
|
238
|
+
self,
|
239
|
+
context_sources: List[str],
|
240
|
+
reasoning_steps: List[str],
|
241
|
+
verdict: TrustVerdict,
|
242
|
+
) -> float:
|
243
|
+
base_score = 5.0
|
244
|
+
|
245
|
+
authority_bonus = sum(self.canon_registry.get_authority_level(source) for source in context_sources) / 10.0
|
246
|
+
base_score += min(authority_bonus, 2.0)
|
247
|
+
|
248
|
+
reasoning_bonus = min(len(reasoning_steps) * 0.2, 2.0)
|
249
|
+
base_score += reasoning_bonus
|
250
|
+
|
251
|
+
if verdict == TrustVerdict.ALLOW:
|
252
|
+
base_score += 1.0
|
253
|
+
elif verdict == TrustVerdict.DENY:
|
254
|
+
base_score += 0.5
|
255
|
+
|
256
|
+
return min(base_score, 10.0)
|
@@ -0,0 +1,117 @@
|
|
1
|
+
import json
|
2
|
+
import hashlib
|
3
|
+
import uuid
|
4
|
+
from datetime import datetime, timezone
|
5
|
+
from typing import Dict, List, Optional, Any
|
6
|
+
from dataclasses import dataclass, asdict
|
7
|
+
from enum import Enum
|
8
|
+
from abc import ABC, abstractmethod
|
9
|
+
|
10
|
+
|
11
|
+
class TrustVerdict(Enum):
|
12
|
+
ALLOW = "ALLOW"
|
13
|
+
DENY = "DENY"
|
14
|
+
DEFER = "DEFER"
|
15
|
+
FLAG = "FLAG"
|
16
|
+
|
17
|
+
def __str__(self) -> str:
|
18
|
+
return self.value
|
19
|
+
|
20
|
+
def to_json(self) -> str:
|
21
|
+
return self.value
|
22
|
+
|
23
|
+
|
24
|
+
@dataclass
|
25
|
+
class SacredChainTrace:
|
26
|
+
input_data: str
|
27
|
+
context_sources: List[str]
|
28
|
+
reasoning_steps: List[str]
|
29
|
+
suggestion: str
|
30
|
+
verdict: TrustVerdict
|
31
|
+
audit_info: Dict[str, Any]
|
32
|
+
irl_score: float
|
33
|
+
execution_id: str
|
34
|
+
timestamp: str
|
35
|
+
canon_version: str
|
36
|
+
|
37
|
+
def to_audit_log(self) -> str:
|
38
|
+
data_dict = asdict(self)
|
39
|
+
data_dict["verdict"] = self.verdict.value
|
40
|
+
|
41
|
+
return json.dumps({
|
42
|
+
"execution_id": self.execution_id,
|
43
|
+
"timestamp": self.timestamp,
|
44
|
+
"sacred_chain": data_dict,
|
45
|
+
"rule_zero_compliant": True,
|
46
|
+
}, indent=2)
|
47
|
+
|
48
|
+
def verify_integrity(self) -> bool:
|
49
|
+
chain_data = f"{self.input_data}{self.context_sources}{self.reasoning_steps}{self.suggestion}"
|
50
|
+
expected_hash = hashlib.sha256(chain_data.encode()).hexdigest()[:16]
|
51
|
+
return expected_hash in self.execution_id
|
52
|
+
|
53
|
+
|
54
|
+
class SacredChainBase(ABC):
|
55
|
+
|
56
|
+
def __init__(self) -> None:
|
57
|
+
self.execution_log: List[SacredChainTrace] = []
|
58
|
+
self.canon_version = "1.3.0"
|
59
|
+
|
60
|
+
def generate_execution_id(self, input_data: str) -> str:
|
61
|
+
timestamp = datetime.now(timezone.utc).isoformat()
|
62
|
+
data_hash = hashlib.sha256(input_data.encode()).hexdigest()[:8]
|
63
|
+
unique_id = uuid.uuid4().hex[:8]
|
64
|
+
return f"exec-{data_hash}-{unique_id}-{int(datetime.now().timestamp())}"
|
65
|
+
|
66
|
+
def create_sacred_chain_trace(
|
67
|
+
self,
|
68
|
+
input_data: str,
|
69
|
+
context_sources: List[str],
|
70
|
+
reasoning_steps: List[str],
|
71
|
+
suggestion: str,
|
72
|
+
verdict: TrustVerdict,
|
73
|
+
audit_info: Dict[str, Any],
|
74
|
+
irl_score: float,
|
75
|
+
) -> SacredChainTrace:
|
76
|
+
execution_id = self.generate_execution_id(input_data)
|
77
|
+
timestamp = datetime.now(timezone.utc).isoformat()
|
78
|
+
|
79
|
+
trace = SacredChainTrace(
|
80
|
+
input_data=input_data,
|
81
|
+
context_sources=context_sources,
|
82
|
+
reasoning_steps=reasoning_steps,
|
83
|
+
suggestion=suggestion,
|
84
|
+
verdict=verdict,
|
85
|
+
audit_info=audit_info,
|
86
|
+
irl_score=irl_score,
|
87
|
+
execution_id=execution_id,
|
88
|
+
timestamp=timestamp,
|
89
|
+
canon_version=self.canon_version,
|
90
|
+
)
|
91
|
+
|
92
|
+
self.execution_log.append(trace)
|
93
|
+
return trace
|
94
|
+
|
95
|
+
@abstractmethod
|
96
|
+
async def analyze_with_sacred_chain(self, input_data: str) -> SacredChainTrace:
|
97
|
+
pass
|
98
|
+
|
99
|
+
def get_audit_summary(self) -> Dict[str, Any]:
|
100
|
+
if not self.execution_log:
|
101
|
+
return {"total_executions": 0, "verdicts": {}, "average_irl_score": 0.0}
|
102
|
+
|
103
|
+
verdict_counts = {}
|
104
|
+
total_irl_score = 0.0
|
105
|
+
|
106
|
+
for trace in self.execution_log:
|
107
|
+
verdict = trace.verdict.value
|
108
|
+
verdict_counts[verdict] = verdict_counts.get(verdict, 0) + 1
|
109
|
+
total_irl_score += trace.irl_score
|
110
|
+
|
111
|
+
return {
|
112
|
+
"total_executions": len(self.execution_log),
|
113
|
+
"verdicts": verdict_counts,
|
114
|
+
"average_irl_score": total_irl_score / len(self.execution_log),
|
115
|
+
"canon_version": self.canon_version,
|
116
|
+
"last_execution": self.execution_log[-1].timestamp if self.execution_log else None,
|
117
|
+
}
|
@@ -0,0 +1,54 @@
|
|
1
|
+
import subprocess
|
2
|
+
import tempfile
|
3
|
+
import shutil
|
4
|
+
import os
|
5
|
+
import toml
|
6
|
+
from typing import Dict, Any, Optional
|
7
|
+
|
8
|
+
class CrateAnalyzer:
|
9
|
+
def __init__(self, crate_source_path: str):
|
10
|
+
self.crate_source_path = crate_source_path
|
11
|
+
|
12
|
+
def run_cargo_cmd(self, cmd, timeout=600) -> Dict[str, Any]:
|
13
|
+
try:
|
14
|
+
result = subprocess.run(
|
15
|
+
cmd,
|
16
|
+
cwd=self.crate_source_path,
|
17
|
+
capture_output=True,
|
18
|
+
text=True,
|
19
|
+
timeout=timeout
|
20
|
+
)
|
21
|
+
return {
|
22
|
+
"cmd": " ".join(cmd),
|
23
|
+
"returncode": result.returncode,
|
24
|
+
"stdout": result.stdout,
|
25
|
+
"stderr": result.stderr,
|
26
|
+
}
|
27
|
+
except Exception as e:
|
28
|
+
return {"cmd": " ".join(cmd), "error": str(e)}
|
29
|
+
|
30
|
+
def analyze(self) -> Dict[str, Any]:
|
31
|
+
results = {}
|
32
|
+
# Build & test
|
33
|
+
results["build"] = self.run_cargo_cmd(["cargo", "build", "--all-features"])
|
34
|
+
results["test"] = self.run_cargo_cmd(["cargo", "test", "--all-features"])
|
35
|
+
# Lint & format
|
36
|
+
results["clippy"] = self.run_cargo_cmd(["cargo", "clippy", "--all-features", "--", "-D", "warnings"])
|
37
|
+
results["fmt"] = self.run_cargo_cmd(["cargo", "fmt", "--", "--check"])
|
38
|
+
# Security
|
39
|
+
results["audit"] = self.run_cargo_cmd(["cargo", "audit"])
|
40
|
+
# Dependency graph
|
41
|
+
results["tree"] = self.run_cargo_cmd(["cargo", "tree"])
|
42
|
+
# Documentation
|
43
|
+
results["doc"] = self.run_cargo_cmd(["cargo", "doc", "--no-deps"])
|
44
|
+
# Provenance
|
45
|
+
vcs_info_path = os.path.join(self.crate_source_path, ".cargo_vcs_info.json")
|
46
|
+
if os.path.exists(vcs_info_path):
|
47
|
+
with open(vcs_info_path) as f:
|
48
|
+
results["vcs_info"] = f.read()
|
49
|
+
# Metadata
|
50
|
+
cargo_toml = os.path.join(self.crate_source_path, "Cargo.toml")
|
51
|
+
if os.path.exists(cargo_toml):
|
52
|
+
with open(cargo_toml) as f:
|
53
|
+
results["metadata"] = toml.load(f)
|
54
|
+
return results
|