mirror of
https://github.com/lucasrcsantana/story-generator.git
synced 2025-12-18 06:17:56 +00:00
Compare commits
3 Commits
69dbb5fa48
...
66866602e7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66866602e7 | ||
|
|
f3fbdb8228 | ||
|
|
7e93a59609 |
15
CHANGELOG.md
15
CHANGELOG.md
@ -201,3 +201,18 @@ e este projeto adere ao [Semantic Versioning](https://semver.org/lang/pt-BR/).
|
|||||||
- Adicionada tipagem forte para dados de idioma
|
- Adicionada tipagem forte para dados de idioma
|
||||||
- Implementada validação robusta de códigos de idioma
|
- Implementada validação robusta de códigos de idioma
|
||||||
- Melhorada a estrutura de componentes para suportar dados dinâmicos
|
- Melhorada a estrutura de componentes para suportar dados dinâmicos
|
||||||
|
|
||||||
|
## [1.5.0] - 2024-03-19
|
||||||
|
|
||||||
|
### Modificado
|
||||||
|
- Aprimoramento no cálculo de métricas do dashboard do aluno:
|
||||||
|
- Métricas agora são calculadas considerando todas as histórias e gravações do aluno
|
||||||
|
- Adicionadas novas métricas detalhadas: pronúncia, precisão, compreensão, velocidade, pausas e erros
|
||||||
|
- Melhorias na interface com tooltips explicativos para cada métrica
|
||||||
|
- Separação entre dados para métricas (todas as histórias) e exibição (6 mais recentes)
|
||||||
|
|
||||||
|
### Técnico
|
||||||
|
- Refatoração da busca de dados no StudentDashboardPage:
|
||||||
|
- Separação entre consulta de métricas e consulta de exibição
|
||||||
|
- Otimização no cálculo de médias das métricas
|
||||||
|
- Melhoria na organização do código com comentários explicativos
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Plus, BookOpen, Clock, TrendingUp, Award } from 'lucide-react';
|
import { Plus, BookOpen, Clock, TrendingUp, Award, Mic, Target, Brain, Gauge, Pause, XCircle, HelpCircle } from 'lucide-react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { supabase } from '../../lib/supabase';
|
import { supabase } from '../../lib/supabase';
|
||||||
import type { Story, Student } from '../../types/database';
|
import type { Story, Student } from '../../types/database';
|
||||||
@ -9,6 +9,12 @@ interface DashboardMetrics {
|
|||||||
averageReadingFluency: number;
|
averageReadingFluency: number;
|
||||||
totalReadingTime: number;
|
totalReadingTime: number;
|
||||||
currentLevel: number;
|
currentLevel: number;
|
||||||
|
averagePronunciation: number;
|
||||||
|
averageAccuracy: number;
|
||||||
|
averageComprehension: number;
|
||||||
|
averageWordsPerMinute: number;
|
||||||
|
averagePauses: number;
|
||||||
|
averageErrors: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function StudentDashboardPage() {
|
export function StudentDashboardPage() {
|
||||||
@ -18,7 +24,13 @@ export function StudentDashboardPage() {
|
|||||||
totalStories: 0,
|
totalStories: 0,
|
||||||
averageReadingFluency: 0,
|
averageReadingFluency: 0,
|
||||||
totalReadingTime: 0,
|
totalReadingTime: 0,
|
||||||
currentLevel: 1
|
currentLevel: 1,
|
||||||
|
averagePronunciation: 0,
|
||||||
|
averageAccuracy: 0,
|
||||||
|
averageComprehension: 0,
|
||||||
|
averageWordsPerMinute: 0,
|
||||||
|
averagePauses: 0,
|
||||||
|
averageErrors: 0
|
||||||
});
|
});
|
||||||
const [loading, setLoading] = React.useState(true);
|
const [loading, setLoading] = React.useState(true);
|
||||||
const [error, setError] = React.useState<string | null>(null);
|
const [error, setError] = React.useState<string | null>(null);
|
||||||
@ -44,8 +56,16 @@ export function StudentDashboardPage() {
|
|||||||
if (studentError) throw studentError;
|
if (studentError) throw studentError;
|
||||||
setStudent(studentData);
|
setStudent(studentData);
|
||||||
|
|
||||||
// Buscar histórias do aluno
|
// Buscar todas as histórias do aluno para cálculo de métricas
|
||||||
const { data: storiesData, error: storiesError } = await supabase
|
const { data: allStoriesData, error: allStoriesError } = await supabase
|
||||||
|
.from('stories')
|
||||||
|
.select('id')
|
||||||
|
.eq('student_id', session.user.id);
|
||||||
|
|
||||||
|
if (allStoriesError) throw allStoriesError;
|
||||||
|
|
||||||
|
// Buscar histórias recentes para exibição
|
||||||
|
const { data: recentStoriesData, error: storiesError } = await supabase
|
||||||
.from('stories')
|
.from('stories')
|
||||||
.select('*')
|
.select('*')
|
||||||
.eq('student_id', session.user.id)
|
.eq('student_id', session.user.id)
|
||||||
@ -54,15 +74,52 @@ export function StudentDashboardPage() {
|
|||||||
|
|
||||||
if (storiesError) throw storiesError;
|
if (storiesError) throw storiesError;
|
||||||
|
|
||||||
// Calcular métricas (exemplo simplificado)
|
// Buscar todas as gravações completadas do aluno
|
||||||
setMetrics({
|
const { data: recordings, error: recordingsError } = await supabase
|
||||||
totalStories: storiesData?.length || 0,
|
.from('story_recordings')
|
||||||
averageReadingFluency: 85,
|
.select('*')
|
||||||
totalReadingTime: 120,
|
.eq('status', 'completed')
|
||||||
currentLevel: 3
|
.in('story_id', allStoriesData.map(story => story.id));
|
||||||
});
|
|
||||||
|
|
||||||
// Buscar histórias recentes com a capa definida
|
if (recordingsError) throw recordingsError;
|
||||||
|
|
||||||
|
// Calcular métricas baseadas nas gravações
|
||||||
|
if (recordings && recordings.length > 0) {
|
||||||
|
const totalRecordings = recordings.length;
|
||||||
|
const metricsSum = recordings.reduce((acc, recording) => ({
|
||||||
|
fluency: acc.fluency + recording.fluency_score,
|
||||||
|
pronunciation: acc.pronunciation + recording.pronunciation_score,
|
||||||
|
accuracy: acc.accuracy + recording.accuracy_score,
|
||||||
|
comprehension: acc.comprehension + recording.comprehension_score,
|
||||||
|
wordsPerMinute: acc.wordsPerMinute + recording.words_per_minute,
|
||||||
|
pauses: acc.pauses + recording.pause_count,
|
||||||
|
errors: acc.errors + recording.error_count
|
||||||
|
}), {
|
||||||
|
fluency: 0,
|
||||||
|
pronunciation: 0,
|
||||||
|
accuracy: 0,
|
||||||
|
comprehension: 0,
|
||||||
|
wordsPerMinute: 0,
|
||||||
|
pauses: 0,
|
||||||
|
errors: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
// Calcular médias
|
||||||
|
setMetrics({
|
||||||
|
totalStories: allStoriesData.length, // Usando o total de histórias
|
||||||
|
averageReadingFluency: Math.round(metricsSum.fluency / totalRecordings),
|
||||||
|
totalReadingTime: recordings.length * 2, // Exemplo: 2 minutos por gravação
|
||||||
|
currentLevel: Math.ceil(metricsSum.fluency / (totalRecordings * 20)), // Exemplo: nível baseado na fluência
|
||||||
|
averagePronunciation: Math.round(metricsSum.pronunciation / totalRecordings),
|
||||||
|
averageAccuracy: Math.round(metricsSum.accuracy / totalRecordings),
|
||||||
|
averageComprehension: Math.round(metricsSum.comprehension / totalRecordings),
|
||||||
|
averageWordsPerMinute: Math.round(metricsSum.wordsPerMinute / totalRecordings),
|
||||||
|
averagePauses: Math.round(metricsSum.pauses / totalRecordings),
|
||||||
|
averageErrors: Math.round(metricsSum.errors / totalRecordings)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buscar histórias recentes com a capa definida (mantendo o limite de 6 para exibição)
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from('stories')
|
.from('stories')
|
||||||
.select(`
|
.select(`
|
||||||
@ -177,8 +234,8 @@ export function StudentDashboardPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Métricas */}
|
{/* Métricas Principais */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-6 mb-8">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
|
||||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
|
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<div className="p-3 bg-purple-100 rounded-lg">
|
<div className="p-3 bg-purple-100 rounded-lg">
|
||||||
@ -228,6 +285,147 @@ export function StudentDashboardPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Métricas Detalhadas */}
|
||||||
|
<div className="mb-8">
|
||||||
|
<div className="flex items-center gap-2 mb-4">
|
||||||
|
<h2 className="text-lg font-semibold text-gray-900">Métricas Detalhadas de Leitura</h2>
|
||||||
|
<div
|
||||||
|
className="text-gray-400 hover:text-gray-600 cursor-help transition-colors"
|
||||||
|
title="Estas métricas são calculadas com base em todas as suas gravações de leitura, fornecendo uma visão detalhada do seu progresso"
|
||||||
|
>
|
||||||
|
<HelpCircle className="h-4 w-4" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
|
{/* Pronúncia */}
|
||||||
|
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<div className="p-3 bg-indigo-100 rounded-lg">
|
||||||
|
<Mic className="h-6 w-6 text-indigo-600" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<p className="text-sm text-gray-500">Pronúncia Média</p>
|
||||||
|
<div
|
||||||
|
className="text-gray-400 hover:text-gray-600 cursor-help transition-colors"
|
||||||
|
title="Avalia a qualidade da sua pronúncia durante a leitura, considerando a clareza e correção dos sons das palavras"
|
||||||
|
>
|
||||||
|
<HelpCircle className="h-4 w-4" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="text-2xl font-bold text-gray-900">{metrics.averagePronunciation}%</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Precisão */}
|
||||||
|
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<div className="p-3 bg-pink-100 rounded-lg">
|
||||||
|
<Target className="h-6 w-6 text-pink-600" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<p className="text-sm text-gray-500">Precisão na Leitura</p>
|
||||||
|
<div
|
||||||
|
className="text-gray-400 hover:text-gray-600 cursor-help transition-colors"
|
||||||
|
title="Indica o quão preciso você é ao ler as palavras, sem trocas ou omissões de letras e sílabas"
|
||||||
|
>
|
||||||
|
<HelpCircle className="h-4 w-4" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="text-2xl font-bold text-gray-900">{metrics.averageAccuracy}%</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Compreensão */}
|
||||||
|
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<div className="p-3 bg-orange-100 rounded-lg">
|
||||||
|
<Brain className="h-6 w-6 text-orange-600" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<p className="text-sm text-gray-500">Compreensão do Texto</p>
|
||||||
|
<div
|
||||||
|
className="text-gray-400 hover:text-gray-600 cursor-help transition-colors"
|
||||||
|
title="Avalia seu nível de entendimento do texto durante a leitura, baseado no ritmo e entonação adequados"
|
||||||
|
>
|
||||||
|
<HelpCircle className="h-4 w-4" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="text-2xl font-bold text-gray-900">{metrics.averageComprehension}%</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Velocidade */}
|
||||||
|
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<div className="p-3 bg-cyan-100 rounded-lg">
|
||||||
|
<Gauge className="h-6 w-6 text-cyan-600" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<p className="text-sm text-gray-500">Velocidade de Leitura</p>
|
||||||
|
<div
|
||||||
|
className="text-gray-400 hover:text-gray-600 cursor-help transition-colors"
|
||||||
|
title="Média de palavras lidas por minuto (WPM), indicando a velocidade e fluidez da sua leitura"
|
||||||
|
>
|
||||||
|
<HelpCircle className="h-4 w-4" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="text-2xl font-bold text-gray-900">{metrics.averageWordsPerMinute} WPM</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Pausas */}
|
||||||
|
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<div className="p-3 bg-amber-100 rounded-lg">
|
||||||
|
<Pause className="h-6 w-6 text-amber-600" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<p className="text-sm text-gray-500">Pausas na Leitura</p>
|
||||||
|
<div
|
||||||
|
className="text-gray-400 hover:text-gray-600 cursor-help transition-colors"
|
||||||
|
title="Média de pausas não planejadas durante a leitura, indicando momentos de hesitação"
|
||||||
|
>
|
||||||
|
<HelpCircle className="h-4 w-4" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="text-2xl font-bold text-gray-900">{metrics.averagePauses}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Erros */}
|
||||||
|
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<div className="p-3 bg-red-100 rounded-lg">
|
||||||
|
<XCircle className="h-6 w-6 text-red-600" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<p className="text-sm text-gray-500">Erros de Leitura</p>
|
||||||
|
<div
|
||||||
|
className="text-gray-400 hover:text-gray-600 cursor-help transition-colors"
|
||||||
|
title="Média de erros cometidos durante a leitura, como trocas, omissões ou adições de palavras"
|
||||||
|
>
|
||||||
|
<HelpCircle className="h-4 w-4" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="text-2xl font-bold text-gray-900">{metrics.averageErrors}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Histórias Recentes */}
|
{/* Histórias Recentes */}
|
||||||
<div>
|
<div>
|
||||||
<div className="flex justify-between items-center mb-6">
|
<div className="flex justify-between items-center mb-6">
|
||||||
|
|||||||
@ -23,6 +23,7 @@ export interface GeneratedStory {
|
|||||||
content: {
|
content: {
|
||||||
pages: {
|
pages: {
|
||||||
text: string;
|
text: string;
|
||||||
|
text_syllables: string;
|
||||||
image?: string;
|
image?: string;
|
||||||
}[];
|
}[];
|
||||||
};
|
};
|
||||||
|
|||||||
17
src/utils/cache.ts
Normal file
17
src/utils/cache.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { getFromCache, setInCache, invalidateCache } from '../lib/redis';
|
||||||
|
|
||||||
|
const CACHE_TTL = 300; // 5 minutos em segundos
|
||||||
|
|
||||||
|
export const cacheUtils = {
|
||||||
|
async get<T>(key: string): Promise<T | null> {
|
||||||
|
return getFromCache<T>(key);
|
||||||
|
},
|
||||||
|
|
||||||
|
async set<T>(key: string, value: T): Promise<void> {
|
||||||
|
return setInCache(key, value, CACHE_TTL);
|
||||||
|
},
|
||||||
|
|
||||||
|
async del(key: string): Promise<void> {
|
||||||
|
return invalidateCache(key);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -44,6 +44,7 @@ interface StoryResponse {
|
|||||||
content: {
|
content: {
|
||||||
pages: Array<{
|
pages: Array<{
|
||||||
text: string;
|
text: string;
|
||||||
|
text_syllables: string;
|
||||||
imagePrompt: string;
|
imagePrompt: string;
|
||||||
keywords?: string[];
|
keywords?: string[];
|
||||||
phonemes?: string[];
|
phonemes?: string[];
|
||||||
@ -128,6 +129,7 @@ serve(async (req) => {
|
|||||||
console.log('[GPT] Prompt construído:', prompt);
|
console.log('[GPT] Prompt construído:', prompt);
|
||||||
|
|
||||||
console.log('[GPT] Iniciando geração da história...');
|
console.log('[GPT] Iniciando geração da história...');
|
||||||
|
|
||||||
const completion = await openai.chat.completions.create({
|
const completion = await openai.chat.completions.create({
|
||||||
model: "gpt-4o-mini",
|
model: "gpt-4o-mini",
|
||||||
messages: [
|
messages: [
|
||||||
@ -136,15 +138,131 @@ serve(async (req) => {
|
|||||||
content: "Você é um contador de histórias infantis especializado em conteúdo educativo."
|
content: "Você é um contador de histórias infantis especializado em conteúdo educativo."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: "user",
|
role: "user",
|
||||||
content: prompt
|
content: prompt
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
temperature: 0.7,
|
store: true,
|
||||||
max_tokens: 1000,
|
response_format: {
|
||||||
response_format: { type: "json_object" }
|
type: "json_schema",
|
||||||
|
json_schema: {
|
||||||
|
name: "story",
|
||||||
|
schema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
title: {
|
||||||
|
type: "string",
|
||||||
|
description: "Título da história"
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
pages: {
|
||||||
|
type: "array",
|
||||||
|
items: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
text: {
|
||||||
|
type: "string",
|
||||||
|
description: "Texto completo da página"
|
||||||
|
},
|
||||||
|
text_syllables: {
|
||||||
|
type: "string",
|
||||||
|
description: "Texto separado em sílabas"
|
||||||
|
},
|
||||||
|
imagePrompt: {
|
||||||
|
type: "string",
|
||||||
|
description: "Descrição para gerar imagem"
|
||||||
|
},
|
||||||
|
keywords: {
|
||||||
|
type: "array",
|
||||||
|
items: {
|
||||||
|
type: "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
phonemes: {
|
||||||
|
type: "array",
|
||||||
|
items: {
|
||||||
|
type: "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
syllablePatterns: {
|
||||||
|
type: "array",
|
||||||
|
items: {
|
||||||
|
type: "string",
|
||||||
|
enum: ["CV", "CVC", "CCVC"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: ["text", "text_syllables", "imagePrompt", "keywords", "phonemes", "syllablePatterns"],
|
||||||
|
additionalProperties: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: ["pages"],
|
||||||
|
additionalProperties: false
|
||||||
|
},
|
||||||
|
metadata: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
targetAge: {
|
||||||
|
type: "number"
|
||||||
|
},
|
||||||
|
difficulty: {
|
||||||
|
type: "string"
|
||||||
|
},
|
||||||
|
exerciseWords: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
pronunciation: {
|
||||||
|
type: "array",
|
||||||
|
items: {
|
||||||
|
type: "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
formation: {
|
||||||
|
type: "array",
|
||||||
|
items: {
|
||||||
|
type: "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
completion: {
|
||||||
|
type: "array",
|
||||||
|
items: {
|
||||||
|
type: "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: ["pronunciation", "formation", "completion"],
|
||||||
|
additionalProperties: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: ["targetAge", "difficulty", "exerciseWords"],
|
||||||
|
additionalProperties: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: ["title", "content", "metadata"],
|
||||||
|
additionalProperties: false
|
||||||
|
},
|
||||||
|
strict: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
temperature: 0.7
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (completion.choices[0].finish_reason === "length") {
|
||||||
|
throw new Error("Resposta incompleta do modelo");
|
||||||
|
}
|
||||||
|
|
||||||
|
const story_response = completion.choices[0].message;
|
||||||
|
|
||||||
|
if (story_response.refusal) {
|
||||||
|
console.log(story_response.refusal);
|
||||||
|
throw new Error("História recusada pelo modelo");
|
||||||
|
} else if (!story_response.content) {
|
||||||
|
throw new Error("Sem conteúdo na resposta");
|
||||||
|
}
|
||||||
|
|
||||||
console.log('[GPT] História gerada:', completion.choices[0].message);
|
console.log('[GPT] História gerada:', completion.choices[0].message);
|
||||||
const storyContent = JSON.parse(completion.choices[0].message.content || '{}') as StoryResponse;
|
const storyContent = JSON.parse(completion.choices[0].message.content || '{}') as StoryResponse;
|
||||||
|
|
||||||
@ -236,6 +354,7 @@ serve(async (req) => {
|
|||||||
story_id: payload.story_id,
|
story_id: payload.story_id,
|
||||||
page_number: index + 1,
|
page_number: index + 1,
|
||||||
text: page.text,
|
text: page.text,
|
||||||
|
text_syllables: page.text_syllables,
|
||||||
image_url: page.image,
|
image_url: page.image,
|
||||||
image_path: page.image_path
|
image_path: page.image_path
|
||||||
}))
|
}))
|
||||||
@ -313,9 +432,6 @@ serve(async (req) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Error] Erro ao gerar história:', error);
|
|
||||||
console.error('[Error] Stack trace:', error.stack);
|
|
||||||
|
|
||||||
return new Response(
|
return new Response(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
error: error.message,
|
error: error.message,
|
||||||
@ -364,16 +480,16 @@ function buildPrompt(base: StoryPrompt, voice?: string) {
|
|||||||
- ${selectedLanguage.instructions}
|
- ${selectedLanguage.instructions}
|
||||||
- Linguagem clara e envolvente
|
- Linguagem clara e envolvente
|
||||||
- 3-8 páginas de conteúdo
|
- 3-8 páginas de conteúdo
|
||||||
- Cada página deve ter um texto curto e sugestão para uma imagem
|
- Cada página deve ter um texto curto, o mesmo texto separado em sílabas e uma sugestão para uma imagem
|
||||||
- Evitar conteúdo sensível ou inadequado
|
- Evitar conteúdo sensível ou inadequado
|
||||||
- Incluir elementos de ${base.theme_id}
|
- Incluir elementos de ${base.theme_id}
|
||||||
- Ambientado em ${base.setting_id}
|
- Ambientado em ${base.setting_id}
|
||||||
- Personagem principal baseado em ${base.character_id}
|
- Personagem principal baseado em ${base.character_id}
|
||||||
- A resposta precisa ser em JSON
|
- Use a jornada no héroi para escrever as histórias.
|
||||||
|
|
||||||
Requisitos específicos para exercícios:
|
Requisitos específicos para exercícios:
|
||||||
1. Para o exercício de completar frases:
|
1. Para o exercício de completar frases:
|
||||||
- Selecione 5-8 palavras importantes do texto
|
- Selecione 8-12 palavras importantes do texto
|
||||||
- Escolha palavras que sejam substantivos, verbos ou adjetivos
|
- Escolha palavras que sejam substantivos, verbos ou adjetivos
|
||||||
- Evite artigos, preposições ou palavras muito simples
|
- Evite artigos, preposições ou palavras muito simples
|
||||||
- As palavras devem ser relevantes para o contexto da história
|
- As palavras devem ser relevantes para o contexto da história
|
||||||
@ -395,6 +511,7 @@ function buildPrompt(base: StoryPrompt, voice?: string) {
|
|||||||
"pages": [
|
"pages": [
|
||||||
{
|
{
|
||||||
"text": "Texto da página com frases completas...",
|
"text": "Texto da página com frases completas...",
|
||||||
|
"text_syllables": "Texto da página com frases completas separadas em sílabas em ${selectedLanguage.language}...",
|
||||||
"imagePrompt": "Descrição para gerar imagem...",
|
"imagePrompt": "Descrição para gerar imagem...",
|
||||||
"keywords": ["palavra1", "palavra2"],
|
"keywords": ["palavra1", "palavra2"],
|
||||||
"phonemes": ["fonema1", "fonema2"],
|
"phonemes": ["fonema1", "fonema2"],
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user