Refactor: Renomeacao e modernizacao completa para Pedagog / PedagogIA

This commit is contained in:
2026-08-31 10:34:33 +00:00
parent 00341ebf64
commit f3350b0969
16 changed files with 225 additions and 176 deletions
+1 -1
View File
@@ -2,7 +2,7 @@ PORT=3000
ACCESS_PASSWORD=puglindo26 ACCESS_PASSWORD=puglindo26
OPENROUTER_API_KEY=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 OPENROUTER_API_KEY=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
OPENROUTER_MODEL=openai/gpt-4.1-nano OPENROUTER_MODEL=openai/gpt-4.1-nano
SESSION_SECRET=camila-secret-key-321123 SESSION_SECRET=pedagog-secret-key-321123
MINIMAX_API_KEY=sk-cp-siEwoNh9WA3Prxe6frpJ2HsXPje-gjt5jObhHloqqoO0FX0i9yP54N3zhY492GKu18l9XiANDiCoECU3t0uMtRODvkzzi93A2Rmtco6MjATrKNOEDR_bxa4 MINIMAX_API_KEY=sk-cp-siEwoNh9WA3Prxe6frpJ2HsXPje-gjt5jObhHloqqoO0FX0i9yP54N3zhY492GKu18l9XiANDiCoECU3t0uMtRODvkzzi93A2Rmtco6MjATrKNOEDR_bxa4
MINIMAX_API_BASE=https://api.minimax.io/v1 MINIMAX_API_BASE=https://api.minimax.io/v1
GROQ_API_KEY=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 GROQ_API_KEY=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
@@ -1,10 +1,10 @@
# Kemily — Agente de Educação Infantil # PedagogIA — Agente de Educação Infantil
## Identidade ## Identidade
- **Nome:** Kemily - **Nome:** PedagogIA
- **Género:** Feminino - **Gênero:** Feminino
- **Voz:** Feminina brasileira (将来 implementação de TTS) - **Voz:** Feminina brasileira (MsEdgeTTS pt-BR)
- **Idioma:** Português do Brasil (PT-BR) - **Idioma:** Português do Brasil (PT-BR)
- **Papel:** Assistente de Educação Infantil para Camila (professora da rede municipal de São Bernardo do Campo) - **Papel:** Assistente de Educação Infantil para Camila (professora da rede municipal de São Bernardo do Campo)
@@ -22,7 +22,7 @@ Todo o conhecimento está armazenado em `/root/Apps/Camila/Agente/`:
- `dossie_sbc.md` — Educação municipal de São Bernardo do Campo - `dossie_sbc.md` — Educação municipal de São Bernardo do Campo
- `mindlab_dossier.md` — Organização e programas Mind Lab - `mindlab_dossier.md` — Organização e programas Mind Lab
- `mindlab_jogos_detalhado.md` — Jogos, regras, aplicações práticas - `mindlab_jogos_detalhado.md` — Jogos, regras, aplicações práticas
- `exemplos/` — Documentos reais da Camila (quando disponíveis) - `exemplos/` — Documentos reais da professora Camila
### Áreas de Especialização ### Áreas de Especialização
@@ -30,7 +30,7 @@ Todo o conhecimento está armazenado em `/root/Apps/Camila/Agente/`:
- Desenvolvimento infantil - Desenvolvimento infantil
- Campos de experiência BNCC - Campos de experiência BNCC
- Planejamento pedagógico - Planejamento pedagógico
- Relatórios descritivos (semanais/mensais) - Relatórios descritivos (semanais/mensais/semestrais)
2. **Metodologia Mind Lab** 2. **Metodologia Mind Lab**
- Jogos pedagógicos (certificados INMETRO) - Jogos pedagógicos (certificados INMETRO)
@@ -45,7 +45,7 @@ Todo o conhecimento está armazenado em `/root/Apps/Camila/Agente/`:
- Objetivos de aprendizagem e desenvolvimento - Objetivos de aprendizagem e desenvolvimento
4. **Documentação Pedagógica** 4. **Documentação Pedagógica**
- Relatórios semanais/mensais - Relatórios semanais/mensais/semestrais
- Resumos de livros infantis - Resumos de livros infantis
- Planejamento de atividades - Planejamento de atividades
- Avaliação descritiva - Avaliação descritiva
@@ -60,7 +60,7 @@ Todo o conhecimento está armazenado em `/root/Apps/Camila/Agente/`:
### Como Responder ### Como Responder
1. **Entender a necessidade:** Identificar se é relatório, planejamento, pesquisa, resumo, etc. 1. **Entender a necessidade:** Identificar se é relatório, planejamento, pesquisa, resumo, etc.
2. **Aplicar contexto:** Usar conhecimento da pasta Agente para fundamentar resposta 2. **Aplicar contexto:** Usar conhecimento pedagógico para fundamentar resposta
3. **Linguagem adequada:** Adaptar para português brasileiro acessível 3. **Linguagem adequada:** Adaptar para português brasileiro acessível
4. **Formato prático:** Fornecer modelos, estruturas, sugestões prontas 4. **Formato prático:** Fornecer modelos, estruturas, sugestões prontas
5. **Incluir referência:** Quando usar Mind Lab ou BNCC, mencionar a conexão 5. **Incluir referência:** Quando usar Mind Lab ou BNCC, mencionar a conexão
@@ -69,7 +69,7 @@ Todo o conhecimento está armazenado em `/root/Apps/Camila/Agente/`:
| Tipo | Como Agir | | Tipo | Como Agir |
|------|-----------| |------|-----------|
| Relatório semanal | Fornecer estrutura, pontos a abordar, modelo | | Relatório semanal/semestral | Fornecer estrutura, pontos a abordar, modelo |
| Resumo de livro | Extrair temas, personagens, lições, sugestões de atividade | | Resumo de livro | Extrair temas, personagens, lições, sugestões de atividade |
| Pesquisa | Buscar em conhecimentos locais, fornecer informações fundamentadas | | Pesquisa | Buscar em conhecimentos locais, fornecer informações fundamentadas |
| Planejamento | Criar estrutura com objetivos, atividades, recursos, avaliação | | Planejamento | Criar estrutura com objetivos, atividades, recursos, avaliação |
@@ -80,31 +80,18 @@ Todo o conhecimento está armazenado em `/root/Apps/Camila/Agente/`:
- ❌ Usar inglês ou termos técnicos sem necessidade - ❌ Usar inglês ou termos técnicos sem necessidade
- ❌ Respostas genéricas sem conexão com contexto brasileiro - ❌ Respostas genéricas sem conexão com contexto brasileiro
- ❌忽略 a especificidade da idade (2-5 anos) - ❌ Ignorar a especificidade da idade (2-5 anos)
- ❌ Fazer afirmações sobre legislação sem confirmar com fontes - ❌ Fazer afirmações sobre legislação sem confirmar com fontes
## Integração Técnica
### Arquivo de Configuração
- `.env` do app: `/root/Apps/Camila/.env`
- Modelo: `openai/gpt-4.1-nano`
- Contexto máximo: ~1M tokens
### Como o Contexto é Injetado
O sistema usa o prompt do sistema abaixo para definir a persona Kemily.
As mensagens da Camila são concatenadas com o system prompt.
O modelo GPT-4.1-nano recebe o contexto completo e responde como Kemily.
## System Prompt (Injetado em cada conversa) ## System Prompt (Injetado em cada conversa)
``` ```
Você é a Kemily, uma assistente de educação infantil especializada. Você é a PedagogIA, uma assistente de educação infantil especializada.
Você ajuda Camilly (professora de educação infantil na rede municipal de São Bernardo do Campo, SP) no seu trabalho diário. Você ajuda a professora Camila (professora de educação infantil na rede municipal de São Bernardo do Campo, SP) no seu trabalho diário.
Sua missão: facilitar a criação de relatórios, planejamentos, resumos de livros infantis, pesquisas pedagógicas e uso de metodologias como Mind Lab. Sua missão: facilitar a criação de relatórios, planejamentos, resumos de livros infantis, pesquisas pedagógicas e uso de metodologias como Mind Lab.
Sua personalidade: carinhosa, paciente, prática, profissional. Você fala em português brasileiro e adapt para o nível da Camila. Sua personalidade: carinhosa, paciente, prática, profissional. Você fala em português brasileiro e adapta para as necessidades da docência.
Você tem acesso a conhecimento detalhado sobre: Você tem acesso a conhecimento detalhado sobre:
- BNCC e campos de experiência para educação infantil (2-5 anos) - BNCC e campos de experiência para educação infantil (2-5 anos)
@@ -120,12 +107,3 @@ Sempre que possível:
Responda SEMPRE em português do Brasil. Responda SEMPRE em português do Brasil.
``` ```
---
**Criado em:** 2026-05-25
**Status:** Atualizado em 2026-05-26 — System prompt agora inclui:
- Estilo de escrita rigoroso baseado em documentos reais da Camila
- 4 exemplos reais de textos (HTPC, carta de intenções, planejamento, formação)
- Conhecimento detalhado de projetos, livros, formadoras e metodologia
- Regras de atuação e estrutura documental
@@ -1,6 +1,6 @@
# PROMPT PARA KEMILY — ANÁLISE E REDAÇÃO DE RELATÓRIOS SEMESTRAIS # PROMPT PARA PEDAGOGIA — ANÁLISE E REDAÇÃO DE RELATÓRIOS SEMESTRAIS
Você é a assistente da Professora Camila Reifonas, do Educação Infantil da rede municipal de São Bernardo do Campo. Sua função é analisar observações de alunos e redigir relatórios semestrais no **EXATO** estilo da Professora Camila. Você é a assistente virtual e especialista pedagógica da Professora Camila Reifonas, da Educação Infantil da rede municipal de São Bernardo do Campo. Sua função é analisar observações de alunos e redigir relatórios semestrais no **EXATO** estilo da Professora Camila.
--- ---
@@ -13,7 +13,7 @@ Ao receber observações cruas (notas, fotos, registros) sobre um aluno:
- Brincadeiras e Preferências (materiais, brinquedos, espaços) - Brincadeiras e Preferências (materiais, brinquedos, espaços)
- Interações Sociais (com quem brinca, como se relaciona, conflitos) - Interações Sociais (com quem brinca, como se relaciona, conflitos)
- Desenvolvimento Motor (correr, pular, pedalar, equilíbrio) - Desenvolvimento Motor (correr, pular, pedalar, equilíbrio)
- Linguagem e Oralidade (vocabulário, clareza,连贯性) - Linguagem e Oralidade (vocabulário, clareza, coerência)
- Criatividade e Atividades (construções, arte, imaginação) - Criatividade e Atividades (construções, arte, imaginação)
- Autonomia (cuidado de material, organização, refeições) - Autonomia (cuidado de material, organização, refeições)
- Desafios a desenvolver (sempre formular com cuidado) - Desafios a desenvolver (sempre formular com cuidado)
@@ -138,7 +138,7 @@ Espero que o/a [nome] continue se interessando bastante pela escola e avançando
| `cultura de paz` | resolução de conflitos | | `cultura de paz` | resolução de conflitos |
| `sensibilidade e afetividade` | empatia | | `sensibilidade e afetividade` | empatia |
| `concentração e foco` | atenção | | `concentração e foco` | atenção |
| `combidados` | regras da turma (nunca diga "regras") | | `combinados` | regras da turma (nunca diga "regras") |
| `porcionar` | servings de comida | | `porcionar` | servings de comida |
| `ampliou sua preferência por` | alimentação | | `ampliou sua preferência por` | alimentação |
@@ -146,8 +146,8 @@ Espero que o/a [nome] continue se interessando bastante pela escola e avançando
### EXPRESSÕES CARACTERÍSTICAS: ### EXPRESSÕES CARACTERÍSTICAS:
- `a todo momento` — para常态化 - `a todo momento` — para constância
- `juntos com`nunca "junto com" - `juntos com`conforme estilo da professora
- `de acordo com o que lhe é proposto` - `de acordo com o que lhe é proposto`
- `especialmente` — para destacar pontos marcantes - `especialmente` — para destacar pontos marcantes
- `demonstrando` — ações que evidenciam qualidades - `demonstrando` — ações que evidenciam qualidades
@@ -159,7 +159,7 @@ Espero que o/a [nome] continue se interessando bastante pela escola e avançando
### O QUE NUNCA USAR: ### O QUE NUNCA USAR:
- ❌ `mostrou` → use **demonstrou** - ❌ `mostrou` → use **demonstrou**
- ❌ `acordos` → use **combidados** - ❌ `acordos` → use **combinados**
- ❌ `regras` isoladamente → use **regras e combinados** - ❌ `regras` isoladamente → use **regras e combinados**
- ❌ Críticas diretas ou palavras como "problema", "dificuldade" - ❌ Críticas diretas ou palavras como "problema", "dificuldade"
- ❌ Menção a condições de saúde ou situações familiares - ❌ Menção a condições de saúde ou situações familiares
@@ -242,15 +242,15 @@ O relatório semestral completo é composto por **3 partes**:
## PARTE 7 — DIFERENÇAS COM A ESTRUTURA INSTITUCIONAL ## PARTE 7 — DIFERENÇAS COM A ESTRUTURA INSTITUCIONAL
Os relatórios da Camila **NÃO têm:** Os relatórios **NÃO têm:**
- Cabeçalhos institucionais (escola, turma, ano) - Cabeçalhos institucionais (escola, turma, ano)
- Numeração de blocos ou tópicos - Numeração de blocos ou tópicos
- Notas ou conceitos (satisfatório, insatisfatório, etc.) - Notas ou conceitos (satisfatório, insatisfatório, etc.)
- Avaliação por disciplina (é Educação Infantil) - Avaliação por disciplina (é Educação Infantil)
Os relatórios da Camila **TÊM:** Os relatórios **TÊM:**
- Texto corrido, narrativo, em parágrafos - Texto corrido, narrativo, em parágrafos
- Linguagem descritiva e positiva - Linguagem descritiva e positiva
- Foco em avanços, não em deficiências - Foco em avanços, não em deficiências
- Pronóstico esperançoso no fechamento - Pronóstico esperançoso no fechamento
- Linguagem atenuada para pontos de melhoria ("necessitando de", "permanece o desafio de") - Linguagem atenuada para pontos de melhoria ("necessitando de", "permanece o desafio de")
+3 -3
View File
@@ -1,5 +1,5 @@
{ {
"agentName": "Kemily", "agentName": "PedagogIA",
"kemilyAvatarUrl": "assets/kemily.png", "iaAvatarUrl": "assets/pedagog_ia.png",
"camilaAvatarUrl": "assets/camila_prof.png" "userAvatarUrl": "assets/pedagog_user.png"
} }
+2 -2
View File
@@ -1,7 +1,7 @@
{ {
"name": "camila-chatgpt-clone", "name": "pedagog",
"version": "1.0.0", "version": "1.0.0",
"description": "Replica do ChatGPT moderna com autenticação e integração OpenRouter", "description": "Pedagog - Plataforma de IA Educacional para Professores",
"main": "server.js", "main": "server.js",
"scripts": { "scripts": {
"start": "node server.js", "start": "node server.js",
+56 -56
View File
@@ -216,16 +216,16 @@ const initApp = () => {
const searchClearAll = document.getElementById('searchClearAll'); const searchClearAll = document.getElementById('searchClearAll');
const sidebarTags = document.getElementById('sidebarTags'); const sidebarTags = document.getElementById('sidebarTags');
// Configuração de Identidade do Agente (Kemily/Camila) // Configuração de Identidade do Agente (PedagogIA/Pedagog)
const btnSettings = document.getElementById('btnSettings'); const btnSettings = document.getElementById('btnSettings');
const settingsModal = document.getElementById('settingsModal'); const settingsModal = document.getElementById('settingsModal');
const closeSettingsModal = document.getElementById('closeSettingsModal'); const closeSettingsModal = document.getElementById('closeSettingsModal');
const btnSaveSettings = document.getElementById('btnSaveSettings'); const btnSaveSettings = document.getElementById('btnSaveSettings');
const settingAgentName = document.getElementById('settingAgentName'); const settingAgentName = document.getElementById('settingAgentName');
const previewKemilyAvatar = document.getElementById('previewKemilyAvatar'); const previewIaAvatar = document.getElementById('previewIaAvatar');
const uploadKemilyAvatar = document.getElementById('uploadKemilyAvatar'); const uploadIaAvatar = document.getElementById('uploadIaAvatar');
const previewCamilaAvatar = document.getElementById('previewCamilaAvatar'); const previewUserAvatar = document.getElementById('previewUserAvatar');
const uploadCamilaAvatar = document.getElementById('uploadCamilaAvatar'); const uploadUserAvatar = document.getElementById('uploadUserAvatar');
const welcomeTitle = document.getElementById('welcomeTitle'); const welcomeTitle = document.getElementById('welcomeTitle');
const welcomeAvatarImg = document.getElementById('welcomeAvatarImg'); const welcomeAvatarImg = document.getElementById('welcomeAvatarImg');
const sidebarAvatarImg = document.getElementById('sidebarAvatarImg'); const sidebarAvatarImg = document.getElementById('sidebarAvatarImg');
@@ -235,9 +235,9 @@ const initApp = () => {
const btnAddKnowledge = document.getElementById('btnAddKnowledge'); const btnAddKnowledge = document.getElementById('btnAddKnowledge');
window.agentConfig = { window.agentConfig = {
agentName: "Kemily", agentName: "PedagogIA",
kemilyAvatarUrl: "assets/kemily.png", iaAvatarUrl: "assets/pedagog_ia.png",
camilaAvatarUrl: "assets/camila_prof.png", userAvatarUrl: "assets/pedagog_user.png",
temperaturePreset: 'equilibrado' temperaturePreset: 'equilibrado'
}; };
@@ -264,22 +264,22 @@ const initApp = () => {
const updateAgentIdentityUI = () => { const updateAgentIdentityUI = () => {
if (welcomeTitle) { if (welcomeTitle) {
welcomeTitle.innerText = `Olá! Sou sua assistente virtual e me chamo ${window.agentConfig.agentName}. Como posso ajudar?`; welcomeTitle.innerText = `Olá! Sou sua assistente virtual e me chamo ${window.agentConfig.agentName || 'PedagogIA'}. Como posso ajudar?`;
} }
if (welcomeAvatarImg) { if (welcomeAvatarImg) {
welcomeAvatarImg.src = window.agentConfig.kemilyAvatarUrl; welcomeAvatarImg.src = window.agentConfig.iaAvatarUrl || window.agentConfig.kemilyAvatarUrl || 'assets/pedagog_ia.png';
} }
if (sidebarAvatarImg) { if (sidebarAvatarImg) {
sidebarAvatarImg.src = window.agentConfig.camilaAvatarUrl; sidebarAvatarImg.src = window.agentConfig.userAvatarUrl || window.agentConfig.camilaAvatarUrl || 'assets/pedagog_user.png';
} }
if (settingAgentName) { if (settingAgentName) {
settingAgentName.value = window.agentConfig.agentName; settingAgentName.value = window.agentConfig.agentName || 'PedagogIA';
} }
if (previewKemilyAvatar) { if (previewIaAvatar) {
previewKemilyAvatar.src = window.agentConfig.kemilyAvatarUrl; previewIaAvatar.src = window.agentConfig.iaAvatarUrl || window.agentConfig.kemilyAvatarUrl || 'assets/pedagog_ia.png';
} }
if (previewCamilaAvatar) { if (previewUserAvatar) {
previewCamilaAvatar.src = window.agentConfig.camilaAvatarUrl; previewUserAvatar.src = window.agentConfig.userAvatarUrl || window.agentConfig.camilaAvatarUrl || 'assets/pedagog_user.png';
} }
// Atualiza seleção de temperatura // Atualiza seleção de temperatura
renderTemperatureOptions(); renderTemperatureOptions();
@@ -472,17 +472,17 @@ const initApp = () => {
}; };
// Variáveis temporárias para avatares (em base64 ou URLs novas) // Variáveis temporárias para avatares (em base64 ou URLs novas)
let newKemilyAvatarData = null; let newIaAvatarData = null;
let newCamilaAvatarData = null; let newUserAvatarData = null;
// Eventos do Modal // Eventos do Modal
if (btnSettings) { if (btnSettings) {
btnSettings.addEventListener('click', () => { btnSettings.addEventListener('click', () => {
newKemilyAvatarData = null; newIaAvatarData = null;
newCamilaAvatarData = null; newUserAvatarData = null;
if (settingAgentName) settingAgentName.value = window.agentConfig.agentName; if (settingAgentName) settingAgentName.value = window.agentConfig.agentName || 'PedagogIA';
if (previewKemilyAvatar) previewKemilyAvatar.src = window.agentConfig.kemilyAvatarUrl; if (previewIaAvatar) previewIaAvatar.src = window.agentConfig.iaAvatarUrl || window.agentConfig.kemilyAvatarUrl || 'assets/pedagog_ia.png';
if (previewCamilaAvatar) previewCamilaAvatar.src = window.agentConfig.camilaAvatarUrl; if (previewUserAvatar) previewUserAvatar.src = window.agentConfig.userAvatarUrl || window.agentConfig.camilaAvatarUrl || 'assets/pedagog_user.png';
renderTemperatureOptions(); renderTemperatureOptions();
loadConhecimento(); loadConhecimento();
settingsModal.style.display = 'flex'; settingsModal.style.display = 'flex';
@@ -529,18 +529,18 @@ const initApp = () => {
} }
}; };
if (uploadKemilyAvatar) { if (uploadIaAvatar) {
uploadKemilyAvatar.addEventListener('change', () => { uploadIaAvatar.addEventListener('change', () => {
handleAvatarFileSelect(uploadKemilyAvatar, previewKemilyAvatar, (base64) => { handleAvatarFileSelect(uploadIaAvatar, previewIaAvatar, (base64) => {
newKemilyAvatarData = base64; newIaAvatarData = base64;
}); });
}); });
} }
if (uploadCamilaAvatar) { if (uploadUserAvatar) {
uploadCamilaAvatar.addEventListener('change', () => { uploadUserAvatar.addEventListener('change', () => {
handleAvatarFileSelect(uploadCamilaAvatar, previewCamilaAvatar, (base64) => { handleAvatarFileSelect(uploadUserAvatar, previewUserAvatar, (base64) => {
newCamilaAvatarData = base64; newUserAvatarData = base64;
}); });
}); });
} }
@@ -552,31 +552,31 @@ const initApp = () => {
btnSaveSettings.innerText = 'Salvando...'; btnSaveSettings.innerText = 'Salvando...';
try { try {
let kemilyUrl = window.agentConfig.kemilyAvatarUrl; let iaUrl = window.agentConfig.iaAvatarUrl || window.agentConfig.kemilyAvatarUrl || 'assets/pedagog_ia.png';
let camilaUrl = window.agentConfig.camilaAvatarUrl; let userUrl = window.agentConfig.userAvatarUrl || window.agentConfig.camilaAvatarUrl || 'assets/pedagog_user.png';
// Se houver novas imagens, faz upload // Se houver novas imagens, faz upload
if (newKemilyAvatarData) { if (newIaAvatarData) {
const res = await fetch('/api/upload-avatar', { const res = await fetch('/api/upload-avatar', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ avatarType: 'kemily', base64Data: newKemilyAvatarData }) body: JSON.stringify({ avatarType: 'ia', base64Data: newIaAvatarData })
}); });
if (res.ok) { if (res.ok) {
const data = await res.json(); const data = await res.json();
kemilyUrl = data.url; iaUrl = data.url;
} }
} }
if (newCamilaAvatarData) { if (newUserAvatarData) {
const res = await fetch('/api/upload-avatar', { const res = await fetch('/api/upload-avatar', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ avatarType: 'camila', base64Data: newCamilaAvatarData }) body: JSON.stringify({ avatarType: 'user', base64Data: newUserAvatarData })
}); });
if (res.ok) { if (res.ok) {
const data = await res.json(); const data = await res.json();
camilaUrl = data.url; userUrl = data.url;
} }
} }
@@ -587,8 +587,8 @@ const initApp = () => {
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ body: JSON.stringify({
agentName: settingAgentName.value, agentName: settingAgentName.value,
kemilyAvatarUrl: kemilyUrl, iaAvatarUrl: iaUrl,
camilaAvatarUrl: camilaUrl, userAvatarUrl: userUrl,
temperaturePreset: selectedTempPreset temperaturePreset: selectedTempPreset
}) })
}); });
@@ -615,10 +615,10 @@ const initApp = () => {
loadAgentConfig(); loadAgentConfig();
// Variáveis de Estado // Variáveis de Estado
let chats = JSON.parse(localStorage.getItem('camila_chats')) || []; let chats = JSON.parse(localStorage.getItem('pedagog_chats') || localStorage.getItem('camila_chats')) || [];
// Migração: adicionar campos tags e pinned a chats antigos // Migração: adicionar campos tags e pinned a chats antigos
chats = chats.map(c => ({ ...c, tags: c.tags || [], pinned: c.pinned || false })); chats = chats.map(c => ({ ...c, tags: c.tags || [], pinned: c.pinned || false }));
let currentChatId = localStorage.getItem('camila_current_chat_id') || null; let currentChatId = localStorage.getItem('pedagog_current_chat_id') || localStorage.getItem('camila_current_chat_id') || null;
let isGenerating = false; let isGenerating = false;
let searchQuery = ''; let searchQuery = '';
let activeTag = 'all'; let activeTag = 'all';
@@ -638,7 +638,7 @@ const initApp = () => {
const applyTheme = (theme) => { const applyTheme = (theme) => {
document.documentElement.setAttribute('data-theme', theme); document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('camila_theme', theme); localStorage.setItem('pedagog_theme', theme);
if (theme === 'light') { if (theme === 'light') {
iconThemeSun.style.display = 'none'; iconThemeSun.style.display = 'none';
iconThemeMoon.style.display = 'block'; iconThemeMoon.style.display = 'block';
@@ -649,7 +649,7 @@ const initApp = () => {
}; };
// Init theme from localStorage or system preference // Init theme from localStorage or system preference
const savedTheme = localStorage.getItem('camila_theme'); const savedTheme = localStorage.getItem('pedagog_theme') || localStorage.getItem('camila_theme');
if (savedTheme) { if (savedTheme) {
applyTheme(savedTheme); applyTheme(savedTheme);
} else { } else {
@@ -1283,11 +1283,11 @@ const initApp = () => {
// ========================================================================== // ==========================================================================
const saveChats = () => { const saveChats = () => {
localStorage.setItem('camila_chats', JSON.stringify(chats)); localStorage.setItem('pedagog_chats', JSON.stringify(chats));
if (currentChatId) { if (currentChatId) {
localStorage.setItem('camila_current_chat_id', currentChatId); localStorage.setItem('pedagog_current_chat_id', currentChatId);
} else { } else {
localStorage.removeItem('camila_current_chat_id'); localStorage.removeItem('pedagog_current_chat_id');
} }
}; };
@@ -1563,7 +1563,7 @@ const initApp = () => {
if (isGenerating) return; if (isGenerating) return;
currentChatId = null; currentChatId = null;
localStorage.removeItem('camila_current_chat_id'); localStorage.removeItem('pedagog_current_chat_id');
welcomeContainer.style.display = 'flex'; welcomeContainer.style.display = 'flex';
conversationList.innerHTML = ''; conversationList.innerHTML = '';
@@ -1599,8 +1599,8 @@ const initApp = () => {
row.className = `message-row ${role}`; row.className = `message-row ${role}`;
const avatarHtml = isUser const avatarHtml = isUser
? `<div class="avatar user-avatar user-avatar-img-wrapper"><img src="${window.agentConfig?.camilaAvatarUrl || 'assets/camila_prof.png'}" alt="Camila" class="user-avatar-img"></div>` ? `<div class="avatar user-avatar user-avatar-img-wrapper"><img src="${window.agentConfig?.userAvatarUrl || window.agentConfig?.camilaAvatarUrl || 'assets/pedagog_user.png'}" alt="Usuário" class="user-avatar-img"></div>`
: `<div class="avatar bot-avatar bot-avatar-img-wrapper"><img src="${window.agentConfig?.kemilyAvatarUrl || 'assets/kemily.png'}" alt="${window.agentConfig?.agentName || 'Kemily'}" class="bot-avatar-img"></div>`; : `<div class="avatar bot-avatar bot-avatar-img-wrapper"><img src="${window.agentConfig?.iaAvatarUrl || window.agentConfig?.kemilyAvatarUrl || 'assets/pedagog_ia.png'}" alt="${window.agentConfig?.agentName || 'PedagogIA'}" class="bot-avatar-img"></div>`;
let contentHtml = ''; let contentHtml = '';
if (isUser) { if (isUser) {
@@ -1991,7 +1991,7 @@ const initApp = () => {
botRow.innerHTML = ` botRow.innerHTML = `
<div class="message-icon"> <div class="message-icon">
<div class="avatar bot-avatar bot-avatar-img-wrapper"><img src="${window.agentConfig?.kemilyAvatarUrl || 'assets/kemily.png'}" alt="${window.agentConfig?.agentName || 'Kemily'}" class="bot-avatar-img"></div> <div class="avatar bot-avatar bot-avatar-img-wrapper"><img src="${window.agentConfig?.iaAvatarUrl || window.agentConfig?.kemilyAvatarUrl || 'assets/pedagog_ia.png'}" alt="${window.agentConfig?.agentName || 'PedagogIA'}" class="bot-avatar-img"></div>
</div> </div>
<div class="message-content-wrapper"> <div class="message-content-wrapper">
<div class="message-content"> <div class="message-content">
@@ -2713,7 +2713,7 @@ const initApp = () => {
showRecordState('processing'); showRecordState('processing');
try { try {
const turmaData = localStorage.getItem('camila_turma') || '[]'; const turmaData = localStorage.getItem('pedagog_turma') || localStorage.getItem('camila_turma') || '[]';
const res = await fetch('/api/observacao/analisar', { const res = await fetch('/api/observacao/analisar', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
@@ -2923,7 +2923,7 @@ const initApp = () => {
let especialKidsNames = []; let especialKidsNames = [];
if (isSpecialObsMode) { if (isSpecialObsMode) {
const turma = JSON.parse(localStorage.getItem('camila_turma') || '[]'); const turma = JSON.parse(localStorage.getItem('pedagog_turma') || localStorage.getItem('camila_turma') || '[]');
especialKidsNames = turma.filter(c => c.especial).map(c => (c.nome || '').toLowerCase().trim()).filter(Boolean); especialKidsNames = turma.filter(c => c.especial).map(c => (c.nome || '').toLowerCase().trim()).filter(Boolean);
if (especialKidsNames.length === 0) { if (especialKidsNames.length === 0) {
obsList.innerHTML = '<div class="obs-empty">Nenhuma criança com "Educação Especial" cadastrada na Minha Turma.</div>'; obsList.innerHTML = '<div class="obs-empty">Nenhuma criança com "Educação Especial" cadastrada na Minha Turma.</div>';
@@ -3051,7 +3051,7 @@ const initApp = () => {
try { try {
const res = await fetch('/api/alunos'); const res = await fetch('/api/alunos');
currentAlunos = await res.json(); currentAlunos = await res.json();
localStorage.setItem('camila_turma', JSON.stringify(currentAlunos)); localStorage.setItem('pedagog_turma', JSON.stringify(currentAlunos));
return currentAlunos; return currentAlunos;
} catch(e) { console.error(e); return []; } } catch(e) { console.error(e); return []; }
}; };
@@ -4981,7 +4981,7 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.`
} }
// Renderizar quadrinhos na tela (todos, incluindo os anteriores caso tenha sido extensão) // Renderizar quadrinhos na tela (todos, incluindo os anteriores caso tenha sido extensão)
comicsResultTitle.textContent = scriptData.title || comicsResultTitle.textContent || 'Fábrica de Quadrinhos Camila'; comicsResultTitle.textContent = scriptData.title || comicsResultTitle.textContent || 'Fábrica de Quadrinhos Pedagog';
comicsGridContainer.innerHTML = ''; comicsGridContainer.innerHTML = '';
generatedPanelsData.forEach((panel, index) => { generatedPanelsData.forEach((panel, index) => {
Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

+13 -13
View File
@@ -146,10 +146,10 @@
<div class="sidebar-footer"> <div class="sidebar-footer">
<div class="user-profile"> <div class="user-profile">
<div class="avatar user-avatar sidebar-avatar-wrapper"> <div class="avatar user-avatar sidebar-avatar-wrapper">
<img src="assets/camila_prof.png" alt="Camila Reifonas" class="sidebar-avatar-img" id="sidebarAvatarImg"> <img src="assets/pedagog_user.png" alt="Pedagog" class="sidebar-avatar-img" id="sidebarAvatarImg">
</div> </div>
<div class="user-info"> <div class="user-info">
<span class="user-name">Camila Reifonas</span> <span class="user-name">Pedagog</span>
<span class="user-status">Online</span> <span class="user-status">Online</span>
</div> </div>
<button class="btn-settings" id="btnSettings" title="Configurações do Agente" style="background: none; border: none; color: var(--text-secondary); cursor: pointer; padding: 8px; border-radius: 8px; display: flex; align-items: center; justify-content: center; transition: color var(--transition-fast), background-color var(--transition-fast); margin-right: 4px;"> <button class="btn-settings" id="btnSettings" title="Configurações do Agente" style="background: none; border: none; color: var(--text-secondary); cursor: pointer; padding: 8px; border-radius: 8px; display: flex; align-items: center; justify-content: center; transition: color var(--transition-fast), background-color var(--transition-fast); margin-right: 4px;">
@@ -226,10 +226,10 @@
<div class="welcome-container" id="welcomeContainer"> <div class="welcome-container" id="welcomeContainer">
<div class="welcome-logo"> <div class="welcome-logo">
<div class="logo-outer welcome-avatar-wrapper"> <div class="logo-outer welcome-avatar-wrapper">
<img src="assets/kemily.png" alt="Kemily AI" class="welcome-avatar-img" id="welcomeAvatarImg"> <img src="assets/pedagog_ia.png" alt="PedagogIA" class="welcome-avatar-img" id="welcomeAvatarImg">
</div> </div>
</div> </div>
<h2 id="welcomeTitle">Olá! Sou sua assistente virtual e me chamo Kemily. Como posso ajudar?</h2> <h2 id="welcomeTitle">Olá! Sou sua assistente virtual e me chamo PedagogIA. Como posso ajudar?</h2>
</div> </div>
@@ -422,25 +422,25 @@
<div id="tabIdentidade" class="settings-tab-content active"> <div id="tabIdentidade" class="settings-tab-content active">
<div class="settings-group"> <div class="settings-group">
<label for="settingAgentName">Nome da Assistente</label> <label for="settingAgentName">Nome da Assistente</label>
<input type="text" id="settingAgentName" placeholder="Ex: Kemily"> <input type="text" id="settingAgentName" placeholder="Ex: PedagogIA">
<small style="color: var(--text-muted); margin-top: 4px; display: block;">Nome que aparece para a Camila na conversa</small> <small style="color: var(--text-muted); margin-top: 4px; display: block;">Nome que aparece para a usuária na conversa</small>
</div> </div>
<div class="settings-group"> <div class="settings-group">
<label>Avatar da Assistente</label> <label>Avatar da Assistente</label>
<div class="avatar-preview-container"> <div class="avatar-preview-container">
<img id="previewKemilyAvatar" src="assets/kemily.png" alt="Assistante Preview" class="preview-avatar-img"> <img id="previewIaAvatar" src="assets/pedagog_ia.png" alt="Assistente Preview" class="preview-avatar-img">
<input type="file" id="uploadKemilyAvatar" accept="image/*" style="display: none;"> <input type="file" id="uploadIaAvatar" accept="image/*" style="display: none;">
<button type="button" class="btn-change-avatar" onclick="document.getElementById('uploadKemilyAvatar').click()">Escolher Imagem</button> <button type="button" class="btn-change-avatar" onclick="document.getElementById('uploadIaAvatar').click()">Escolher Imagem</button>
</div> </div>
</div> </div>
<div class="settings-group"> <div class="settings-group">
<label>Avatar da Camila (Usuária)</label> <label>Avatar do Usuário</label>
<div class="avatar-preview-container"> <div class="avatar-preview-container">
<img id="previewCamilaAvatar" src="assets/camila_prof.png" alt="Camila Preview" class="preview-avatar-img"> <img id="previewUserAvatar" src="assets/pedagog_user.png" alt="Usuário Preview" class="preview-avatar-img">
<input type="file" id="uploadCamilaAvatar" accept="image/*" style="display: none;"> <input type="file" id="uploadUserAvatar" accept="image/*" style="display: none;">
<button type="button" class="btn-change-avatar" onclick="document.getElementById('uploadCamilaAvatar').click()">Escolher Imagem</button> <button type="button" class="btn-change-avatar" onclick="document.getElementById('uploadUserAvatar').click()">Escolher Imagem</button>
</div> </div>
</div> </div>
</div> </div>
+3 -3
View File
@@ -4,7 +4,7 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Camila - Entrar</title> <title>Pedagog - Entrar</title>
<!-- Google Fonts Outfit & Inter --> <!-- Google Fonts Outfit & Inter -->
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
@@ -21,10 +21,10 @@
<div class="login-card"> <div class="login-card">
<div class="login-header"> <div class="login-header">
<div class="avatar-profile-wrapper"> <div class="avatar-profile-wrapper">
<img src="assets/camila_prof.png" alt="Camila Reifonas" class="avatar-profile-img"> <img src="assets/pedagog_user.png" alt="Pedagog" class="avatar-profile-img">
<div class="avatar-profile-pulse"></div> <div class="avatar-profile-pulse"></div>
</div> </div>
<h1>CamiIA</h1> <h1>Pedagog</h1>
<p>Insira a senha de acesso para entrar no workspace</p> <p>Insira a senha de acesso para entrar no workspace</p>
</div> </div>
+1 -1
View File
@@ -2361,7 +2361,7 @@ code {
color: #fdba74; color: #fdba74;
} }
/* Modo Texto/História: Estilos e Cores (Verde Kemily) */ /* Modo Texto/História: Estilos e Cores (Verde PedagogIA) */
.btn-prompt-mode.mode-text { .btn-prompt-mode.mode-text {
border-color: rgba(16, 163, 127, 0.4); border-color: rgba(16, 163, 127, 0.4);
color: #34d399; color: #34d399;
+7 -5
View File
@@ -1,12 +1,14 @@
const CACHE_NAME = 'camila-ai-v10'; const CACHE_NAME = 'pedagog-ai-v11';
const urlsToCache = [ const urlsToCache = [
'/', '/',
'/index.html', '/index.html',
'/style.css', '/style.css',
'/app.js', '/app.js',
'/manifest.json', '/manifest.json',
'/icon-192.png', '/assets/icon-192.png',
'/icon-512.png' '/assets/icon-512.png',
'/assets/pedagog_user.png',
'/assets/pedagog_ia.png'
]; ];
// Instalação - abre cache e armazena os arquivos iniciais // Instalação - abre cache e armazena os arquivos iniciais
@@ -14,7 +16,7 @@ self.addEventListener('install', (event) => {
event.waitUntil( event.waitUntil(
caches.open(CACHE_NAME) caches.open(CACHE_NAME)
.then((cache) => { .then((cache) => {
console.log('Camila AI SW: Armazenando cache inicial'); console.log('Pedagog AI SW: Armazenando cache inicial');
return cache.addAll(urlsToCache); return cache.addAll(urlsToCache);
}) })
.then(() => self.skipWaiting()) .then(() => self.skipWaiting())
@@ -28,7 +30,7 @@ self.addEventListener('activate', (event) => {
return Promise.all( return Promise.all(
cacheNames.map((cacheName) => { cacheNames.map((cacheName) => {
if (cacheName !== CACHE_NAME) { if (cacheName !== CACHE_NAME) {
console.log('Camila AI SW: Removendo cache antigo:', cacheName); console.log('Pedagog AI SW: Removendo cache antigo:', cacheName);
return caches.delete(cacheName); return caches.delete(cacheName);
} }
}) })
+46
View File
@@ -0,0 +1,46 @@
require('dotenv').config({ path: '/root/Apps/Camila/.env' });
const { Pool } = require('pg');
const pool = new Pool({
connectionString: process.env.DATABASE_URL
});
async function migrate() {
try {
console.log("Conectando ao banco...");
const cols = await pool.query(`
SELECT column_name
FROM information_schema.columns
WHERE table_schema='escola' AND table_name='config';
`);
const colNames = cols.rows.map(r => r.column_name);
console.log("Colunas atuais:", colNames);
if (colNames.includes('kemily_avatar_url')) {
console.log("Renomeando kemily_avatar_url -> ia_avatar_url");
await pool.query(`ALTER TABLE escola.config RENAME COLUMN kemily_avatar_url TO ia_avatar_url;`);
}
if (colNames.includes('camila_avatar_url')) {
console.log("Renomeando camila_avatar_url -> user_avatar_url");
await pool.query(`ALTER TABLE escola.config RENAME COLUMN camila_avatar_url TO user_avatar_url;`);
}
await pool.query(`ALTER TABLE escola.config ALTER COLUMN agent_name SET DEFAULT 'PedagogIA';`);
await pool.query(`ALTER TABLE escola.config ALTER COLUMN ia_avatar_url SET DEFAULT 'assets/pedagog_ia.png';`);
await pool.query(`ALTER TABLE escola.config ALTER COLUMN user_avatar_url SET DEFAULT 'assets/pedagog_user.png';`);
await pool.query(`UPDATE escola.config SET agent_name = 'PedagogIA' WHERE agent_name = 'Kemily';`);
await pool.query(`UPDATE escola.config SET ia_avatar_url = 'assets/pedagog_ia.png' WHERE ia_avatar_url = 'assets/kemily.png';`);
await pool.query(`UPDATE escola.config SET user_avatar_url = 'assets/pedagog_user.png' WHERE user_avatar_url = 'assets/camila_prof.png';`);
console.log("Migração concluída com sucesso!");
const updated = await pool.query(`SELECT * FROM escola.config;`);
console.log("Configurações atualizadas:", updated.rows);
} catch (err) {
console.error("Erro na migração:", err);
} finally {
await pool.end();
}
}
migrate();
+1 -1
View File
@@ -62,7 +62,7 @@ async function test() {
}).join('\n\n---\n\n'); }).join('\n\n---\n\n');
const agentConfig = readAgentConfig(); const agentConfig = readAgentConfig();
const agentName = agentConfig.agentName || "Kemily"; const agentName = agentConfig.agentName || "PedagogIA";
const targetStudentInfo = `\nATENÇÃO CRÍTICA: A criança que está sendo avaliada e para quem você deve gerar o relatório individual é especificamente: "${crianca}". Todas as observações, avanços, preferências e conclusões descritas na PARTE 2 (Desenvolvimento Individual) devem referir-se exclusivamente a ${crianca}. Nunca confunda ${crianca} com outros colegas eventualmente citados nas observações (como Joãozinho, Vanderlei, etc.), e não atribua as ações deles a ela.\n`; const targetStudentInfo = `\nATENÇÃO CRÍTICA: A criança que está sendo avaliada e para quem você deve gerar o relatório individual é especificamente: "${crianca}". Todas as observações, avanços, preferências e conclusões descritas na PARTE 2 (Desenvolvimento Individual) devem referir-se exclusivamente a ${crianca}. Nunca confunda ${crianca} com outros colegas eventualmente citados nas observações (como Joãozinho, Vanderlei, etc.), e não atribua as ações deles a ela.\n`;
+65 -42
View File
@@ -60,11 +60,11 @@ async function callMinimax({ messages, temperature = 0.5, max_tokens = 1500, sys
app.use(express.json({ limit: '50mb' })); app.use(express.json({ limit: '50mb' }));
app.use(express.urlencoded({ limit: '50mb', extended: true })); app.use(express.urlencoded({ limit: '50mb', extended: true }));
app.use(cookieParser(process.env.SESSION_SECRET || 'camila-secret')); app.use(cookieParser(process.env.SESSION_SECRET || 'pedagog-secret'));
// Middleware para verificar autenticação // Middleware para verificar autenticação
const requireAuth = (req, res, next) => { const requireAuth = (req, res, next) => {
const session = req.signedCookies.camila_session || req.cookies.camila_session; const session = req.signedCookies.pedagog_session || req.cookies.pedagog_session || req.signedCookies.camila_session || req.cookies.camila_session;
if (session === 'authenticated') { if (session === 'authenticated') {
next(); next();
} else { } else {
@@ -115,9 +115,9 @@ const TEMPERATURE_PRESETS = {
}; };
let agentConfigCache = { let agentConfigCache = {
agentName: "Kemily", agentName: "PedagogIA",
kemilyAvatarUrl: "assets/kemily.png", iaAvatarUrl: "assets/pedagog_ia.png",
camilaAvatarUrl: "assets/camila_prof.png", userAvatarUrl: "assets/pedagog_user.png",
temperaturePreset: "equilibrado" temperaturePreset: "equilibrado"
}; };
@@ -127,27 +127,27 @@ function readAgentConfig() {
async function writeAgentConfig(config) { async function writeAgentConfig(config) {
agentConfigCache = { agentConfigCache = {
agentName: config.agentName, agentName: config.agentName || "PedagogIA",
kemilyAvatarUrl: config.kemilyAvatarUrl, iaAvatarUrl: config.iaAvatarUrl || config.kemilyAvatarUrl || "assets/pedagog_ia.png",
camilaAvatarUrl: config.camilaAvatarUrl, userAvatarUrl: config.userAvatarUrl || config.camilaAvatarUrl || "assets/pedagog_user.png",
temperaturePreset: config.temperaturePreset || 'equilibrado' temperaturePreset: config.temperaturePreset || 'equilibrado'
}; };
try { try {
await dbPool.query( await dbPool.query(
`INSERT INTO escola.config (key, agent_name, kemily_avatar_url, camila_avatar_url, temperature_preset, updated_at) `INSERT INTO escola.config (key, agent_name, ia_avatar_url, user_avatar_url, temperature_preset, updated_at)
VALUES ('current', $1, $2, $3, $4, NOW()) VALUES ('current', $1, $2, $3, $4, NOW())
ON CONFLICT (key) DO UPDATE SET ON CONFLICT (key) DO UPDATE SET
agent_name = EXCLUDED.agent_name, agent_name = EXCLUDED.agent_name,
kemily_avatar_url = EXCLUDED.kemily_avatar_url, ia_avatar_url = EXCLUDED.ia_avatar_url,
camila_avatar_url = EXCLUDED.camila_avatar_url, user_avatar_url = EXCLUDED.user_avatar_url,
temperature_preset = EXCLUDED.temperature_preset, temperature_preset = EXCLUDED.temperature_preset,
updated_at = NOW();`, updated_at = NOW();`,
[config.agentName, config.kemilyAvatarUrl, config.camilaAvatarUrl, config.temperaturePreset || 'equilibrado'] [agentConfigCache.agentName, agentConfigCache.iaAvatarUrl, agentConfigCache.userAvatarUrl, agentConfigCache.temperaturePreset]
); );
fs.writeFileSync(CONFIG_FILE_PATH, JSON.stringify(config, null, 2), 'utf8'); fs.writeFileSync(CONFIG_FILE_PATH, JSON.stringify(agentConfigCache, null, 2), 'utf8');
const botConfigDir = path.join(__dirname, '..', 'BotVPS', 'data'); const botConfigDir = path.join(__dirname, '..', 'BotVPS', 'data');
if (fs.existsSync(botConfigDir)) { if (fs.existsSync(botConfigDir)) {
fs.writeFileSync(path.join(botConfigDir, 'agent_config.json'), JSON.stringify(config, null, 2), 'utf8'); fs.writeFileSync(path.join(botConfigDir, 'agent_config.json'), JSON.stringify(agentConfigCache, null, 2), 'utf8');
} }
} catch (e) { } catch (e) {
console.error('Erro ao salvar agent_config no banco:', e); console.error('Erro ao salvar agent_config no banco:', e);
@@ -1386,7 +1386,7 @@ app.post('/api/cartazes/generate', requireAuth, async (req, res) => {
try { try {
const agentConfig = readAgentConfig(); const agentConfig = readAgentConfig();
const agentName = agentConfig.agentName || "Kemily"; const agentName = agentConfig.agentName || "PedagogIA";
// 1. Gerar Conteúdo do Cartaz // 1. Gerar Conteúdo do Cartaz
const posterPrompt = `Você é a ${agentName}, assistente pedagógica da professora Camila Martella Gasparini Reifonas. const posterPrompt = `Você é a ${agentName}, assistente pedagógica da professora Camila Martella Gasparini Reifonas.
@@ -1642,7 +1642,7 @@ Return ONLY the English prompt, nothing else.`;
if (!tema) return res.status(400).json({ error: 'Tema ou layers_manual são obrigatórios.' }); if (!tema) return res.status(400).json({ error: 'Tema ou layers_manual são obrigatórios.' });
const agentConfig = readAgentConfig(); const agentConfig = readAgentConfig();
const agentName = agentConfig.agentName || "Kemily"; const agentName = agentConfig.agentName || "PedagogIA";
const segmentPrompt = `Você é a ${agentName}, assistente pedagógica. const segmentPrompt = `Você é a ${agentName}, assistente pedagógica.
Tema: "${tema}" / Título: "${titulo || ''}" Tema: "${tema}" / Título: "${titulo || ''}"
@@ -2067,14 +2067,14 @@ app.get('/api/agent-config', requireAuth, (req, res) => {
// Rota POST para atualizar configurações do agente // Rota POST para atualizar configurações do agente
app.post('/api/agent-config', requireAuth, async (req, res) => { app.post('/api/agent-config', requireAuth, async (req, res) => {
const { agentName, kemilyAvatarUrl, camilaAvatarUrl, temperaturePreset } = req.body; const { agentName, iaAvatarUrl, userAvatarUrl, kemilyAvatarUrl, camilaAvatarUrl, temperaturePreset } = req.body;
if (!agentName) { if (!agentName) {
return res.status(400).json({ error: 'Nome do agente é obrigatório' }); return res.status(400).json({ error: 'Nome do agente é obrigatório' });
} }
const config = { const config = {
agentName: agentName.trim(), agentName: agentName.trim(),
kemilyAvatarUrl: kemilyAvatarUrl || "assets/kemily.png", iaAvatarUrl: iaAvatarUrl || kemilyAvatarUrl || "assets/pedagog_ia.png",
camilaAvatarUrl: camilaAvatarUrl || "assets/camila_prof.png", userAvatarUrl: userAvatarUrl || camilaAvatarUrl || "assets/pedagog_user.png",
temperaturePreset: temperaturePreset || 'equilibrado' temperaturePreset: temperaturePreset || 'equilibrado'
}; };
await writeAgentConfig(config); await writeAgentConfig(config);
@@ -2473,21 +2473,37 @@ async function initDatabase() {
} }
// 1. Sincronizar cache de configurações do agente ou migrar // 1. Sincronizar cache de configurações do agente ou migrar
const configRes = await dbPool.query("SELECT agent_name, kemily_avatar_url, camila_avatar_url, temperature_preset FROM escola.config WHERE key = 'current';"); try {
await dbPool.query(`
DO $$
BEGIN
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='escola' AND table_name='config' AND column_name='kemily_avatar_url') THEN
ALTER TABLE escola.config RENAME COLUMN kemily_avatar_url TO ia_avatar_url;
END IF;
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='escola' AND table_name='config' AND column_name='camila_avatar_url') THEN
ALTER TABLE escola.config RENAME COLUMN camila_avatar_url TO user_avatar_url;
END IF;
END $$;
`);
} catch (migErr) {
console.warn('[Database Init] Aviso ao verificar colunas da tabela escola.config:', migErr.message);
}
const configRes = await dbPool.query("SELECT agent_name, ia_avatar_url, user_avatar_url, temperature_preset FROM escola.config WHERE key = 'current';");
if (configRes.rows.length > 0) { if (configRes.rows.length > 0) {
agentConfigCache = { agentConfigCache = {
agentName: configRes.rows[0].agent_name, agentName: configRes.rows[0].agent_name || "PedagogIA",
kemilyAvatarUrl: configRes.rows[0].kemily_avatar_url, iaAvatarUrl: configRes.rows[0].ia_avatar_url || "assets/pedagog_ia.png",
camilaAvatarUrl: configRes.rows[0].camila_avatar_url, userAvatarUrl: configRes.rows[0].user_avatar_url || "assets/pedagog_user.png",
temperaturePreset: configRes.rows[0].temperature_preset temperaturePreset: configRes.rows[0].temperature_preset || "equilibrado"
}; };
console.log('[Database Init] Configurações do agente carregadas do banco de dados:', agentConfigCache.agentName); console.log('[Database Init] Configurações do agente carregadas do banco de dados:', agentConfigCache.agentName);
} else { } else {
console.log('[Database Init] Migrando configurações do agente do arquivo local...'); console.log('[Database Init] Migrando configurações do agente do arquivo local...');
let localConfig = { let localConfig = {
agentName: "Kemily", agentName: "PedagogIA",
kemilyAvatarUrl: "assets/kemily.png", iaAvatarUrl: "assets/pedagog_ia.png",
camilaAvatarUrl: "assets/camila_prof.png", userAvatarUrl: "assets/pedagog_user.png",
temperaturePreset: "equilibrado" temperaturePreset: "equilibrado"
}; };
try { try {
@@ -2497,10 +2513,15 @@ async function initDatabase() {
} catch (err) { } catch (err) {
console.error('[Database Init] Erro ao ler agent_config.json local:', err.message); console.error('[Database Init] Erro ao ler agent_config.json local:', err.message);
} }
agentConfigCache = localConfig; agentConfigCache = {
agentName: localConfig.agentName || "PedagogIA",
iaAvatarUrl: localConfig.iaAvatarUrl || localConfig.kemilyAvatarUrl || "assets/pedagog_ia.png",
userAvatarUrl: localConfig.userAvatarUrl || localConfig.camilaAvatarUrl || "assets/pedagog_user.png",
temperaturePreset: localConfig.temperaturePreset || "equilibrado"
};
await dbPool.query( await dbPool.query(
"INSERT INTO escola.config (key, agent_name, kemily_avatar_url, camila_avatar_url, temperature_preset) VALUES ($1, $2, $3, $4, $5) ON CONFLICT (key) DO NOTHING;", "INSERT INTO escola.config (key, agent_name, ia_avatar_url, user_avatar_url, temperature_preset) VALUES ($1, $2, $3, $4, $5) ON CONFLICT (key) DO NOTHING;",
['current', localConfig.agentName, localConfig.kemilyAvatarUrl, localConfig.camilaAvatarUrl, localConfig.temperaturePreset || 'equilibrado'] ['current', agentConfigCache.agentName, agentConfigCache.iaAvatarUrl, agentConfigCache.userAvatarUrl, agentConfigCache.temperaturePreset]
); );
} }
@@ -2663,7 +2684,7 @@ app.post('/api/observacao/analisar', requireAuth, async (req, res) => {
try { try {
const agentConfig = readAgentConfig(); const agentConfig = readAgentConfig();
const agentName = agentConfig.agentName || "Kemily"; const agentName = agentConfig.agentName || "PedagogIA";
const temperaturePreset = agentConfig.temperaturePreset || 'equilibrado'; const temperaturePreset = agentConfig.temperaturePreset || 'equilibrado';
const presetDesc = TEMPERATURE_PRESETS[temperaturePreset]?.description || TEMPERATURE_PRESETS['equilibrado'].description; const presetDesc = TEMPERATURE_PRESETS[temperaturePreset]?.description || TEMPERATURE_PRESETS['equilibrado'].description;
const presetLabel = TEMPERATURE_PRESETS[temperaturePreset]?.label || 'Equilibrado'; const presetLabel = TEMPERATURE_PRESETS[temperaturePreset]?.label || 'Equilibrado';
@@ -3194,7 +3215,7 @@ app.post('/api/modelos/gerar-relatorio', requireAuth, async (req, res) => {
}).join('\n\n---\n\n'); }).join('\n\n---\n\n');
const agentConfig = readAgentConfig(); const agentConfig = readAgentConfig();
const agentName = agentConfig.agentName || "Kemily"; const agentName = agentConfig.agentName || "PedagogIA";
const isGlobal = crianca === 'todas'; const isGlobal = crianca === 'todas';
const targetStudentInfo = isGlobal const targetStudentInfo = isGlobal
@@ -3445,7 +3466,7 @@ app.get('/api/observacoes/resumo', requireAuth, async (req, res) => {
const combined = texts.join('\n\n---\n\n'); const combined = texts.join('\n\n---\n\n');
const agentConfig = readAgentConfig(); const agentConfig = readAgentConfig();
const agentName = agentConfig.agentName || "Kemily"; const agentName = agentConfig.agentName || "PedagogIA";
const compilePrompt = `Você é a ${agentName}, assistente pedagógica. Compilar um RESUMO PEDAGÓGICO consolidado a partir de várias observações de uma criança. const compilePrompt = `Você é a ${agentName}, assistente pedagógica. Compilar um RESUMO PEDAGÓGICO consolidado a partir de várias observações de uma criança.
@@ -3551,7 +3572,7 @@ app.post('/api/upload-avatar', requireAuth, (req, res) => {
// Rota de login (página) // Rota de login (página)
app.get('/login', (req, res) => { app.get('/login', (req, res) => {
const session = req.signedCookies.camila_session || req.cookies.camila_session; const session = req.signedCookies.pedagog_session || req.cookies.pedagog_session || req.signedCookies.camila_session || req.cookies.camila_session;
if (session === 'authenticated') { if (session === 'authenticated') {
res.redirect('/'); res.redirect('/');
} else { } else {
@@ -3574,7 +3595,7 @@ app.post('/api/login', (req, res) => {
const { password } = req.body; const { password } = req.body;
if (password === ACCESS_PASSWORD) { if (password === ACCESS_PASSWORD) {
// Define cookie de sessão assinado que dura 30 dias // Define cookie de sessão assinado que dura 30 dias
res.cookie('camila_session', 'authenticated', { res.cookie('pedagog_session', 'authenticated', {
signed: true, signed: true,
httpOnly: true, httpOnly: true,
maxAge: 30 * 24 * 60 * 60 * 1000, // 30 dias maxAge: 30 * 24 * 60 * 60 * 1000, // 30 dias
@@ -3589,6 +3610,7 @@ app.post('/api/login', (req, res) => {
// API para Logout // API para Logout
app.post('/api/logout', (req, res) => { app.post('/api/logout', (req, res) => {
res.clearCookie('pedagog_session');
res.clearCookie('camila_session'); res.clearCookie('camila_session');
res.json({ success: true }); res.json({ success: true });
}); });
@@ -3650,10 +3672,10 @@ app.get('/generated-media/:filename', async (req, res, next) => {
// Servir arquivos estáticos do diretório public // Servir arquivos estáticos do diretório public
app.use(express.static(path.join(__dirname, 'public'))); app.use(express.static(path.join(__dirname, 'public')));
// System prompt da Kemily (injetado em todas as conversas) // System prompt da PedagogIA (injetado em todas as conversas)
const KEMILY_SYSTEM_PROMPT = { const PEDAGOG_IA_SYSTEM_PROMPT = {
role: "system", role: "system",
content: `Você é a Kemily, assistente virtual e extensionista digital da Profª Camila Martella Gasparini Reifonas. content: `Você é a PedagogIA, assistente virtual e extensionista digital da Profª Camila Martella Gasparini Reifonas.
═══════════════════════════════════════════════════ ═══════════════════════════════════════════════════
IDENTIDADE — Quem é a Camila (para que você possa falar dela com precisão) IDENTIDADE — Quem é a Camila (para que você possa falar dela com precisão)
@@ -4545,7 +4567,7 @@ app.post('/api/chat', requireAuth, async (req, res) => {
// FLUXO PADRÃO DE TEXTO (OpenRouter com SSE streaming original, com fallback Groq) // FLUXO PADRÃO DE TEXTO (OpenRouter com SSE streaming original, com fallback Groq)
// ========================================================================== // ==========================================================================
const agentConfig = readAgentConfig(); const agentConfig = readAgentConfig();
const agentName = agentConfig.agentName || "Kemily"; const agentName = agentConfig.agentName || "PedagogIA";
const temperaturePreset = agentConfig.temperaturePreset || 'equilibrado'; const temperaturePreset = agentConfig.temperaturePreset || 'equilibrado';
const temperature = TEMPERATURE_PRESETS[temperaturePreset]?.value || 0.6; const temperature = TEMPERATURE_PRESETS[temperaturePreset]?.value || 0.6;
@@ -4622,7 +4644,8 @@ app.post('/api/chat', requireAuth, async (req, res) => {
const dynamicSystemPrompt = { const dynamicSystemPrompt = {
role: "system", role: "system",
content: styleDirective + languageDirective + KEMILY_SYSTEM_PROMPT.content content: styleDirective + languageDirective + PEDAGOG_IA_SYSTEM_PROMPT.content
.replace(/Você é a PedagogIA/g, `Você é a ${agentName}`)
.replace(/Você é a Kemily/g, `Você é a ${agentName}`) .replace(/Você é a Kemily/g, `Você é a ${agentName}`)
.replace('INFORMAÇÕES PESSOAIS:', `${knowledgeText}${observationsContext}INFORMAÇÕES PESSOAIS:`) + styleDirective + languageDirective .replace('INFORMAÇÕES PESSOAIS:', `${knowledgeText}${observationsContext}INFORMAÇÕES PESSOAIS:`) + styleDirective + languageDirective
}; };
@@ -4671,8 +4694,8 @@ app.post('/api/chat', requireAuth, async (req, res) => {
method: 'POST', method: 'POST',
headers: { headers: {
'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`, 'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
'HTTP-Referer': 'https://camila.reifonas.cloud', 'HTTP-Referer': 'https://pedagog.app',
'X-Title': 'Camila ChatGPT replica', 'X-Title': 'Pedagog AI',
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
body: JSON.stringify({ body: JSON.stringify({
+4 -4
View File
@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
# --------------------------------------------------------- # ---------------------------------------------------------
# CAMILA - SCRIPT DE AUTO-DEPLOY E SYNC # PEDAGOG - SCRIPT DE AUTO-DEPLOY E SYNC
# --------------------------------------------------------- # ---------------------------------------------------------
CYAN='\033[0;36m' CYAN='\033[0;36m'
@@ -8,7 +8,7 @@ GREEN='\033[0;32m'
YELLOW='\033[1;33m' YELLOW='\033[1;33m'
NC='\033[0m' NC='\033[0m'
echo -e "\n${CYAN}🚀 Iniciando Ciclo Automático Camila...${NC}" echo -e "\n${CYAN}🚀 Iniciando Ciclo Automático Pedagog...${NC}"
# 1. Sincronização Git # 1. Sincronização Git
echo -e "${YELLOW}📝 Sincronizando código com o Gitea...${NC}" echo -e "${YELLOW}📝 Sincronizando código com o Gitea...${NC}"
@@ -18,7 +18,7 @@ if git diff-index --quiet HEAD --; then
echo -e "${GREEN}✨ Código local já está sincronizado.${NC}" echo -e "${GREEN}✨ Código local já está sincronizado.${NC}"
else else
TIMESTAMP=$(date +"%d/%m/%Y %H:%M:%S") TIMESTAMP=$(date +"%d/%m/%Y %H:%M:%S")
MSG="${1:-🚀 Auto-deploy: Camila atualizado em $TIMESTAMP}" MSG="${1:-🚀 Auto-deploy: Pedagog atualizado em $TIMESTAMP}"
git commit -m "$MSG" git commit -m "$MSG"
git push origin main git push origin main
fi fi
@@ -27,7 +27,7 @@ fi
COOLIFY_RESOURCE_UUID="zzmj37ya8lmntomh3bm8rivi" COOLIFY_RESOURCE_UUID="zzmj37ya8lmntomh3bm8rivi"
COOLIFY_TOKEN="12|wbepNILQe24LAOjfEUmMROgU93F6uG1zuwPLwrRi786bca03" COOLIFY_TOKEN="12|wbepNILQe24LAOjfEUmMROgU93F6uG1zuwPLwrRi786bca03"
echo -e "${YELLOW}🔄 Disparando Deploy na Coolify (camila.reifonas.cloud)...${NC}" echo -e "${YELLOW}🔄 Disparando Deploy na Coolify...${NC}"
curl -s -X GET "https://painel.reifonas.cloud/api/v1/deploy?uuid=${COOLIFY_RESOURCE_UUID}&force=false" \ curl -s -X GET "https://painel.reifonas.cloud/api/v1/deploy?uuid=${COOLIFY_RESOURCE_UUID}&force=false" \
-H "Authorization: Bearer $COOLIFY_TOKEN" -H "Authorization: Bearer $COOLIFY_TOKEN"