healthtechbrasil commited on
Commit
cc51976
·
1 Parent(s): 3c016be

app update

Browse files
Files changed (6) hide show
  1. Dockerfile +6 -6
  2. app.py +43 -31
  3. app/questions.json +0 -0
  4. index.html +0 -29
  5. requirements.txt +2 -1
  6. static/index.html +65 -0
Dockerfile CHANGED
@@ -4,12 +4,12 @@ WORKDIR /app
4
 
5
  COPY . /app
6
 
7
- # Instala dependências otimizadas, incluindo sentencepiece
8
- RUN apt-get update && apt-get install -y \
9
- build-essential \
10
- libssl-dev \
11
- && pip install --no-cache-dir -r requirements.txt \
12
- && pip install --no-cache-dir transformers accelerate sentencepiece
13
 
14
  # Define o comando de execução
15
  CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
 
4
 
5
  COPY . /app
6
 
7
+ # Atualiza e instala dependências do sistema
8
+ RUN apt-get update && apt-get install -y build-essential libssl-dev && rm -rf /var/lib/apt/lists/*
9
+
10
+ # Instala dependências Python
11
+ RUN pip install --no-cache-dir -r requirements.txt
12
+ RUN pip install --no-cache-dir transformers accelerate sentencepiece torch
13
 
14
  # Define o comando de execução
15
  CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
app.py CHANGED
@@ -1,18 +1,29 @@
1
- from fastapi import FastAPI
2
  from transformers import AutoTokenizer, T5ForConditionalGeneration
 
 
3
  import json
4
  import os
5
  import logging
6
  import time
7
  import gc
 
8
 
9
- # Configura logging para capturar mais detalhes
10
  logging.basicConfig(level=logging.INFO)
11
  logger = logging.getLogger(__name__)
12
 
13
  app = FastAPI()
 
 
 
 
 
 
 
 
14
 
15
- # Carrega o arquivo questions.json
16
  try:
17
  with open("questions.json", "r", encoding="utf-8") as f:
18
  examples = json.load(f)
@@ -21,10 +32,10 @@ except FileNotFoundError:
21
  examples = []
22
  logger.warning("questions.json não encontrado, usando lista vazia.")
23
 
24
- # Função para carregar o modelo e tokenizer sob demanda
25
  def get_model():
26
  if not hasattr(get_model, "model_data"):
27
- logger.info("Carregando modelo e tokenizer pela primeira vez...")
28
  start_time = time.time()
29
  try:
30
  tokenizer = AutoTokenizer.from_pretrained(
@@ -43,14 +54,31 @@ def get_model():
43
  get_model.model_data = None
44
  return get_model.model_data
45
 
46
- # Força o carregamento inicial para testar
47
  logger.info("Testando carregamento inicial do modelo...")
48
  start_time = time.time()
49
  model_data = get_model()
50
  if model_data:
51
- logger.info(f"Modelo e tokenizer inicializados com sucesso em {time.time() - start_time:.2f} segundos.")
52
  else:
53
- logger.error("Falha na inicialização do modelo. Aplicação não será funcional.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
 
55
  def generate_question_from_prompt(theme, difficulty, example_question=None):
56
  model_data = get_model()
@@ -68,15 +96,13 @@ def generate_question_from_prompt(theme, difficulty, example_question=None):
68
  f"Explicação: {example_question['explanation']}"
69
  )
70
  prompt = (
71
- f"Baseado no exemplo: '{example_text}', gere uma questão de múltipla escolha "
72
- f"sobre '{theme}', dificuldade {difficulty}, estilo prova de residência médica da USP. "
73
- f"Formato: 'Enunciado clínico: [texto]. Alternativas: A) [opção], B) [opção], C) [opção], D) [opção]. "
74
  f"Gabarito: [letra]. Explicação: [texto].'"
75
  )
76
  else:
77
  prompt = (
78
- f"Gere uma questão de múltipla escolha sobre '{theme}', dificuldade {difficulty}, "
79
- f"estilo prova de residência médica da USP. Formato: "
80
  f"'Enunciado clínico: [texto]. Alternativas: A) [opção], B) [opção], C) [opção], D) [opção]. "
81
  f"Gabarito: [letra]. Explicação: [texto].'"
82
  )
@@ -84,24 +110,7 @@ def generate_question_from_prompt(theme, difficulty, example_question=None):
84
  inputs = tokenizer(prompt, return_tensors="pt", padding=True, truncation=True, max_length=512)
85
  outputs = model.generate(**inputs, max_new_tokens=256, temperature=0.7, top_p=0.9)
86
  response = tokenizer.decode(outputs[0], skip_special_tokens=True)
87
- parts = response.split("Alternativas:")
88
- if len(parts) > 1:
89
- question_part = parts[0].replace("Enunciado clínico:", "").strip()
90
- options_part = parts[1].split("Gabarito:")[0].strip()
91
- answer_part = parts[1].split("Gabarito:")[1].split("Explicação:")[0].strip()
92
- explanation_part = parts[1].split("Explicação:")[1].strip() if "Explicação:" in parts[1] else "Explicação padrão"
93
- options = [opt.strip() for opt in options_part.split(",")]
94
- if len(options) >= 4:
95
- result = {
96
- "question": f"Enunciado clínico: {question_part}",
97
- "options": [f"A) {options[0]}", f"B) {options[1]}", f"C) {options[2]}", f"D) {options[3]}"],
98
- "answer": answer_part,
99
- "explanation": explanation_part
100
- }
101
- # Libera memória
102
- gc.collect()
103
- return result
104
- result = {"question": response, "options": [], "answer": "", "explanation": "Explicação padrão"}
105
  gc.collect()
106
  return result
107
  except Exception as e:
@@ -110,6 +119,9 @@ def generate_question_from_prompt(theme, difficulty, example_question=None):
110
 
111
  @app.get("/generate")
112
  async def generate_question(theme: str, difficulty: str):
 
 
 
113
  example = examples[0] if examples else None
114
  return generate_question_from_prompt(theme, difficulty, example)
115
 
 
1
+ from fastapi import FastAPI, HTTPException
2
  from transformers import AutoTokenizer, T5ForConditionalGeneration
3
+ from fastapi.staticfiles import StaticFiles
4
+ from fastapi.middleware.cors import CORSMiddleware
5
  import json
6
  import os
7
  import logging
8
  import time
9
  import gc
10
+ import re
11
 
12
+ # Configura logging
13
  logging.basicConfig(level=logging.INFO)
14
  logger = logging.getLogger(__name__)
15
 
16
  app = FastAPI()
17
+ app.mount("/", StaticFiles(directory="static", html=True), name="static")
18
+ app.add_middleware(
19
+ CORSMiddleware,
20
+ allow_origins=["*"],
21
+ allow_credentials=True,
22
+ allow_methods=["*"],
23
+ allow_headers=["*"],
24
+ )
25
 
26
+ # Carrega questions.json
27
  try:
28
  with open("questions.json", "r", encoding="utf-8") as f:
29
  examples = json.load(f)
 
32
  examples = []
33
  logger.warning("questions.json não encontrado, usando lista vazia.")
34
 
35
+ # Função para carregar modelo e tokenizer
36
  def get_model():
37
  if not hasattr(get_model, "model_data"):
38
+ logger.info("Carregando modelo e tokenizer...")
39
  start_time = time.time()
40
  try:
41
  tokenizer = AutoTokenizer.from_pretrained(
 
54
  get_model.model_data = None
55
  return get_model.model_data
56
 
57
+ # Força carregamento inicial
58
  logger.info("Testando carregamento inicial do modelo...")
59
  start_time = time.time()
60
  model_data = get_model()
61
  if model_data:
62
+ logger.info(f"Modelo e tokenizer inicializados em {time.time() - start_time:.2f} segundos.")
63
  else:
64
+ logger.error("Falha na inicialização do modelo.")
65
+
66
+ def parse_model_output(response):
67
+ pattern = r"Enunciado clínico: (.*?)\s*Alternativas: (.*?)\s*Gabarito: (.*?)\s*Explicação: (.*)"
68
+ match = re.match(pattern, response, re.DOTALL)
69
+ if match:
70
+ question = match.group(1).strip()
71
+ options = [opt.strip() for opt in match.group(2).split(",")]
72
+ answer = match.group(3).strip()
73
+ explanation = match.group(4).strip()
74
+ if len(options) >= 4:
75
+ return {
76
+ "question": f"Enunciado clínico: {question}",
77
+ "options": [f"A) {options[0]}", f"B) {options[1]}", f"C) {options[2]}", f"D) {options[3]}"],
78
+ "answer": answer,
79
+ "explanation": explanation
80
+ }
81
+ return {"question": response, "options": [], "answer": "", "explanation": "Erro no parsing"}
82
 
83
  def generate_question_from_prompt(theme, difficulty, example_question=None):
84
  model_data = get_model()
 
96
  f"Explicação: {example_question['explanation']}"
97
  )
98
  prompt = (
99
+ f"Baseado no exemplo: '{example_text}', gere uma questão sobre '{theme}', dificuldade {difficulty}, "
100
+ f"estilo prova USP. Formato: 'Enunciado clínico: [texto]. Alternativas: A) [opção], B) [opção], C) [opção], D) [opção]. "
 
101
  f"Gabarito: [letra]. Explicação: [texto].'"
102
  )
103
  else:
104
  prompt = (
105
+ f"Gere uma questão sobre '{theme}', dificuldade {difficulty}, estilo prova USP. Formato: "
 
106
  f"'Enunciado clínico: [texto]. Alternativas: A) [opção], B) [opção], C) [opção], D) [opção]. "
107
  f"Gabarito: [letra]. Explicação: [texto].'"
108
  )
 
110
  inputs = tokenizer(prompt, return_tensors="pt", padding=True, truncation=True, max_length=512)
111
  outputs = model.generate(**inputs, max_new_tokens=256, temperature=0.7, top_p=0.9)
112
  response = tokenizer.decode(outputs[0], skip_special_tokens=True)
113
+ result = parse_model_output(response)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  gc.collect()
115
  return result
116
  except Exception as e:
 
119
 
120
  @app.get("/generate")
121
  async def generate_question(theme: str, difficulty: str):
122
+ valid_difficulties = ["fácil", "médio", "difícil"]
123
+ if not theme or difficulty.lower() not in valid_difficulties:
124
+ raise HTTPException(status_code=400, detail="Tema inválido ou dificuldade deve ser 'fácil', 'médio' ou 'difícil'.")
125
  example = examples[0] if examples else None
126
  return generate_question_from_prompt(theme, difficulty, example)
127
 
app/questions.json DELETED
The diff for this file is too large to render. See raw diff
 
index.html DELETED
@@ -1,29 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="pt-br">
3
- <head>
4
- <title>Simulado USP</title>
5
- <style>
6
- body { font-family: Arial, sans-serif; margin: 20px; }
7
- h1 { color: #2c3e50; }
8
- button { padding: 10px 20px; background: #3498db; color: white; border: none; cursor: pointer; }
9
- button:hover { background: #2980b9; }
10
- #questions { margin-top: 20px; }
11
- ul { list-style-type: upper-alpha; padding-left: 20px; }
12
- li { margin-bottom: 5px; }
13
- .question { border-bottom: 1px solid #ecf0f1; padding-bottom: 10px; margin-bottom: 10px; }
14
- </style>
15
- </head>
16
- <body>
17
- <h1>Simulado</h1>
18
- <button onclick="loadSimulado()">Carregar</button>
19
- <div id="questions"></div>
20
- <script>
21
- async function loadSimulado() {
22
- const response = await fetch('/simulado?num_questions=5');
23
- const data = await response.json();
24
- const questionsDiv = document.getElementById('questions');
25
- questionsDiv.innerHTML = data.simulado.map(q => `<div class="question">${q.question.replace(/\n/g, '<br>')}</div>`).join('');
26
- }
27
- </script>
28
- </body>
29
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
requirements.txt CHANGED
@@ -3,4 +3,5 @@ uvicorn==0.23.2
3
  transformers==4.45.0
4
  accelerate==0.21.0
5
  huggingface_hub
6
- sentencepiece
 
 
3
  transformers==4.45.0
4
  accelerate==0.21.0
5
  huggingface_hub
6
+ sentencepiece
7
+ torch
static/index.html ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="pt-br">
3
+ <head>
4
+ <title>Simulado USP</title>
5
+ <style>
6
+ body { font-family: Arial, sans-serif; margin: 20px; }
7
+ h1 { color: #2c3e50; }
8
+ button { padding: 10px 20px; background: #3498db; color: white; border: none; cursor: pointer; }
9
+ button:hover { background: #2980b9; }
10
+ #questions { margin-top: 20px; }
11
+ ul { list-style-type: upper-alpha; padding-left: 20px; }
12
+ li { margin-bottom: 5px; }
13
+ .question { border-bottom: 1px solid #ecf0f1; padding-bottom: 10px; margin-bottom: 10px; }
14
+ .answer { color: #27ae60; }
15
+ .explanation { font-style: italic; color: #7f8c8d; }
16
+ .error { color: #e74c3c; }
17
+ </style>
18
+ </head>
19
+ <body>
20
+ <h1>Simulado</h1>
21
+ <button onclick="loadSimulado()">Carregar</button>
22
+ <div id="questions"></div>
23
+ <script>
24
+ async function loadSimulado() {
25
+ console.log('Iniciando carregamento do simulado...');
26
+ try {
27
+ const response = await fetch('/simulado?num_questions=5');
28
+ console.log('Requisição enviada, status:', response.status);
29
+ if (!response.ok) {
30
+ throw new Error(`Erro na requisição: ${response.status} ${response.statusText}`);
31
+ }
32
+ const data = await response.json();
33
+ console.log('Dados recebidos:', data);
34
+ const questionsDiv = document.getElementById('questions');
35
+ questionsDiv.innerHTML = data.simulado.map((q, index) => `
36
+ <div class="question">
37
+ <p><strong>Questão ${index + 1}:</strong> ${q.question.replace(/\n/g, '<br>')}</p>
38
+ <ul>
39
+ ${q.options.map((opt, i) => `
40
+ <li>
41
+ <input type="radio" name="q${index}" value="${opt}" id="q${index}_${i}">
42
+ <label for="q${index}_${i}">${opt}</label>
43
+ </li>
44
+ `).join('')}
45
+ </ul>
46
+ <button onclick="showAnswer(${index})">Verificar Resposta</button>
47
+ <p id="answer${index}" style="display: none;" class="answer">
48
+ <strong>Gabarito:</strong> ${q.answer}<br>
49
+ <strong>Explicação:</strong> ${q.explanation.replace(/\n/g, '<br>')}
50
+ </p>
51
+ </div>
52
+ `).join('');
53
+ } catch (error) {
54
+ console.error('Erro ao carregar simulado:', error);
55
+ document.getElementById('questions').innerHTML = `<p class="error">Erro ao carregar simulado: ${error.message}</p>`;
56
+ }
57
+ }
58
+
59
+ function showAnswer(index) {
60
+ console.log(`Exibindo resposta para questão ${index + 1}`);
61
+ document.getElementById(`answer${index}`).style.display = 'block';
62
+ }
63
+ </script>
64
+ </body>
65
+ </html>