first commit
This commit is contained in:
235
base44/functions/reasonAboutEvent/entry.ts
Normal file
235
base44/functions/reasonAboutEvent/entry.ts
Normal file
@@ -0,0 +1,235 @@
|
||||
import { createClientFromRequest } from 'npm:@base44/sdk@0.8.31';
|
||||
|
||||
Deno.serve(async (req) => {
|
||||
try {
|
||||
const base44 = createClientFromRequest(req);
|
||||
const user = await base44.auth.me();
|
||||
if (!user) return Response.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
|
||||
const body = await req.json();
|
||||
const {
|
||||
event_id,
|
||||
event_type,
|
||||
subject_entity_type,
|
||||
subject_entity_id,
|
||||
context_summary,
|
||||
metadata = {},
|
||||
related_entities = []
|
||||
} = body;
|
||||
|
||||
const entities = base44.asServiceRole.entities;
|
||||
|
||||
// 1. Fetch context from the Workforce Graph
|
||||
let graphContext = {};
|
||||
try {
|
||||
const graphRes = await base44.asServiceRole.functions.invoke('queryWorkforceGraph', {
|
||||
query_type: 'subgraph',
|
||||
entity_type: subject_entity_type,
|
||||
entity_id: subject_entity_id
|
||||
});
|
||||
graphContext = graphRes.data?.graph || {};
|
||||
} catch (e) {
|
||||
console.log('Graph query failed (non-blocking):', e.message);
|
||||
}
|
||||
|
||||
// 2. Fetch similar past memories (Operational Memory)
|
||||
const similarMemories = await entities.OperationalMemory.filter(
|
||||
{ trigger_event_type: event_type },
|
||||
'-created_date',
|
||||
5
|
||||
);
|
||||
|
||||
const memoryContext = similarMemories.map(m => ({
|
||||
decision: m.decision?.recommendation || 'N/A',
|
||||
outcome: m.outcome || 'pending',
|
||||
outcome_score: m.outcome_score,
|
||||
reasoning: m.reasoning?.substring(0, 200) || ''
|
||||
}));
|
||||
|
||||
// 3. Determine department based on event type
|
||||
const departmentMap = {
|
||||
'shift.completed': 'operations',
|
||||
'shift.cancelled': 'operations',
|
||||
'shift.no_show': 'operations',
|
||||
'order.created': 'operations',
|
||||
'order.cancelled': 'operations',
|
||||
'order.filled': 'operations',
|
||||
'assignment.cancelled': 'operations',
|
||||
'invoice.overdue': 'finance',
|
||||
'vendor.rate_changed': 'procurement',
|
||||
'worker.availability_changed': 'operations',
|
||||
'worker.certification_expired': 'compliance',
|
||||
'budget.threshold_exceeded': 'finance',
|
||||
'compliance.flagged': 'compliance',
|
||||
'system.anomaly_detected': 'executive'
|
||||
};
|
||||
const department = departmentMap[event_type] || 'operations';
|
||||
|
||||
// 4. Build the reasoning prompt with full context — CAUSAL CHAIN reasoning
|
||||
const prompt = `You are the KROW Intelligence Engine — an AI workforce reasoning system for contingent labor management.
|
||||
|
||||
You do not answer. You REASON.
|
||||
|
||||
When an event occurs, you trace the CAUSAL CHAIN backwards — from the observed event through each contributing factor (connected by ↓) to the root cause(s). You do not give generic analysis. You follow the chain.
|
||||
|
||||
AN EVENT HAS OCCURRED:
|
||||
Event Type: ${event_type}
|
||||
Summary: ${context_summary || 'No summary provided'}
|
||||
Subject: ${subject_entity_type} (${subject_entity_id})
|
||||
|
||||
CURRENT GRAPH STATE (connected entities, relationships, metrics):
|
||||
${JSON.stringify(graphContext, null, 2).substring(0, 3000)}
|
||||
|
||||
SIMILAR PAST DECISIONS (Operational Memory — what happened last time):
|
||||
${memoryContext.length > 0 ? JSON.stringify(memoryContext, null, 2) : 'No similar past decisions found.'}
|
||||
|
||||
EVENT METADATA:
|
||||
${JSON.stringify(metadata, null, 2).substring(0, 1000)}
|
||||
|
||||
REASONING INSTRUCTIONS:
|
||||
|
||||
1. OBSERVATION: State what happened as a fact.
|
||||
|
||||
2. CAUSAL CHAIN: Trace WHY this event occurred. Walk backwards from the event through contributing factors. Each link in the chain is a factor that CAUSED the next. The chain flows:
|
||||
|
||||
Factor A → caused → Factor B → caused → Factor C → caused → [Root Cause]
|
||||
|
||||
Use the graph state, metadata, and past memories as evidence. Reference specific entities, workers, vendors, shifts, or metrics. Do not be generic.
|
||||
|
||||
Categories: vendor, operations, workforce, training, demand, external, compliance, finance, equipment, transportation, system.
|
||||
|
||||
3. ROOT CAUSE(S): The deepest factor(s) — the thing that, if fixed, prevents recurrence.
|
||||
|
||||
4. PREDICTION: What will happen next if no action is taken.
|
||||
|
||||
5. RECOMMENDATION: Target the root cause, not just the symptom.
|
||||
|
||||
Respond as JSON.`;
|
||||
|
||||
// 5. Call the LLM for reasoning
|
||||
let aiDecision = {};
|
||||
try {
|
||||
const llmResponse = await base44.asServiceRole.integrations.Core.InvokeLLM({
|
||||
prompt,
|
||||
response_json_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
observation: { type: 'string', description: 'What happened, stated as a fact' },
|
||||
causal_chain: {
|
||||
type: 'array',
|
||||
description: 'Causal chain traced backwards from the event to root cause(s)',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
step: { type: 'number' },
|
||||
factor: { type: 'string' },
|
||||
evidence: { type: 'string' },
|
||||
category: { type: 'string', enum: ['vendor', 'operations', 'workforce', 'training', 'demand', 'external', 'compliance', 'finance', 'equipment', 'transportation', 'system'] },
|
||||
leads_to: { type: 'string' }
|
||||
}
|
||||
}
|
||||
},
|
||||
root_causes: {
|
||||
type: 'array',
|
||||
items: { type: 'string' }
|
||||
},
|
||||
prediction: { type: 'string' },
|
||||
recommendation: { type: 'string' },
|
||||
confidence_score: { type: 'number' },
|
||||
risk_level: { type: 'string' },
|
||||
proposed_actions: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
action_type: { type: 'string' },
|
||||
description: { type: 'string' },
|
||||
auto_execute: { type: 'boolean' }
|
||||
}
|
||||
}
|
||||
},
|
||||
reasoning: { type: 'string', description: 'Full reasoning chain in natural language' }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
aiDecision = typeof llmResponse === 'string' ? JSON.parse(llmResponse) : llmResponse;
|
||||
} catch (llmErr) {
|
||||
console.log('LLM reasoning failed:', llmErr.message);
|
||||
aiDecision = {
|
||||
prediction: 'Unable to generate prediction',
|
||||
recommendation: 'Manual review recommended',
|
||||
confidence_score: 0,
|
||||
risk_level: 'medium',
|
||||
proposed_actions: [],
|
||||
reasoning: 'LLM reasoning failed: ' + llmErr.message
|
||||
};
|
||||
}
|
||||
|
||||
// 6. Create Operational Memory — store the causal chain
|
||||
const memory_id = `MEM-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
|
||||
const memory = await entities.OperationalMemory.create({
|
||||
memory_id,
|
||||
trigger_event_id: event_id,
|
||||
trigger_event_type: event_type,
|
||||
context_snapshot: { graph: graphContext, metadata },
|
||||
decision: aiDecision,
|
||||
reasoning: aiDecision.reasoning || '',
|
||||
causal_chain: aiDecision.causal_chain || [],
|
||||
root_causes: aiDecision.root_causes || [],
|
||||
similar_memories_consulted: similarMemories.map(m => m.memory_id),
|
||||
status: 'pending',
|
||||
agent_name: `${department}_agent`,
|
||||
department,
|
||||
tags: [event_type, department, aiDecision.risk_level || 'medium']
|
||||
});
|
||||
|
||||
// 7. Mark event as reasoning-triggered
|
||||
await entities.WorkforceEvent.update(event_id, {
|
||||
reasoning_triggered: true,
|
||||
memory_id: memory.id
|
||||
});
|
||||
|
||||
// 8. Log agent activity
|
||||
await entities.AgentActivity.create({
|
||||
agent_name: `${department}_agent`,
|
||||
department,
|
||||
activity_type: 'event_processed',
|
||||
trigger_event_id: event_id,
|
||||
memory_id: memory.id,
|
||||
summary: `Processed ${event_type}: ${aiDecision.recommendation || 'No recommendation'}`,
|
||||
details: { confidence: aiDecision.confidence_score, risk: aiDecision.risk_level },
|
||||
impact_level: aiDecision.risk_level === 'high' ? 'high' : (aiDecision.risk_level === 'medium' ? 'medium' : 'low'),
|
||||
entities_affected: [{ entity_type: subject_entity_type, entity_id: subject_entity_id }]
|
||||
});
|
||||
|
||||
// 9. If high risk, queue for human approval; if low risk + auto_execute, execute
|
||||
const hasAutoActions = (aiDecision.proposed_actions || []).some(a => a.auto_execute);
|
||||
if (aiDecision.risk_level === 'high' || (aiDecision.confidence_score || 0) < 60) {
|
||||
await entities.DecisionQueue.create({
|
||||
memory_id: memory.id,
|
||||
trigger_event_id: event_id,
|
||||
title: `${department} decision: ${event_type}`,
|
||||
summary: aiDecision.recommendation || 'AI recommends manual review',
|
||||
department,
|
||||
agent_name: `${department}_agent`,
|
||||
proposed_actions: aiDecision.proposed_actions || [],
|
||||
confidence_score: aiDecision.confidence_score || 0,
|
||||
risk_level: aiDecision.risk_level || 'medium',
|
||||
status: 'pending_approval',
|
||||
expires_at: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
|
||||
context_snapshot: { graph: graphContext, event_summary: context_summary }
|
||||
});
|
||||
}
|
||||
|
||||
return Response.json({
|
||||
success: true,
|
||||
memory_id: memory.id,
|
||||
decision: aiDecision,
|
||||
graph_nodes: graphContext.nodes?.length || 0,
|
||||
similar_memories: similarMemories.length
|
||||
});
|
||||
} catch (error) {
|
||||
return Response.json({ error: error.message }, { status: 500 });
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user