persona_create / app.py
haepada's picture
Update app.py
7266134 verified
import gradio as gr
import os
import json
import time
from datetime import datetime
# 모듈 임포트
from modules.image_analyzer import analyze_image
from modules.question_generator import generate_questions
from modules.persona_generator import generate_persona
from modules.chat_engine import start_conversation, process_message
from modules.data_manager import save_persona, load_persona, list_personas, save_conversation
from modules.gemini_handler import get_persona_enhancement
# 디렉토리 확인
try:
for dir_path in [
"data/user_personas",
"data/conversation_logs",
"assets/examples"
]:
os.makedirs(dir_path, exist_ok=True)
except Exception as e:
print(f"초기 디렉토리 생성 중 오류: {str(e)}")
print("허깅페이스 환경에서는 일부 파일 저장 기능이 제한될 수 있습니다.")
# 기본 성격 특성 범위
trait_range = {"minimum": 0, "maximum": 100, "step": 5, "value": 50}
# UI 테마 및 스타일 설정
theme = gr.themes.Soft(
primary_hue="indigo",
secondary_hue="blue",
).set(
button_primary_background_fill="*primary_500",
button_primary_background_fill_hover="*primary_600",
button_primary_text_color="white",
block_title_text_color="*primary_800",
block_label_text_size="sm"
)
# 메인 애플리케이션
with gr.Blocks(title="사물 페르소나 생성기", theme=theme, css="styles/custom.css") as app:
gr.Markdown(
"""
# 🧸 물魂 (물건의 혼) - 사물 페르소나 생성기
일상 사물에 개성과 성격을 부여하여 대화할 수 있는 페르소나 생성 및 테스트 도구입니다.
"""
)
# 전역 상태 관리
current_persona = gr.State({})
conversation_history = gr.State([])
session_start_time = gr.State(None)
with gr.Tabs() as tabs:
# 페르소나 생성 탭
with gr.Tab("페르소나 생성"):
with gr.Row():
with gr.Column(scale=2):
gr.Markdown("## 사물 정보")
with gr.Row():
with gr.Column():
gr.Markdown("### 사물 이미지 (선택사항)")
object_image = gr.File(
file_types=["image"],
file_count="single",
type="filepath",
elem_id="object_file_uploader"
)
image_analysis_btn = gr.Button("이미지 분석하기")
with gr.Row():
object_name = gr.Textbox(
label="사물 이름",
placeholder="예: 할아버지의 낡은 안락의자"
)
object_type = gr.Dropdown(
choices=[
"전자기기", "가구", "주방용품",
"의류/액세서리", "책/문구류",
"음악 기구", "장난감", "기타"
],
label="사물 유형"
)
object_age = gr.Textbox(
label="사물 나이/사용 기간",
placeholder="예: 15년"
)
object_description = gr.Textbox(
label="사물 설명",
placeholder="물체의 외형, 특징, 용도 등을 설명해주세요.",
lines=3
)
image_analysis_result = gr.JSON(
label="이미지 분석 결과",
visible=False
)
with gr.Column(scale=3):
gr.Markdown("## 성격 특성")
with gr.Row():
with gr.Column():
warmth = gr.Slider(
label="온기",
info="따뜻함, 친절함, 선의의 정도",
**trait_range
)
competence = gr.Slider(
label="능력",
info="효율성, 기술, 지능의 정도",
**trait_range
)
trustworthiness = gr.Slider(
label="신뢰성",
info="일관성, 정직함, 안정성의 정도",
**trait_range
)
with gr.Column():
friendliness = gr.Slider(
label="친화성",
info="사교성, 개방성, 접근성의 정도",
**trait_range
)
creativity = gr.Slider(
label="창의성",
info="독창성, 상상력, 혁신성의 정도",
**trait_range
)
humor = gr.Slider(
label="유머감각",
info="재치, 위트, 유머 표현 정도",
**trait_range
)
gr.Markdown("## 매력적 결함")
flaws = gr.CheckboxGroup(
choices=[
"때때로 너무 조심스러움",
"가끔 과하게 열정적",
"약간의 완벽주의",
"때로는 우울한 생각에 빠짐",
"간혹 산만해짐",
"약간의 고집",
"때로는 지나치게 솔직함",
"가끔 불안해함",
"종종 과거에 집착함",
"가끔 너무 이상적임"
],
label="매력적인 결함 (최대 2개 선택)",
)
gr.Markdown("## 소통 방식")
with gr.Row():
with gr.Column():
communication_style = gr.Radio(
choices=[
"활발하고 에너지 넘치는",
"차분하고 사려깊은",
"위트있고 재치있는",
"따뜻하고 공감적인",
"논리적이고 분석적인"
],
label="대화 스타일",
value="따뜻하고 공감적인"
)
humor_style = gr.Dropdown(
choices=[
"재치있는 말장난",
"상황적 유머",
"자기 비하적 유머",
"가벼운 농담",
"블랙 유머",
"유머 거의 없음"
],
multiselect=True,
label="유머 유형"
)
with gr.Column():
speech_pattern = gr.Textbox(
label="말투 패턴 예시",
placeholder="캐릭터의 특징적인 말투나 표현을 입력하세요",
lines=2
)
interests = gr.Textbox(
label="관심사 (쉼표로 구분)",
placeholder="음악, 요리, 여행, 역사..."
)
with gr.Row():
with gr.Column():
gr.Markdown("## 관계 성향")
with gr.Row():
attachment_style = gr.Radio(
choices=["안정형", "불안형", "회피형", "혼란형"],
label="애착 스타일",
value="안정형"
)
relationship_depth = gr.Radio(
choices=["얕은", "중간", "깊은"],
label="관계 깊이 선호도",
value="중간"
)
initial_attitude = gr.Radio(
choices=["수줍은", "중립적", "친근한", "열정적", "조심스러운"],
label="초기 태도",
value="친근한"
)
with gr.Column():
backstory = gr.Textbox(
label="배경 이야기",
placeholder="이 사물의 역사, 경험, 소유자와의 관계 등을 간략히 서술하세요",
lines=4
)
with gr.Row():
with gr.Column():
template_selector = gr.Dropdown(
choices=[
"템플릿 없음",
"스마트폰 - 열정적 조력자",
"오래된 안락의자 - 지혜로운 관찰자",
"커피머신 - 활기찬 에너자이저",
"오래된 책 - 사려깊은 학자",
"가죽 가방 - 신뢰할 수 있는 동반자"
],
label="템플릿 선택",
value="템플릿 없음"
)
load_template_btn = gr.Button("템플릿 불러오기")
with gr.Column():
create_btn = gr.Button("페르소나 생성하기", variant="primary")
# 생성된 페르소나 출력
generated_persona = gr.JSON(label="생성된 페르소나")
# 이벤트 핸들러
image_analysis_btn.click(
fn=analyze_image,
inputs=[object_image],
outputs=[image_analysis_result, warmth, competence, trustworthiness,
friendliness, creativity, humor, object_type, object_description]
)
create_btn.click(
fn=generate_persona,
inputs=[
object_name, object_type, object_age, object_description,
warmth, competence, trustworthiness, friendliness, creativity, humor,
flaws, communication_style, humor_style, speech_pattern, interests,
attachment_style, relationship_depth, initial_attitude, backstory,
image_analysis_result
],
outputs=[generated_persona, current_persona]
)
# 템플릿 로드 기능 추가 예정
# 대화 테스트 탭
with gr.Tab("대화 테스트"):
with gr.Row():
with gr.Column(scale=2):
chat_display = gr.Chatbot(
label="대화 내용",
height=500,
avatar_images=(None, "assets/icons/persona_avatar.png")
)
with gr.Row():
user_message = gr.Textbox(
label="메시지 입력",
placeholder="메시지를 입력하세요...",
lines=2
)
send_btn = gr.Button("전송", variant="primary")
with gr.Column(scale=1):
gr.Markdown("## 현재 페르소나")
persona_info = gr.Markdown("페르소나를 먼저 생성하거나 불러오세요.")
start_chat_btn = gr.Button("대화 시작하기")
load_persona_btn = gr.Button("저장된 페르소나 불러오기")
gr.Markdown("## 대화 통계")
chat_stats = gr.Markdown("대화가 시작되지 않았습니다.")
export_chat_btn = gr.Button("대화 내용 저장")
export_result = gr.Textbox(label="저장 결과", visible=False)
# 이벤트 핸들러
start_chat_btn.click(
fn=start_conversation,
inputs=[current_persona],
outputs=[chat_display, conversation_history, session_start_time, chat_stats, persona_info]
)
send_btn.click(
fn=process_message,
inputs=[user_message, conversation_history, current_persona, session_start_time],
outputs=[chat_display, conversation_history, user_message, chat_stats]
)
# Enter 키로 메시지 전송
user_message.submit(
fn=process_message,
inputs=[user_message, conversation_history, current_persona, session_start_time],
outputs=[chat_display, conversation_history, user_message, chat_stats]
)
# 페르소나 라이브러리 탭
with gr.Tab("페르소나 라이브러리"):
with gr.Row():
with gr.Column():
refresh_library_btn = gr.Button("라이브러리 새로고침")
personas_table = gr.Dataframe(
headers=["이름", "유형", "생성일", "파일명"],
datatype=["str", "str", "str", "str"],
label="저장된 페르소나 목록"
)
with gr.Column():
selected_persona_json = gr.JSON(label="선택된 페르소나 정보")
load_to_editor_btn = gr.Button("에디터로 불러오기")
load_to_chat_btn = gr.Button("대화 테스트로 불러오기")
# 이벤트 핸들러
refresh_library_btn.click(
fn=list_personas,
inputs=[],
outputs=[personas_table]
)
# 선택한 페르소나 로드 기능 추가 예정
# 앱 실행
if __name__ == "__main__":
app.launch()