Update app.py
Browse files
app.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
| 1 |
-
# -*- coding: utf-8 -*-
|
| 2 |
import os
|
| 3 |
import time
|
| 4 |
import google.generativeai as genai
|
|
@@ -6,75 +5,31 @@ import streamlit as st
|
|
| 6 |
from streamlit_extras.colored_header import colored_header
|
| 7 |
from streamlit_extras.add_vertical_space import add_vertical_space
|
| 8 |
import markdown
|
| 9 |
-
import pyperclip # pyperclip 임포트 추가
|
| 10 |
|
| 11 |
-
#
|
| 12 |
-
|
| 13 |
-
# Google Gemini API 값 설정 (Streamlit secrets 또는 환경 변수 사용 권장)
|
| 14 |
-
# 로컬 테스트 시: os.environ["GEMINI_API_KEY"] = "YOUR_API_KEY"
|
| 15 |
-
api_key_configured = False
|
| 16 |
-
try:
|
| 17 |
-
# secrets에서 먼저 시도
|
| 18 |
-
gemini_api_key = st.secrets["GEMINI_API_KEY"]
|
| 19 |
-
genai.configure(api_key=gemini_api_key)
|
| 20 |
-
api_key_configured = True
|
| 21 |
-
# st.success("Secrets에서 Gemini API 키 로드 성공!") # 디버깅용
|
| 22 |
-
except (KeyError, FileNotFoundError):
|
| 23 |
-
# secrets에 없으면 환경 변수에서 시도
|
| 24 |
-
try:
|
| 25 |
-
gemini_api_key = os.environ["GEMINI_API_KEY"]
|
| 26 |
-
genai.configure(api_key=gemini_api_key)
|
| 27 |
-
api_key_configured = True
|
| 28 |
-
# st.info("환경 변수에서 Gemini API 키 로드 성공!") # 디버깅용
|
| 29 |
-
except KeyError:
|
| 30 |
-
# 둘 다 없으면 오류 메시지 표시
|
| 31 |
-
st.error("⚠️ Gemini API 키가 설정되지 않았습니다. Streamlit secrets 또는 환경 변수에 'GEMINI_API_KEY'를 설정해주세요.")
|
| 32 |
-
st.stop() # API 키 없으면 앱 중지
|
| 33 |
-
|
| 34 |
-
# API 키 설정 확인 (만약을 위한 추가 검사)
|
| 35 |
-
if not api_key_configured:
|
| 36 |
-
st.error("API 키 설정에 실패했습니다. 애플리케이션을 중지합니다.")
|
| 37 |
-
st.stop()
|
| 38 |
|
| 39 |
# 모델 설정
|
| 40 |
generation_config = {
|
| 41 |
"temperature": 0.7,
|
| 42 |
"top_p": 0.95,
|
| 43 |
"top_k": 64,
|
| 44 |
-
"max_output_tokens":
|
| 45 |
"response_mime_type": "text/plain",
|
| 46 |
}
|
| 47 |
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
# model_name = "gemini-2.5-flash-preview-04-17" # -> 유효하지 않을 가능성 있음
|
| 53 |
-
model_name = "gemini-2.5-flash-preview-04-17" # 안정적인 최신 flash 모델로 변경
|
| 54 |
-
|
| 55 |
-
try:
|
| 56 |
-
model = genai.GenerativeModel(
|
| 57 |
-
model_name=model_name,
|
| 58 |
-
generation_config=generation_config,
|
| 59 |
-
# safety_settings = Adjust safety settings
|
| 60 |
-
# See https://ai.google.dev/gemini-api/docs/safety-settings
|
| 61 |
-
)
|
| 62 |
-
# st.info(f"'{model_name}' 모델 로드 성공!") # 디버깅용
|
| 63 |
-
except Exception as e:
|
| 64 |
-
st.error(f"Gemini 모델 ('{model_name}') 로딩 중 오류 발생: {e}")
|
| 65 |
-
st.error("올바른 모델 이름인지 확인하거나 Google AI Studio에서 사용 가능한 모델 목록을 확인하세요.")
|
| 66 |
-
st.stop()
|
| 67 |
-
|
| 68 |
-
# --- 함수 정의 ---
|
| 69 |
|
| 70 |
-
def
|
| 71 |
"""
|
| 72 |
주어진 조건에 따라 설명문 텍스트, 어휘 목록, 독해 문제를 생성하고 스트리밍으로 출력합니다.
|
| 73 |
"""
|
| 74 |
# 상세화된 프롬프트: 설명문 작성 + 어휘 목록 + 독해 문제 출제 요청 추가
|
| 75 |
prompt = f"""
|
| 76 |
# 지시사항
|
| 77 |
-
|
| 78 |
## 1. 설명문 작성
|
| 79 |
- **대상 독자:** 초등학교 {grade} 학생
|
| 80 |
- **주제:** '{topic}'
|
|
@@ -85,7 +40,6 @@ def generate_text_and_questions(grade, num_paragraphs, sentences_per_paragraph,
|
|
| 85 |
- 줄글 형식으로 작성, 문단 구분 명확히 (빈 줄 사용)
|
| 86 |
- 각 문단의 첫 문장 또는 마지막 문장이 중심 문장이 되도록 작성
|
| 87 |
- **제목:** 글의 맨 처음에 내용을 잘 나타내는 제목을 **크고 굵은 글씨**로 작성 (Markdown 형식: '** 제목 **')
|
| 88 |
-
|
| 89 |
## 2. 어휘 목록 작성
|
| 90 |
- **선정 기준:** 본문 내용 중 초등학교 {grade}에게 어려울 수 있는 단어, 한자어, 학습 용어
|
| 91 |
- **설명 방식:** 해당 학년 수준에 맞는 쉬운 유의어 또는 풀이 제공
|
|
@@ -96,7 +50,6 @@ def generate_text_and_questions(grade, num_paragraphs, sentences_per_paragraph,
|
|
| 96 |
* 단어2: 쉬운 설명/유의어
|
| 97 |
...
|
| 98 |
```
|
| 99 |
-
|
| 100 |
## 3. 독해 문제 및 정답 출제
|
| 101 |
- **출제 기반:** **반드시 위에서 생성된 설명문 내용만을 바탕으로** 출제
|
| 102 |
- **문제 수:** 총 7~8개
|
|
@@ -117,161 +70,62 @@ def generate_text_and_questions(grade, num_paragraphs, sentences_per_paragraph,
|
|
| 117 |
① 선택지1 ② 선택지2 ③ 선택지3 ④ 선택지4 ⑤ 선택지5 (선다형의 경우)
|
| 118 |
2. [문제 내용 - 단답형 또는 5지 선다형]
|
| 119 |
... (총 7~8개)
|
| 120 |
-
|
| 121 |
### 정답
|
| 122 |
1. [정답 내용 또는 번호]
|
| 123 |
2. [정답 내�� 또는 번호]
|
| 124 |
...
|
| 125 |
```
|
| 126 |
-
|
| 127 |
## # 출력 요구사항
|
| 128 |
- 위의 모든 지시사항(설명문, 어휘 목록, 독해 문제, 정답)을 **하나의 응답**으로 이어서 생성해주세요.
|
| 129 |
- 각 섹션(제목, 본문, 어휘 목록, 독해 문제, 정답) 구분을 Markdown 제목(##, ###)과 빈 줄로 명확히 해주세요.
|
| 130 |
- 모든 내용은 초등학교 {grade} 눈높이에 맞춰 작성해주세요.
|
| 131 |
-
"""
|
| 132 |
-
|
| 133 |
-
full_response_text = "" # 전체 응답 저장 변수 초기화
|
| 134 |
-
output_area = st.empty() # 스트리밍 출력을 위한 빈 공간 생성
|
| 135 |
-
blocked = False # 콘텐츠 차단 플래그
|
| 136 |
|
|
|
|
| 137 |
try:
|
| 138 |
response = model.generate_content(prompt, stream=True)
|
| 139 |
for chunk in response:
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
blocked = True
|
| 146 |
-
break # 차단 시 스트리밍 중단
|
| 147 |
-
|
| 148 |
-
# 응답 텍스트 처리
|
| 149 |
-
try:
|
| 150 |
-
# 최신 API는 chunk.text로 바로 접근 가능할 수 있음
|
| 151 |
-
chunk_text = chunk.text
|
| 152 |
-
except AttributeError:
|
| 153 |
-
# 이전 방식 호환 (candidates 확인)
|
| 154 |
-
if chunk.candidates and chunk.candidates[0].content and chunk.candidates[0].content.parts and chunk.candidates[0].content.parts[0].text:
|
| 155 |
-
chunk_text = chunk.candidates[0].content.parts[0].text
|
| 156 |
-
else:
|
| 157 |
-
# 예상치 못한 청크 형식일 경우 건너뛰기 또는 로깅
|
| 158 |
-
# st.warning(f"Unexpected chunk format: {chunk}") # 디버깅용
|
| 159 |
-
chunk_text = "" # 또는 continue
|
| 160 |
-
|
| 161 |
-
if chunk_text:
|
| 162 |
-
full_response_text += chunk_text
|
| 163 |
-
# Markdown을 HTML로 변환하여 스트리밍 출력 (표, 코드 블록, 줄바꿈 지원)
|
| 164 |
-
html_text = markdown.markdown(full_response_text, extensions=['markdown.extensions.tables', 'markdown.extensions.fenced_code', 'markdown.extensions.nl2br'])
|
| 165 |
-
output_area.markdown(html_text, unsafe_allow_html=True)
|
| 166 |
-
time.sleep(0.05) # 스트리밍 지연 (조절 가능)
|
| 167 |
|
| 168 |
except Exception as e:
|
| 169 |
-
st.error(f"
|
| 170 |
-
|
| 171 |
-
return "" # 오류 시 빈 문자열 반환
|
| 172 |
-
|
| 173 |
-
# 스트리밍 완료 후 처리 (차단되지 않았을 경우)
|
| 174 |
-
if not blocked and full_response_text:
|
| 175 |
-
st.markdown("---") # 구분선 추가
|
| 176 |
-
copy_button = st.button("📄 생성된 내용 전체 복사")
|
| 177 |
-
if copy_button:
|
| 178 |
-
try:
|
| 179 |
-
# 마크다운 형식으로 복사
|
| 180 |
-
pyperclip.copy(full_response_text)
|
| 181 |
-
st.success("클립보드에 복사되었습니다! (마크다운 형식)")
|
| 182 |
-
except Exception as e:
|
| 183 |
-
# pyperclip 에러 처리 (예: 서버 환경 등에서 클립보드 접근 불가 시)
|
| 184 |
-
st.warning(f"클립보드 자동 복사 중 오류 발생: {e}\n서버 환경에서는 클립보드 복사가 지원되지 않을 수 있습니다.\n아래 텍스트 영역에서 수동으로 복사해주세요.")
|
| 185 |
-
# 복사 실패 시 수동 복사를 위해 텍스트 영역 추가 표시
|
| 186 |
-
st.text_area("복사할 내용 (마크다운):", full_response_text, height=300)
|
| 187 |
|
| 188 |
-
elif blocked:
|
| 189 |
-
return "" # 차단 시 빈 문자열 반환
|
| 190 |
|
| 191 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 192 |
|
| 193 |
-
|
| 194 |
|
| 195 |
-
st.set_page_config(layout="wide") # 넓은 레이아웃 사용
|
| 196 |
|
| 197 |
-
#
|
| 198 |
colored_header(
|
| 199 |
-
label="📜
|
| 200 |
-
description="
|
| 201 |
-
color_name="
|
| 202 |
)
|
|
|
|
| 203 |
add_vertical_space(1)
|
| 204 |
|
| 205 |
-
# 사이드바: 옵션 설정
|
| 206 |
with st.sidebar:
|
| 207 |
-
st.header("
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
grade_str = st.selectbox("대상 학년", grade_options, index=2) # 기본값 3학년
|
| 214 |
-
grade = grade_str.replace("학년", "") # '3학년' -> '3'
|
| 215 |
-
|
| 216 |
-
# 문단 수 설정
|
| 217 |
-
num_paragraphs = st.number_input("문단 수 (권장 3~5)", min_value=2, max_value=10, value=3)
|
| 218 |
|
| 219 |
-
|
| 220 |
-
sentences_per_paragraph = st.number_input("문단 당 문장 수 (권장 3~5)", min_value=2, max_value=10, value=4)
|
| 221 |
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
"설명 방식",
|
| 225 |
-
["정의와 예시", "비교와 대조", "분류와 구분", "분석 (구성 요소)", "인과 (원인과 결과)", "서사 (시간 순서 또는 과정)"],
|
| 226 |
-
index=0 # 기본값 '정의와 예시'
|
| 227 |
-
)
|
| 228 |
|
| 229 |
-
# 주제 입력
|
| 230 |
-
topic = st.text_area(
|
| 231 |
-
"✏️ 글의 주제를 입력하세요",
|
| 232 |
-
height=150,
|
| 233 |
-
placeholder="예) 지구 온난화, 스마트폰의 장단점, 우리나라의 사계절, 재활용의 중요성, 좋아하는 동물 설명하기, 독도의 중요성"
|
| 234 |
-
)
|
| 235 |
|
| 236 |
-
add_vertical_space(2)
|
| 237 |
-
st.caption("ℹ️ 주제가 명확하고 구체적일수록 좋은 결과가 나옵니다.")
|
| 238 |
-
st.caption(f"현재 사용 모델: {model_name}") # 사용 중인 모델 표시
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
# 메인 화면: 생성 버튼 및 출력 영역
|
| 242 |
-
st.subheader("✨ 읽기 자료 생성 결과")
|
| 243 |
-
add_vertical_space(1)
|
| 244 |
-
|
| 245 |
-
# 생성 버튼
|
| 246 |
-
generate_button = st.button("🚀 읽기 자료 생성하기", type="primary", use_container_width=True)
|
| 247 |
-
|
| 248 |
-
# 생성 버튼 클릭 시 로직 실행
|
| 249 |
if generate_button:
|
| 250 |
-
|
| 251 |
-
st.warning("⚠️ 주제를 입력해주세요!")
|
| 252 |
-
# API 키 존재 여부는 시작 시점에 이미 확인 및 처리되었으므로 여기서 다시 확인할 필요 없음
|
| 253 |
-
else:
|
| 254 |
-
# 주제 입력 확인 후 진행
|
| 255 |
-
with st.spinner("AI가 열심히 글을 쓰고 문제를 만들고 있어요... 잠시만 기다려주세요! 🤔✍️"):
|
| 256 |
-
# 함수 호출 (출력은 함수 내부에서 스트리밍으로 처리됨)
|
| 257 |
-
generated_content = generate_text_and_questions(
|
| 258 |
-
grade, # 숫자 학년 전달
|
| 259 |
-
num_paragraphs,
|
| 260 |
-
sentences_per_paragraph,
|
| 261 |
-
structure,
|
| 262 |
-
topic
|
| 263 |
-
)
|
| 264 |
-
# 생성 완료 후 별도 메시지는 함수 내에서 처리 (복사 버튼 표시 등)
|
| 265 |
-
# 또는 스피너 완료 자체가 성공 표시가 됨
|
| 266 |
-
# 주석 처리된 부분의 pass 삭제: IndentationError 수정
|
| 267 |
-
# if generated_content:
|
| 268 |
-
# st.success("읽기 자료 생성이 완료되었습니다!") # 성공 메시지 필요 시 주석 해제
|
| 269 |
-
# elif not generated_content: # 생성된 내용이 없는 경우 (오류 또는 차단)
|
| 270 |
-
# 오류 메시지는 함수 내부에서 이미 표시됨
|
| 271 |
-
# st.error("읽기 자료 생성에 실패했습니다.")
|
| 272 |
-
# pass # <- 이 줄이 IndentationError의 원인. 삭제됨.
|
| 273 |
-
pass # 이 pass는 else 블록에 속하며, 현재는 아무 작업도 하지 않음을 나타냄 (필요시 로직 추가 가능)
|
| 274 |
-
|
| 275 |
-
# 만약 스트리밍 출력을 함수 밖에서 제어하고 싶다면,
|
| 276 |
-
# output_area = st.empty() 를 여기 두고, 함수는 text chunk 만 반환하게 수정 필요
|
| 277 |
-
# 하지만 현재 구조(함수 내에서 스트리밍 처리 및 버튼 표시)가 더 간결함.
|
|
|
|
|
|
|
| 1 |
import os
|
| 2 |
import time
|
| 3 |
import google.generativeai as genai
|
|
|
|
| 5 |
from streamlit_extras.colored_header import colored_header
|
| 6 |
from streamlit_extras.add_vertical_space import add_vertical_space
|
| 7 |
import markdown
|
|
|
|
| 8 |
|
| 9 |
+
# Google Gemini API 값 설정
|
| 10 |
+
genai.configure(api_key=os.environ["GEMINI_API_KEY"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
# 모델 설정
|
| 13 |
generation_config = {
|
| 14 |
"temperature": 0.7,
|
| 15 |
"top_p": 0.95,
|
| 16 |
"top_k": 64,
|
| 17 |
+
"max_output_tokens": 8000,
|
| 18 |
"response_mime_type": "text/plain",
|
| 19 |
}
|
| 20 |
|
| 21 |
+
model = genai.GenerativeModel(
|
| 22 |
+
model_name="gemini-2.0-flash-thinking-exp-01-21",
|
| 23 |
+
generation_config=generation_config,
|
| 24 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
|
| 26 |
+
def generate_text(grade, num_paragraphs, sentences_per_paragraph, structure, topic):
|
| 27 |
"""
|
| 28 |
주어진 조건에 따라 설명문 텍스트, 어휘 목록, 독해 문제를 생성하고 스트리밍으로 출력합니다.
|
| 29 |
"""
|
| 30 |
# 상세화된 프롬프트: 설명문 작성 + 어휘 목록 + 독해 문제 출제 요청 추가
|
| 31 |
prompt = f"""
|
| 32 |
# 지시사항
|
|
|
|
| 33 |
## 1. 설명문 작성
|
| 34 |
- **대상 독자:** 초등학교 {grade} 학생
|
| 35 |
- **주제:** '{topic}'
|
|
|
|
| 40 |
- 줄글 형식으로 작성, 문단 구분 명확히 (빈 줄 사용)
|
| 41 |
- 각 문단의 첫 문장 또는 마지막 문장이 중심 문장이 되도록 작성
|
| 42 |
- **제목:** 글의 맨 처음에 내용을 잘 나타내는 제목을 **크고 굵은 글씨**로 작성 (Markdown 형식: '** 제목 **')
|
|
|
|
| 43 |
## 2. 어휘 목록 작성
|
| 44 |
- **선정 기준:** 본문 내용 중 초등학교 {grade}에게 어려울 수 있는 단어, 한자어, 학습 용어
|
| 45 |
- **설명 방식:** 해당 학년 수준에 맞는 쉬운 유의어 또는 풀이 제공
|
|
|
|
| 50 |
* 단어2: 쉬운 설명/유의어
|
| 51 |
...
|
| 52 |
```
|
|
|
|
| 53 |
## 3. 독해 문제 및 정답 출제
|
| 54 |
- **출제 기반:** **반드시 위에서 생성된 설명문 내용만을 바탕으로** 출제
|
| 55 |
- **문제 수:** 총 7~8개
|
|
|
|
| 70 |
① 선택지1 ② 선택지2 ③ 선택지3 ④ 선택지4 ⑤ 선택지5 (선다형의 경우)
|
| 71 |
2. [문제 내용 - 단답형 또는 5지 선다형]
|
| 72 |
... (총 7~8개)
|
|
|
|
| 73 |
### 정답
|
| 74 |
1. [정답 내용 또는 번호]
|
| 75 |
2. [정답 내�� 또는 번호]
|
| 76 |
...
|
| 77 |
```
|
|
|
|
| 78 |
## # 출력 요구사항
|
| 79 |
- 위의 모든 지시사항(설명문, 어휘 목록, 독해 문제, 정답)을 **하나의 응답**으로 이어서 생성해주세요.
|
| 80 |
- 각 섹션(제목, 본문, 어휘 목록, 독해 문제, 정답) 구분을 Markdown 제목(##, ###)과 빈 줄로 명확히 해주세요.
|
| 81 |
- 모든 내용은 초등학교 {grade} 눈높이에 맞춰 작성해주세요.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 82 |
|
| 83 |
+
full_text = "" # 전체 텍스트 저장 변수 초기화
|
| 84 |
try:
|
| 85 |
response = model.generate_content(prompt, stream=True)
|
| 86 |
for chunk in response:
|
| 87 |
+
full_text += chunk.text
|
| 88 |
+
# Markdown to HTML 변환
|
| 89 |
+
html_text = markdown.markdown(full_text, extensions=['tables'])
|
| 90 |
+
output_area.markdown(html_text, unsafe_allow_html=True)
|
| 91 |
+
time.sleep(0.1) # 지연 추가 (필요에 따라 조절)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
|
| 93 |
except Exception as e:
|
| 94 |
+
st.error(f"에러 발생: {str(e)}")
|
| 95 |
+
return ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
|
|
|
|
|
|
|
| 97 |
|
| 98 |
+
# 출력 및 복사 기능 추가
|
| 99 |
+
copy_button = st.button("출력 내용 복사")
|
| 100 |
+
if copy_button:
|
| 101 |
+
pyperclip.copy(full_text)
|
| 102 |
+
st.success("클립보드에 복사되었습니다!")
|
| 103 |
|
| 104 |
+
return full_text
|
| 105 |
|
|
|
|
| 106 |
|
| 107 |
+
# Streamlit 인터페이스 설정
|
| 108 |
colored_header(
|
| 109 |
+
label="📜 초등학생을 위한 읽기 자료 생성기",
|
| 110 |
+
description="주제를 입력하면 초등학생이 이해하기 쉬운 읽기 자료를 만들어줍니다.",
|
| 111 |
+
color_name="red-70",
|
| 112 |
)
|
| 113 |
+
|
| 114 |
add_vertical_space(1)
|
| 115 |
|
|
|
|
| 116 |
with st.sidebar:
|
| 117 |
+
st.header("옵션 설정")
|
| 118 |
+
grade = st.selectbox("학년", [f"초등학교 {i}학년" for i in range(1, 7)]) # 드롭다운 수정
|
| 119 |
+
num_paragraphs = st.number_input("문단 수", min_value=1, value=3)
|
| 120 |
+
sentences_per_paragraph = st.number_input("문단 당 문장 수", min_value=1, value=3) # 드롭다운 수정
|
| 121 |
+
structure = st.selectbox("설명문 구조", ["정의와 예시", "비교와 대조", "분류", "분석", "인과", "순서"], index=0)
|
| 122 |
+
topic = st.text_area("✏️ 주제 및 내용을 입력하세요 ", height=200)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
|
| 124 |
+
generate_button = st.button("읽기 자료 생성")
|
|
|
|
| 125 |
|
| 126 |
+
# 출력 영역을 함수 외부에 정의
|
| 127 |
+
output_area = st.empty()
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
if generate_button:
|
| 131 |
+
generate_text(grade, num_paragraphs, sentences_per_paragraph, structure, topic)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|