Files
ims/app/api/incidents/ai/quality-check/route.ts
T

77 lines
2.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
export const dynamic = 'force-dynamic'
import { NextRequest, NextResponse } from 'next/server'
import { createClient } from '@/lib/supabase/server'
import { anthropic } from '@/lib/claude/client'
export async function POST(request: NextRequest) {
const supabase = await createClient()
const { data: { user }, error: authError } = await supabase.auth.getUser()
if (authError || !user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
let body: { description?: string; incident_type?: string }
try {
body = await request.json()
} catch {
return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 })
}
if (!body.description || !body.incident_type) {
return NextResponse.json({ error: 'description and incident_type required' }, { status: 422 })
}
const message = await anthropic.messages.create({
model: 'claude-opus-4-8',
thinking: { type: 'adaptive' },
max_tokens: 1024,
tools: [{
name: 'assess_quality',
description: 'Assess HSE incident report description quality',
input_schema: {
type: 'object' as const,
properties: {
score: { type: 'number', description: '1-10 quality score' },
passes: { type: 'boolean', description: 'True when score is 6 or above' },
feedback: { type: 'string', description: 'One-sentence quality summary' },
suggestions: {
type: 'array',
items: { type: 'string' },
description: 'Up to 3 concrete suggestions to improve the description',
},
},
required: ['score', 'passes', 'feedback', 'suggestions'],
},
}],
tool_choice: { type: 'tool', name: 'assess_quality' },
messages: [{
role: 'user',
content: `You are an HSE reporting assistant for a Malaysian 3PL warehouse. Assess this incident report description.
Incident type: ${body.incident_type}
Description: ${body.description}
Score 110 based on: specificity (location, time, persons involved), completeness (what happened + immediate actions), and clarity. Score 6 or above passes. If score is below 6, give up to 3 actionable suggestions.`,
}],
})
const toolBlock = message.content.find(b => b.type === 'tool_use')
if (!toolBlock || toolBlock.type !== 'tool_use') {
return NextResponse.json({ error: 'AI assessment failed' }, { status: 500 })
}
const input = toolBlock.input as {
score?: unknown
passes?: unknown
feedback?: unknown
suggestions?: unknown
}
if (
typeof input.score !== 'number' ||
typeof input.passes !== 'boolean' ||
typeof input.feedback !== 'string' ||
!Array.isArray(input.suggestions)
) {
return NextResponse.json({ error: 'AI returned unexpected structure' }, { status: 500 })
}
return NextResponse.json(input)
}