feat: Implementando Edge Function Analyze-Essay

This commit is contained in:
Lucas Santana 2025-02-06 20:22:30 -03:00
parent 206f7bcb30
commit f602f4c666
2 changed files with 212 additions and 0 deletions

View File

@ -0,0 +1,4 @@
export const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
}

View File

@ -0,0 +1,208 @@
import { serve } from 'https://deno.fresh.run/std@0.168.0/http/server.ts'
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'
import { corsHeaders } from '../_shared/cors.ts'
import { OpenAI } from "https://deno.land/x/openai@v4.24.0/mod.ts";
interface EssayAnalysisRequest {
essay_id: string;
content: string;
type_id: string;
genre_id: string;
}
interface EssayAnalysisResponse {
overall_score: number;
feedback: {
structure: string;
content: string;
language: string;
};
strengths: string[];
improvements: string[];
suggestions: string;
criteria_scores: {
adequacy: number; // Adequação ao tema/gênero
coherence: number; // Coerência textual
cohesion: number; // Coesão
vocabulary: number; // Vocabulário
grammar: number; // Gramática/ortografia
};
}
interface EssayType {
id: string;
slug: string;
title: string;
description: string;
}
interface EssayGenre {
id: string;
type_id: string;
slug: string;
title: string;
description: string;
requirements: {
min_words: number;
max_words: number;
required_elements: string[];
};
}
serve(async (req) => {
// Handle CORS
if (req.method === 'OPTIONS') {
return new Response('ok', { headers: corsHeaders })
}
try {
// Criar cliente Supabase
const supabaseClient = createClient(
Deno.env.get('SUPABASE_URL') ?? '',
Deno.env.get('SUPABASE_ANON_KEY') ?? ''
)
// Criar cliente OpenAI
const openai = new OpenAI({
apiKey: Deno.env.get('OPENAI_API_KEY'),
});
// Obter dados da requisição
const { essay_id, content, type_id, genre_id }: EssayAnalysisRequest = await req.json()
// Validar dados obrigatórios
if (!essay_id || !content || !type_id || !genre_id) {
return new Response(
JSON.stringify({ error: 'Dados obrigatórios não fornecidos' }),
{ status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
)
}
// Buscar informações do tipo e gênero
const { data: typeData, error: typeError } = await supabaseClient
.from('essay_types')
.select('*')
.eq('id', type_id)
.single()
const { data: genreData, error: genreError } = await supabaseClient
.from('essay_genres')
.select('*')
.eq('id', genre_id)
.single()
if (typeError || genreError || !typeData || !genreData) {
return new Response(
JSON.stringify({ error: 'Tipo ou gênero não encontrado' }),
{ status: 404, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
)
}
const essayType: EssayType = typeData
const essayGenre: EssayGenre = genreData
// Construir prompt para a análise
const prompt = `Você é um professor especialista em análise de textos. Analise a redação a seguir considerando que é do tipo "${essayType.title}" e gênero "${essayGenre.title}".
Requisitos específicos do gênero:
- Mínimo de palavras: ${essayGenre.requirements.min_words}
- Máximo de palavras: ${essayGenre.requirements.max_words}
- Elementos obrigatórios: ${essayGenre.requirements.required_elements.join(', ')}
Texto para análise:
${content}
Forneça uma análise detalhada considerando:
1. Adequação ao tipo e gênero textual
2. Coerência e coesão textual
3. Vocabulário e linguagem
4. Gramática e ortografia
5. Elementos obrigatórios do gênero
6. Pontos fortes
7. Pontos a melhorar
8. Sugestões específicas para aprimoramento
Responda em formato JSON seguindo exatamente esta estrutura:
{
"overall_score": number, // 0 a 100
"feedback": {
"structure": string, // Feedback sobre estrutura e organização
"content": string, // Feedback sobre conteúdo e ideias
"language": string // Feedback sobre linguagem e gramática
},
"strengths": string[], // Lista de pontos fortes
"improvements": string[], // Lista de pontos a melhorar
"suggestions": string, // Sugestões específicas
"criteria_scores": {
"adequacy": number, // 0 a 100 - Adequação ao tema/gênero
"coherence": number, // 0 a 100 - Coerência textual
"cohesion": number, // 0 a 100 - Coesão
"vocabulary": number, // 0 a 100 - Vocabulário
"grammar": number // 0 a 100 - Gramática/ortografia
}
}`
// Realizar análise com OpenAI
const completion = await openai.chat.completions.create({
model: "gpt-4-turbo-preview",
messages: [
{
role: "system",
content: "Você é um professor especialista em análise de textos, com vasta experiência em avaliação de redações."
},
{
role: "user",
content: prompt
}
],
response_format: { type: "json_object" }
});
const analysis: EssayAnalysisResponse = JSON.parse(completion.choices[0].message.content)
// Salvar análise no banco
const { error: saveError } = await supabaseClient
.from('essay_analyses')
.insert({
essay_id,
overall_score: analysis.overall_score,
feedback: analysis.feedback,
strengths: analysis.strengths,
improvements: analysis.improvements,
suggestions: analysis.suggestions,
criteria_scores: analysis.criteria_scores
})
if (saveError) {
return new Response(
JSON.stringify({ error: 'Erro ao salvar análise' }),
{ status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
)
}
// Atualizar status da redação
const { error: updateError } = await supabaseClient
.from('student_essays')
.update({ status: 'analyzed' })
.eq('id', essay_id)
if (updateError) {
return new Response(
JSON.stringify({ error: 'Erro ao atualizar status da redação' }),
{ status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
)
}
// Retornar análise
return new Response(
JSON.stringify(analysis),
{ headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
)
} catch (error) {
return new Response(
JSON.stringify({ error: error.message }),
{ status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
)
}
})