ll7098ll commited on
Commit
86cdc58
·
verified ·
1 Parent(s): b428688

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +105 -40
app.py CHANGED
@@ -2,7 +2,7 @@ import time
2
  import streamlit as st
3
  import google.generativeai as genai
4
  from streamlit_extras.colored_header import colored_header
5
- import markdown
6
 
7
  # --- Must be the first Streamlit command ---
8
  st.set_page_config(page_title="MBTI 관계 시뮬레이터", page_icon="🤝", layout="wide")
@@ -11,34 +11,50 @@ st.set_page_config(page_title="MBTI 관계 시뮬레이터", page_icon="🤝", l
11
 
12
  # Google Gemini API Key 설정 (Streamlit secrets 사용)
13
  try:
14
- genai.configure(api_key=st.secrets["GEMINI_API_KEY"])
15
- except KeyError:
16
- st.error("GEMINI_API_KEY secrets 설정을 확인해주세요.")
17
- st.stop()
 
 
 
18
  except Exception as e:
19
- st.error(f"API 키 설정 중 오류 발생: {e}")
20
  st.stop()
21
 
 
22
  # 모델 설정
23
  generation_config = {
24
  "temperature": 0.75,
25
  "top_p": 0.8,
26
  "top_k": 40,
27
- "max_output_tokens": 8192,
28
  }
29
 
30
  # 특정 모델 이름으로 설정 (사용자 요청 반영)
31
- target_model_name = "gemini-2.0-flash-thinking-exp-01-21"
 
 
32
 
33
  try:
34
  model = genai.GenerativeModel(
35
  model_name=target_model_name,
36
  generation_config=generation_config,
 
 
 
 
 
 
 
37
  )
38
- # Model display removed - st.info(f"Using model: {target_model_name}")
 
39
  except Exception as e:
40
  st.error(f"Gemini 모델 '{target_model_name}' 로딩 중 오류 발생: {e}")
41
- st.error("모델 이름을 다시 확인하거나, 사용 가능한 모델 목록을 확인하여 다른 모델을 시도해보세요.")
 
 
42
  st.stop()
43
 
44
 
@@ -110,11 +126,23 @@ def generate_relationship_scenario(people, relationship, situation):
110
 
111
  # 참여자 정보 문자열 생성 함수
112
  def create_type_info(person):
113
- type_description = mbti_descriptions.get(person['type'], '알 수 유형') # 상세 설명 사용
114
- return f"{person['name']} ({person['gender']}, {person['type']}): {type_description}"
 
 
 
 
 
 
 
 
 
 
 
 
115
 
116
- # 참여자 정보 목록 생성
117
- people_info = "\n".join([f"- {create_type_info(person)}" for person in people])
118
 
119
  # 시스템 프롬프트 (개선됨: 속마음/의도, 상호작용 역학, 오해 지점, 명확한 구조 강조, 테이블 형식 지양)
120
  system_prompt = f"""
@@ -124,7 +152,7 @@ def generate_relationship_scenario(people, relationship, situation):
124
 
125
  * **참여자:**
126
  {people_info}
127
- * **관계 유형:** {relationship} ({relationship_types_two.get(relationship, relationship_types_multiple.get(relationship, ''))})
128
  * **주어진 상황:** {situation}
129
 
130
  **[요청 사항]**
@@ -147,7 +175,7 @@ def generate_relationship_scenario(people, relationship, situation):
147
 
148
  **4. 관계 개선을 위한 실질적 조언:**
149
  * **각 참여자에게 맞춤화된 조언**을 제공해주세요. 이 조언은 시나리오에서 드러난 **구체적인 상호작용, 속마음, 오해 지점**을 직접적으로 다루어야 합니다.
150
- * 서로를 더 잘 이해하고 **건강한 관계**를 구축하기 위해 각자가 **시도해볼 수 있는 구체적인 말과 행동**을 제안해주세요. (예: "A(INTJ)님, B(ENFP)님이 아이디어를 낼 때 즉시 분석하기보다, 먼저 '흥미로운 생각인데!'라고 반응하며 B님의 열정을 인정해주세요. 그 후에 함께 현실적인 부분을 논의하는 것이 좋습니다.")
151
  * 조언에는 반드시 **모든 참여자의 이름, 성별, MBTI 유형**이 명시되어야 합니다.
152
 
153
  **[출력 형식]**
@@ -164,18 +192,25 @@ def generate_relationship_scenario(people, relationship, situation):
164
  response = model.generate_content([system_prompt], stream=True)
165
  response_container = st.empty() # 응답 표시 영역 미리 확보
166
  for chunk in response:
167
- # chunk.text가 None이 아닌 경우에만 처리
168
  if chunk.text:
169
- full_text += chunk.text
170
- # Markdown으로 변환하여 실시간 업데이트
171
- html_text = markdown.markdown(full_text)
172
- response_container.markdown(html_text, unsafe_allow_html=True)
173
- time.sleep(0.02) # 약간의 딜레이로 부드러운 스트리밍 효과
174
  return full_text # 최종 전체 텍스트 반환
175
 
176
  except Exception as e:
177
  st.error(f"시나리오 생성 중 오류 발생: {e}")
 
178
  st.error(f"오류 상세 정보: {type(e).__name__} - {e}")
 
 
 
 
 
 
 
179
  return ""
180
 
181
 
@@ -183,7 +218,7 @@ def generate_relationship_scenario(people, relationship, situation):
183
 
184
  # 헤더 색상 적용
185
  colored_header(
186
- label="🤝 MBTI 관계 시뮬레이터 v2.0",
187
  description="참여자들의 MBTI, 관계, 상황을 입력하여 심층적인 관계 시나리오와 조언을 받아보세요.",
188
  color_name="blue-70"
189
  )
@@ -200,14 +235,23 @@ with st.sidebar:
200
  num_people = st.number_input("참여자 수", min_value=2, max_value=5, value=2, key="num_people",
201
  help="최소 2명, 최대 5명까지 설정할 수 있습니다.")
202
 
203
- people = []
204
  # 참여자 수에 맞춰 동적으로 입력 필드 생성
205
  for i in range(num_people):
206
- with st.expander(f"👤 참여자 {i+1}", expanded=False): # 기본으로 expander 닫힘
 
207
  person = {}
208
- person['name'] = st.text_input(f"이름/닉네임", key=f"name_{i}", placeholder=f": 철수")
 
 
209
  person['gender'] = st.radio("성별", ["남성", "여성", "기타"], key=f"gender_{i}", horizontal=True)
210
- person['type'] = st.selectbox(f"MBTI 유형", list(mbti_types.keys()), key=f"type_{i}", index=i % len(mbti_types))
 
 
 
 
 
 
211
 
212
  st.divider()
213
 
@@ -222,7 +266,7 @@ with st.sidebar:
222
  relationship_options = relationship_types_multiple
223
  relationship = st.selectbox("관계 유형", list(relationship_options.keys()), key="relationship")
224
 
225
- situation = st.text_area("구체적인 상황", key="situation", height=100, # 높이 줄임
226
  placeholder="예: 중요한 프로젝트 마감일을 앞두고 의견 충돌 발생")
227
 
228
  st.divider()
@@ -234,34 +278,55 @@ with st.sidebar:
234
 
235
  # Main area - Scenario output
236
  st.header("💡 생성된 시나리오")
 
237
  scenario_output_area = st.container()
 
238
 
239
  # Button click logic
240
  if generate_button:
241
- # Input validation
242
  valid_input = True
243
- if not all(p.get('name') for p in people):
 
244
  st.error("모든 참여자의 이름을 입력해주세요.")
245
  valid_input = False
246
- if not situation:
 
247
  st.error("구체적인 상황을 입력해주세요.")
248
  valid_input = False
 
249
  if not relationship:
250
  st.error("관계 유형을 선택해주세요.")
251
  valid_input = False
 
 
 
 
 
252
 
253
  if valid_input:
254
- with st.spinner("🧠 MBTI 전문가가 시나리오 분석 및 생성 중..."):
255
- full_text_result = generate_relationship_scenario(people, relationship, situation)
256
-
257
- if full_text_result:
258
- scenario_output_area.markdown(markdown.markdown(full_text_result), unsafe_allow_html=True)
259
- st.success("🎉 시나리오 생성 완료!")
260
- else:
261
- pass # Error message already shown in generate_relationship_scenario
 
 
 
 
 
 
 
 
 
262
  else:
263
  st.warning("입력값을 다시 확인해주세요.")
264
 
265
- # Initial message
 
 
266
  if not generate_button:
267
  scenario_output_area.info("왼쪽 사이드바에서 참여자 정보, 관계, 상황을 설정 후 '시나리오 생성하기' 버튼을 클릭하세요.")
 
2
  import streamlit as st
3
  import google.generativeai as genai
4
  from streamlit_extras.colored_header import colored_header
5
+ import markdown # markdown 라이브러리 임포트 확인
6
 
7
  # --- Must be the first Streamlit command ---
8
  st.set_page_config(page_title="MBTI 관계 시뮬레이터", page_icon="🤝", layout="wide")
 
11
 
12
  # Google Gemini API Key 설정 (Streamlit secrets 사용)
13
  try:
14
+ # secrets에서 키 로드 시도
15
+ api_key = st.secrets.get("GEMINI_API_KEY")
16
+ if not api_key:
17
+ st.error("Streamlit secrets에 'GEMINI_API_KEY'를 설정해주세요.")
18
+ st.info("Secrets 설정 방법: [Streamlit Docs](https://docs.streamlit.io/library/advanced-features/secrets-management)")
19
+ st.stop()
20
+ genai.configure(api_key=api_key)
21
  except Exception as e:
22
+ st.error(f"API 키 설정 중 예상치 못한 오류 발생: {e}")
23
  st.stop()
24
 
25
+
26
  # 모델 설정
27
  generation_config = {
28
  "temperature": 0.75,
29
  "top_p": 0.8,
30
  "top_k": 40,
31
+ "max_output_tokens": 15000,
32
  }
33
 
34
  # 특정 모델 이름으로 설정 (사용자 요청 반영)
35
+ # 참고: 'gemini-2.0-flash-thinking-exp-01-21'는 실험적 모델일 수 있습니다.
36
+ # 안정적인 최신 모델을 원하시면 'gemini-1.5-flash-latest' 또는 'gemini-1.5-pro-latest' 사용을 고려하세요.
37
+ target_model_name = "gemini-2.0-flash-thinking-exp-01-21" # 안정적인 모델로 변경 권장 (필요시 원래 모델명 사용)
38
 
39
  try:
40
  model = genai.GenerativeModel(
41
  model_name=target_model_name,
42
  generation_config=generation_config,
43
+ # safety_settings 설정 추가 (콘텐츠 필터링 관련)
44
+ safety_settings=[
45
+ {"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_MEDIUM_AND_ABOVE"},
46
+ {"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_MEDIUM_AND_ABOVE"},
47
+ {"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", "threshold": "BLOCK_MEDIUM_AND_ABOVE"},
48
+ {"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "BLOCK_MEDIUM_AND_ABOVE"},
49
+ ]
50
  )
51
+ # 모델 로딩 성공 간단한 정보 표시 (선택 사항)
52
+ # st.sidebar.caption(f"Using model: {target_model_name}")
53
  except Exception as e:
54
  st.error(f"Gemini 모델 '{target_model_name}' 로딩 중 오류 발생: {e}")
55
+ st.error("모델 이름을 다시 확인하거나, 사용 가능한 다른 모델(예: 'gemini-1.5-flash-latest')을 시도해보세요.")
56
+ # 사용 가능한 모델 목록 확인 링크 (선택 사항)
57
+ st.info("사용 가능한 모델 목록은 Google AI Studio 또는 Gemini API 문서를 참조하세요.")
58
  st.stop()
59
 
60
 
 
126
 
127
  # 참여자 정보 문자열 생성 함수
128
  def create_type_info(person):
129
+ # 입력값이 경우 기본값 처리
130
+ name = person.get('name', '이름없음')
131
+ gender = person.get('gender', '미지정')
132
+ mbti_type = person.get('type', '미지정')
133
+ type_description = mbti_descriptions.get(mbti_type, '알 수 없는 유형') # 상세 설명 사용
134
+ return f"{name} ({gender}, {mbti_type}): {type_description}"
135
+
136
+ # 참여자 정보 목록 생성 (people 리스트가 비어있지 않을 때만 생성)
137
+ if people:
138
+ people_info = "\n".join([f"- {create_type_info(person)}" for person in people])
139
+ else:
140
+ # people 리스트가 비어있는 예외적인 경우 처리
141
+ st.error("참여자 정보가 올바르게 전달되지 않았습니다. 사이드바 입력을 확인해주세요.")
142
+ return "" # 오류 발생 시 빈 문자열 반환
143
 
144
+ # 관계 유형 영문명 가져오기
145
+ relationship_en = relationship_types_two.get(relationship, relationship_types_multiple.get(relationship, ''))
146
 
147
  # 시스템 프롬프트 (개선됨: 속마음/의도, 상호작용 역학, 오해 지점, 명확한 구조 강조, 테이블 형식 지양)
148
  system_prompt = f"""
 
152
 
153
  * **참여자:**
154
  {people_info}
155
+ * **관계 유형:** {relationship} ({relationship_en})
156
  * **주어진 상황:** {situation}
157
 
158
  **[요청 사항]**
 
175
 
176
  **4. 관계 개선을 위한 실질적 조언:**
177
  * **각 참여자에게 맞춤화된 조언**을 제공해주세요. 이 조언은 시나리오에서 드러난 **구체적인 상호작용, 속마음, 오해 지점**을 직접적으로 다루어야 합니다.
178
+ * 서로를 더 잘 이해하고 **건강한 관계**를 구축하기 위해 각자가 **시도해볼 수 있는 구체적인 말과 행동**을 제안해주세요. (예: "{people[0]['name']}({people[0]['type']})님, {people[1]['name']}({people[1]['type']})님이 아이디어를 낼 때 즉시 분석하기보다, 먼저 '흥미로운 생각인데!'라고 반응하며 {people[1]['name']}님의 열정을 인정해주세요. 그 후에 함께 현실적인 부분을 논의하는 것이 좋습니다.")
179
  * 조언에는 반드시 **모든 참여자의 이름, 성별, MBTI 유형**이 명시되어야 합니다.
180
 
181
  **[출력 형식]**
 
192
  response = model.generate_content([system_prompt], stream=True)
193
  response_container = st.empty() # 응답 표시 영역 미리 확보
194
  for chunk in response:
195
+ # chunk.text가 None이 아닌 확인
196
  if chunk.text:
197
+ full_text += chunk.text # Append chunk text to full_text
198
+ # Markdown으로 실시간 업데이트 (unsafe_allow_html=False가 더 안전)
199
+ response_container.markdown(full_text, unsafe_allow_html=False) # HTML 대신 마크다운 직접 렌더링
200
+ time.sleep(0.01) # 딜레이 약간 줄임
 
201
  return full_text # 최종 전체 텍스트 반환
202
 
203
  except Exception as e:
204
  st.error(f"시나리오 생성 중 오류 발생: {e}")
205
+ # 오류 유형과 메시지를 함께 출력하여 디버깅에 도움
206
  st.error(f"오류 상세 정보: {type(e).__name__} - {e}")
207
+ # API 호출 관련 오류일 수 있으므로 추가 정보 제공
208
+ if "API key" in str(e):
209
+ st.error("API 키가 유효하지 않거나 할당량이 초과되었을 수 있습니다. Streamlit secrets 설정을 확인하세요.")
210
+ elif "model" in str(e).lower():
211
+ st.error(f"모델 '{target_model_name}'을 찾을 수 없거나 접근 권한이 없을 수 있습니다. 모델 이름을 확인하거나 다른 모델을 시도해보세요.")
212
+ elif "safety" in str(e).lower() or "filtered" in str(e).lower():
213
+ st.error("콘텐츠 안전 설정에 의해 응답이 필터링되었습니다. 입력 내용을 수정하거나 안전 설정을 조정해보세요.")
214
  return ""
215
 
216
 
 
218
 
219
  # 헤더 색상 적용
220
  colored_header(
221
+ label="🤝 MBTI 관계 시뮬레이터 v2.1", # 버전 업데이트
222
  description="참여자들의 MBTI, 관계, 상황을 입력하여 심층적인 관계 시나리오와 조언을 받아보세요.",
223
  color_name="blue-70"
224
  )
 
235
  num_people = st.number_input("참여자 수", min_value=2, max_value=5, value=2, key="num_people",
236
  help="최소 2명, 최대 5명까지 설정할 수 있습니다.")
237
 
238
+ people = [] # 리스트 초기화를 루프 바깥에서 수행
239
  # 참여자 수에 맞춰 동적으로 입력 필드 생성
240
  for i in range(num_people):
241
+ # 번째 참여자 기본으로 확장되도록 수정
242
+ with st.expander(f"👤 참여자 {i+1}", expanded=(i == 0)):
243
  person = {}
244
+ # 이름 예 플레이스홀더 추가
245
+ default_names = ['철수', '영희', '민준', '서연', '지우']
246
+ person['name'] = st.text_input(f"이름/닉네임", key=f"name_{i}", placeholder=f"예: {default_names[i % len(default_names)]}")
247
  person['gender'] = st.radio("성별", ["남성", "여성", "기타"], key=f"gender_{i}", horizontal=True)
248
+ # 기본 MBTI 선택 인덱스 계산 (오류 방지)
249
+ default_mbti_index = i % len(mbti_types)
250
+ person['type'] = st.selectbox(f"MBTI 유형", list(mbti_types.keys()), format_func=lambda x: f"{x} ({mbti_types[x]})", key=f"type_{i}", index=default_mbti_index)
251
+
252
+ # --- ★★★ 수정된 부분 ★★★ ---
253
+ people.append(person) # 입력받은 참여자 정보를 리스트에 추가
254
+ # ---------------------------
255
 
256
  st.divider()
257
 
 
266
  relationship_options = relationship_types_multiple
267
  relationship = st.selectbox("관계 유형", list(relationship_options.keys()), key="relationship")
268
 
269
+ situation = st.text_area("구체적인 상황", key="situation", height=100, # 높이 유지
270
  placeholder="예: 중요한 프로젝트 마감일을 앞두고 의견 충돌 발생")
271
 
272
  st.divider()
 
278
 
279
  # Main area - Scenario output
280
  st.header("💡 생성된 시나리오")
281
+ # Use st.container() for better layout control if needed, otherwise direct markdown is fine
282
  scenario_output_area = st.container()
283
+ # scenario_output_area = st.empty() # Use empty() if you want to replace content completely on each run
284
 
285
  # Button click logic
286
  if generate_button:
287
+ # Input validation 강화
288
  valid_input = True
289
+ # 이름 입력 확인 (공백만 입력된 경우도 방지)
290
+ if not all(p.get('name', '').strip() for p in people):
291
  st.error("모든 참여자의 이름을 입력해주세요.")
292
  valid_input = False
293
+ # 상황 입력 확인 (공백만 입력된 경우도 방지)
294
+ if not situation.strip():
295
  st.error("구체적인 상황을 입력해주세요.")
296
  valid_input = False
297
+ # 관계 유형 선택 확인 (selectbox는 기본값이 있으므로 일반적으로 문제는 없으나 명시적으로 확인)
298
  if not relationship:
299
  st.error("관계 유형을 선택해주세요.")
300
  valid_input = False
301
+ # people 리스트 자체 확인 (만약을 대비)
302
+ if not people:
303
+ st.error("참여자 정보가 없습니다. 페이지를 새로고침하거나 참여자 수를 다시 설정해보세요.")
304
+ valid_input = False
305
+
306
 
307
  if valid_input:
308
+ # 시나리오 생성 영역 초기화 (이전 결과 제거)
309
+ scenario_output_area.empty()
310
+ with scenario_output_area: # 컨테이너 내에서 스피너와 결과 표시
311
+ with st.spinner("🧠 MBTI 전문가가 시나리오 분석 및 생성 중... 잠시만 기다려주세요."):
312
+ # people 리스트가 제대로 채워졌는지 확인 (디버깅용)
313
+ # st.write("전달되는 참여자 정보:", people)
314
+ full_text_result = generate_relationship_scenario(people, relationship, situation)
315
+
316
+ if full_text_result:
317
+ # 최종 결과를 Markdown으로 렌더링 (unsafe_allow_html=True 사용 시 주의)
318
+ # html_output = markdown.markdown(full_text_result)
319
+ # scenario_output_area.markdown(html_output, unsafe_allow_html=True)
320
+ scenario_output_area.markdown(full_text_result, unsafe_allow_html=False) # 마크다운 직접 렌더링 권장
321
+ st.success("🎉 시나리오 생성 완료!")
322
+ else:
323
+ # generate_relationship_scenario 함수 내에서 이미 오류 메시지가 표시됨
324
+ st.warning("시나리오 생성에 실패했습니다. 오류 메시지를 확인해주세요.")
325
  else:
326
  st.warning("입력값을 다시 확인해주세요.")
327
 
328
+ # Initial message when the app loads or before generation
329
+ # Check if the button has been clicked at least once using session state if needed
330
+ # For simplicity, just show if the button wasn't clicked in this run
331
  if not generate_button:
332
  scenario_output_area.info("왼쪽 사이드바에서 참여자 정보, 관계, 상황을 설정 후 '시나리오 생성하기' 버튼을 클릭하세요.")