05ed037fe8
- DNS: pilot.landvex.com -> 16.170.83.169 - TLS: Let's Encrypt certificate (expires 2026-09-30) - Nginx: reverse proxy with SSL termination - API: https://pilot.landvex.com/api/v1/missions - UI: https://pilot.landvex.com/ - Upload: POST /api/v1/missions/import (multipart/form-data) Verified: ✅ https://pilot.landvex.com/health ✅ https://pilot.landvex.com/version ✅ https://pilot.landvex.com/api/v1/missions (list) ✅ https://pilot.landvex.com/api/v1/missions/:id (get) ✅ POST /api/v1/missions/import (video upload) ✅ UI loads with title 'LandveX Intelligence Lab' Next: Pilot 001 — Break the system!
187 lines
5.4 KiB
JavaScript
187 lines
5.4 KiB
JavaScript
#!/usr/bin/env node
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
// Runtime Trust Score
|
|
// Mäter hur mycket vi kan lita på Agent Runtime
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
|
|
class RuntimeTrustScore {
|
|
constructor() {
|
|
this.metrics = {
|
|
acceptancePassRate: 0,
|
|
policyEnforcement: 0,
|
|
determinism: 0,
|
|
reproducibility: 0,
|
|
uncertaintyHandling: 0,
|
|
falsePositives: 0,
|
|
falseNegatives: 0,
|
|
drift: 0
|
|
};
|
|
|
|
this.weights = {
|
|
acceptancePassRate: 0.20,
|
|
policyEnforcement: 0.20,
|
|
determinism: 0.15,
|
|
reproducibility: 0.15,
|
|
uncertaintyHandling: 0.10,
|
|
falsePositives: 0.05,
|
|
falseNegatives: 0.05,
|
|
drift: 0.10
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Uppdatera metric från testresultat
|
|
*/
|
|
updateFromTests(testResults) {
|
|
// Acceptance Pass Rate
|
|
const total = testResults.length;
|
|
const passed = testResults.filter(r => r.passed).length;
|
|
this.metrics.acceptancePassRate = total > 0 ? (passed / total) : 0;
|
|
|
|
// Policy Enforcement
|
|
const safetyTests = testResults.filter(r => r.category === 'Safety');
|
|
const safetyPassed = safetyTests.filter(r => r.passed).length;
|
|
this.metrics.policyEnforcement = safetyTests.length > 0 ?
|
|
(safetyPassed / safetyTests.length) : 0;
|
|
|
|
// Determinism
|
|
const reproTests = testResults.filter(r => r.category === 'Reproducibility');
|
|
const reproPassed = reproTests.filter(r => r.passed).length;
|
|
this.metrics.determinism = reproTests.length > 0 ?
|
|
(reproPassed / reproTests.length) : 0;
|
|
|
|
// False Positives (blockerade som borde gått igenom)
|
|
const blockedHappyPath = testResults.filter(r =>
|
|
r.category === 'Behaviour' && !r.passed
|
|
).length;
|
|
this.metrics.falsePositives = blockedHappyPath / total;
|
|
|
|
// False Negatives (gick igenom som borde blockerats)
|
|
const passedSafety = testResults.filter(r =>
|
|
r.category === 'Safety' && !r.passed
|
|
).length;
|
|
this.metrics.falseNegatives = passedSafety / total;
|
|
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Beräkna total Trust Score
|
|
*/
|
|
calculate() {
|
|
let score = 0;
|
|
|
|
for (const [metric, weight] of Object.entries(this.weights)) {
|
|
const value = this.metrics[metric];
|
|
// Invertera negativa metrics
|
|
const normalized = ['falsePositives', 'falseNegatives', 'drift'].includes(metric)
|
|
? (1 - value)
|
|
: value;
|
|
|
|
score += normalized * weight;
|
|
}
|
|
|
|
return {
|
|
total: Math.round(score * 100),
|
|
breakdown: this.getBreakdown(),
|
|
metrics: this.metrics,
|
|
timestamp: new Date().toISOString()
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Detaljerad uppdelning
|
|
*/
|
|
getBreakdown() {
|
|
const breakdown = {};
|
|
|
|
for (const [metric, weight] of Object.entries(this.weights)) {
|
|
const value = this.metrics[metric];
|
|
const normalized = ['falsePositives', 'falseNegatives', 'drift'].includes(metric)
|
|
? (1 - value)
|
|
: value;
|
|
|
|
breakdown[metric] = {
|
|
raw: value,
|
|
normalized: Math.round(normalized * 100),
|
|
weighted: Math.round(normalized * weight * 100),
|
|
weight
|
|
};
|
|
}
|
|
|
|
return breakdown;
|
|
}
|
|
|
|
/**
|
|
* Generera rapport
|
|
*/
|
|
report() {
|
|
const score = this.calculate();
|
|
|
|
return {
|
|
trustScore: score.total,
|
|
grade: this.getGrade(score.total),
|
|
...score
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Betygsättning
|
|
*/
|
|
getGrade(score) {
|
|
if (score >= 95) return 'A+';
|
|
if (score >= 90) return 'A';
|
|
if (score >= 85) return 'B+';
|
|
if (score >= 80) return 'B';
|
|
if (score >= 70) return 'C';
|
|
if (score >= 60) return 'D';
|
|
return 'F';
|
|
}
|
|
|
|
/**
|
|
* Rekommendation baserat på score
|
|
*/
|
|
getRecommendation(score) {
|
|
if (score >= 90) {
|
|
return 'Runtime är redo för begränsad användning på riktiga uppgifter.';
|
|
}
|
|
if (score >= 80) {
|
|
return 'Runtime är stabil men kräver mänsklig översyn vid kritiska uppgifter.';
|
|
}
|
|
if (score >= 70) {
|
|
return 'Runtime behöver förbättras innan den används på riktiga uppgifter.';
|
|
}
|
|
return 'Runtime är inte redo. Fortsätt testa och förbättra.';
|
|
}
|
|
}
|
|
|
|
// CLI
|
|
if (process.argv[1] === new URL(import.meta.url).pathname) {
|
|
const trustScore = new RuntimeTrustScore();
|
|
|
|
// Exempel: uppdatera från testresultat
|
|
const mockResults = [
|
|
{ passed: true, category: 'Behaviour' },
|
|
{ passed: true, category: 'Behaviour' },
|
|
{ passed: true, category: 'Safety' },
|
|
{ passed: true, category: 'Safety' },
|
|
{ passed: false, category: 'Reasoning' },
|
|
{ passed: true, category: 'Reproducibility' }
|
|
];
|
|
|
|
trustScore.updateFromTests(mockResults);
|
|
|
|
const report = trustScore.report();
|
|
console.log('=== Runtime Trust Score ===');
|
|
console.log(`Score: ${report.trustScore}/100`);
|
|
console.log(`Grade: ${report.grade}`);
|
|
console.log(`Recommendation: ${report.recommendation}`);
|
|
console.log('\nBreakdown:');
|
|
|
|
for (const [metric, data] of Object.entries(report.breakdown)) {
|
|
console.log(` ${metric}: ${data.normalized}/100 (weighted: ${data.weighted})`);
|
|
}
|
|
}
|
|
|
|
export { RuntimeTrustScore };
|