Skip to content

Commit 14e1ed0

Browse files
committed
feat(api): add risk evaluation logic and new POST_EVALUATE endpoint
1 parent 3f07965 commit 14e1ed0

2 files changed

Lines changed: 142 additions & 0 deletions

File tree

client/app/api/decision/route.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export const runtime = "node";
22
import { NextRequest, NextResponse } from 'next/server';
33
import { PrismaClient } from '@prisma/client';
4+
import { evaluateRisk, RiskEvaluationInput } from '../../lib/riskEngine';
45

56
const prisma = new PrismaClient();
67

@@ -63,3 +64,32 @@ export async function GET() {
6364
return NextResponse.json({ error: 'Failed to fetch decisions.' }, { status: 500 });
6465
}
6566
}
67+
68+
export async function POST_EVALUATE(req: NextRequest) {
69+
try {
70+
const { action, context } = await req.json();
71+
// Fetch past decisions from DB
72+
const pastDecisions = await prisma.decisionLog.findMany({
73+
orderBy: { createdAt: 'desc' },
74+
take: 50,
75+
});
76+
// Evaluate risk
77+
const riskResult = evaluateRisk({ action, context }, pastDecisions);
78+
// Store new decision
79+
const newDecision = await prisma.decisionLog.create({
80+
data: {
81+
action,
82+
context,
83+
outcome: 'pending',
84+
productivityDrop: context.productivity_drop || 0,
85+
},
86+
});
87+
return NextResponse.json({
88+
id: newDecision.id,
89+
...riskResult,
90+
createdAt: newDecision.createdAt,
91+
});
92+
} catch (e) {
93+
return NextResponse.json({ error: 'Failed to evaluate decision', details: String(e) }, { status: 500 });
94+
}
95+
}

client/lib/riskEngine.ts

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import { DecisionLog } from '../../types';
2+
3+
export interface RiskEvaluationInput {
4+
action: string;
5+
context: Record<string, any>;
6+
}
7+
8+
export interface RiskEvaluationOutput {
9+
riskScore: number;
10+
warnings: string[];
11+
recommendedAlternative?: string;
12+
explainability: string;
13+
categoryScores?: Record<string, number>;
14+
futureSimulation?: any;
15+
confidence?: number;
16+
activeRiskLoad?: number;
17+
}
18+
19+
// Dummy similarity function
20+
function similarity(a: any, b: any): number {
21+
// Simple context overlap
22+
let score = 0;
23+
for (const key in a) {
24+
if (b[key] !== undefined && a[key] === b[key]) score += 1;
25+
}
26+
return score;
27+
}
28+
29+
export function evaluateRisk(
30+
input: RiskEvaluationInput,
31+
pastDecisions: DecisionLog[]
32+
): RiskEvaluationOutput {
33+
// Find similar past decisions
34+
const similar = pastDecisions
35+
.map(d => ({ d, sim: similarity(input.context, d.context) }))
36+
.filter(x => x.sim > 0)
37+
.sort((a, b) => b.sim - a.sim)
38+
.slice(0, 5);
39+
40+
// Calculate risk factors
41+
let riskScore = 0;
42+
let warnings: string[] = [];
43+
let categoryScores: Record<string, number> = {};
44+
45+
// Similar past failures
46+
const failures = similar.filter(x => x.d.outcome !== 'success');
47+
if (failures.length) {
48+
riskScore += failures.length * 15;
49+
warnings.push('Similar past actions resulted in failure.');
50+
}
51+
52+
// Sleep deficit
53+
if (input.context.sleep !== undefined && input.context.sleep < 6) {
54+
riskScore += 20;
55+
warnings.push('Sleep deficit detected.');
56+
categoryScores['physical'] = 20;
57+
}
58+
59+
// Overcommitment
60+
if (input.context.active_projects !== undefined && input.context.active_projects > 3) {
61+
riskScore += 15;
62+
warnings.push('Too many active projects.');
63+
categoryScores['execution'] = 15;
64+
}
65+
66+
// Stress level
67+
if (input.context.stress !== undefined && input.context.stress > 7) {
68+
riskScore += 20;
69+
warnings.push('High stress level.');
70+
categoryScores['emotional'] = 20;
71+
}
72+
73+
// Emotional state
74+
if (input.context.emotion !== undefined && input.context.emotion === 'regret') {
75+
riskScore += 10;
76+
warnings.push('Negative emotional state.');
77+
categoryScores['emotional'] = (categoryScores['emotional'] || 0) + 10;
78+
}
79+
80+
// Productivity drop
81+
if (input.context.productivity_drop !== undefined && input.context.productivity_drop > 2) {
82+
riskScore += 10;
83+
warnings.push('Recent productivity drop.');
84+
categoryScores['execution'] = (categoryScores['execution'] || 0) + 10;
85+
}
86+
87+
// Clamp risk score
88+
riskScore = Math.min(100, riskScore);
89+
90+
// Recommended alternative (dummy)
91+
let recommendedAlternative = undefined;
92+
if (riskScore > 60) recommendedAlternative = 'Delay action or reduce workload.';
93+
94+
// Explainability
95+
const explainability = `Risk factors: ${warnings.join(' | ')}`;
96+
97+
// Confidence (dummy)
98+
const confidence = 1 - riskScore / 100;
99+
100+
// Active risk load (dummy)
101+
const activeRiskLoad = riskScore;
102+
103+
return {
104+
riskScore,
105+
warnings,
106+
recommendedAlternative,
107+
explainability,
108+
categoryScores,
109+
confidence,
110+
activeRiskLoad,
111+
};
112+
}

0 commit comments

Comments
 (0)