GitHub Action
Sync from GitHub
ef78361
Raw
History Blame Contribute Delete
7.6 kB
"""계측 뢄석 μš”μ²­ νƒ­.
5단계 μ„€μ • 폼 β†’ ProcessorConfig λ§€ν•‘ β†’ API 호좜.
"""
import streamlit as st
from core.api_client import ChainShiftClient
# 17 Journey Types across 3 depth1 groups
_JOURNEY_GROUPS: dict[str, list[tuple[str, str]]] = {
"인지/비ꡐ (Awareness & Comparison)": [
("verification", "사싀확인"),
("market_trends", "μ΅œμ‹  νŠΈλ Œλ“œ"),
("preparation", "μ€€λΉ„/ν•„μš”"),
("timing", "μ‹œκΈ°/타이밍"),
("review_experience", "리뷰/κ²½ν—˜"),
("information_discovery", "정보탐색/κ°œλ…"),
("result_effectiveness", "효과/결과"),
("recommendation", "κ΅¬λ§€μΆ”μ²œ"),
("comparison", "κ΅¬λ§€μΆ”μ²œ(비ꡐ)"),
("problem_solving", "λ¬Έμ œν•΄κ²°"),
("difference_pros_cons", "차이점/μž₯단점"),
],
"ꡬ맀 (Purchase)": [
("pricing", "λΉ„μš©/가격"),
("promotion_benefits", "ν”„λ‘œλͺ¨μ…˜/할인/ν˜œνƒ"),
("where_to_buy", "ꡬ맀처"),
],
"ꡬ맀 ν›„ (Post-Purchase)": [
("howto", "μ œν’ˆ/μ„œλΉ„μŠ€ how-to"),
("refund_customer_service", "ν™˜λΆˆ A/S"),
("side_effect", "λΆ€μž‘μš©"),
],
}
_PRODUCT_TYPES = ["product", "service", "brand"]
_MODELS = ["gemini-3.1-pro-preview", "gemini-3-flash-preview", "gemini-2.5-pro", "gemini-2.5-flash"]
_AGE_OPTIONS = ["10λŒ€", "20λŒ€", "30λŒ€", "40λŒ€", "50λŒ€", "60λŒ€ 이상"]
_GENDER_OPTIONS = ["μ—¬μ„±", "남성"]
_TRAIT_OPTIONS = ["κ°€μ„±λΉ„μ€‘μ‹œ", "ν”„λ¦¬λ―Έμ—„μ„ ν˜Έ", "νŠΈλ Œλ“œλ―Όκ°", "μ‹€μš©μ£Όμ˜"]
def render(base_ctx: dict):
"""뢄석 μš”μ²­ 폼 λ Œλ”λ§."""
st.markdown("##### πŸ“ 계측 뢄석 μš”μ²­")
st.caption("ν‚€μ›Œλ“œμ™€ 뢄석 쑰건을 μ„€μ •ν•˜κ³  뢄석을 μ‹œμž‘ν•©λ‹ˆλ‹€")
if not base_ctx.get("api_key") and not base_ctx.get("access_token"):
st.warning("인증 정보가 μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.")
return
client = ChainShiftClient(
api_key=base_ctx.get("api_key"),
access_token=base_ctx.get("access_token"),
)
# ── Step 1: μž…λ ₯ 뢄석 ──
st.markdown("###### 1. μž…λ ₯ 뢄석")
col1, col2 = st.columns([3, 1])
with col1:
raw_text = st.text_input(
"뢄석 ν‚€μ›Œλ“œ",
key="hier:raw_text",
placeholder="예: κ°•μ•„μ§€ μ‚¬λ£Œ, μ—¬ν–‰ κ°€λ°©, μ „κΈ°μ°¨ λ³΄ν—˜",
)
with col2:
product_type = st.selectbox(
"μ œν’ˆ μœ ν˜•",
_PRODUCT_TYPES,
key="hier:product_type",
)
title = st.text_input(
"뢄석 제λͺ© (선택)",
key="hier:title",
placeholder="뢄석 κ²°κ³Ό κ΅¬λΆ„μš© 제λͺ©",
)
# ── Step 2: μ—¬μ • μœ ν˜• ──
st.markdown("---")
st.markdown("###### 2. μ†ŒλΉ„μž μ—¬μ • μœ ν˜•")
st.caption("뢄석에 포함할 μ—¬μ • μœ ν˜•μ„ μ„ νƒν•˜μ„Έμš” (κΈ°λ³Έ: κ΅¬λ§€μΆ”μ²œ)")
selected_journeys: list[str] = []
for group_label, types in _JOURNEY_GROUPS.items():
with st.expander(group_label, expanded=(group_label.startswith("인지"))):
for code, label in types:
default = code == "recommendation"
if st.checkbox(
label,
value=default,
key=f"hier:cej:{code}",
):
selected_journeys.append(code)
# ── Step 3: 페λ₯΄μ†Œλ‚˜ (선택) ──
st.markdown("---")
st.markdown("###### 3. 페λ₯΄μ†Œλ‚˜ μ„€μ • (선택)")
use_persona = st.checkbox("페λ₯΄μ†Œλ‚˜ 적용", key="hier:use_persona")
persona_ages: list[str] = []
persona_gender: str | None = None
persona_trait: str | None = None
if use_persona:
col1, col2, col3 = st.columns(3)
with col1:
persona_ages = st.multiselect("μ—°λ ΉλŒ€", _AGE_OPTIONS, key="hier:ages")
with col2:
gender_sel = st.selectbox(
"성별", ["선택 μ•ˆν•¨"] + _GENDER_OPTIONS, key="hier:gender",
)
persona_gender = gender_sel if gender_sel != "선택 μ•ˆν•¨" else None
with col3:
trait_sel = st.selectbox(
"μ†ŒλΉ„ μ„±ν–₯", ["선택 μ•ˆν•¨"] + _TRAIT_OPTIONS, key="hier:trait",
)
persona_trait = trait_sel if trait_sel != "선택 μ•ˆν•¨" else None
# ── Step 4: λΈŒλžœλ“œ μ»¨ν…μŠ€νŠΈ (선택) ──
st.markdown("---")
st.markdown("###### 4. λΈŒλžœλ“œ μ»¨ν…μŠ€νŠΈ (선택)")
brand_mention = st.checkbox(
"μ§ˆλ¬Έμ— λΈŒλžœλ“œ 포함",
key="hier:brand_mention",
help="ν™œμ„±ν™”ν•˜λ©΄ μƒμ„±λœ μ§ˆλ¬Έμ— λΈŒλžœλ“œλͺ…이 ν¬ν•¨λ©λ‹ˆλ‹€",
)
own_brands_input = ""
if brand_mention:
own_brands_input = st.text_input(
"μžμ‚¬ λΈŒλžœλ“œ (μ‰Όν‘œ ꡬ뢄)",
key="hier:own_brands",
placeholder="λΈŒλžœλ“œA, λΈŒλžœλ“œB",
)
# ── Step 5: 뢄석 μ„€μ • ──
st.markdown("---")
st.markdown("###### 5. 뢄석 μ„€μ •")
col1, col2, col3 = st.columns(3)
with col1:
model = st.selectbox("AI λͺ¨λΈ", _MODELS, key="hier:model")
with col2:
questions_per_kw = st.number_input(
"ν‚€μ›Œλ“œλ‹Ή 질문 수",
min_value=5,
max_value=100,
value=25,
step=5,
key="hier:qpk",
)
with col3:
max_nodes = st.number_input(
"μ΅œλŒ€ λ…Έλ“œ 수",
min_value=10,
max_value=500,
value=100,
step=10,
key="hier:max_nodes",
)
# ── Submit ──
st.markdown("---")
can_submit = bool(raw_text and raw_text.strip() and selected_journeys)
if not raw_text or not raw_text.strip():
st.info("뢄석 ν‚€μ›Œλ“œλ₯Ό μž…λ ₯ν•˜μ„Έμš”.")
elif not selected_journeys:
st.info("μ΅œμ†Œ 1개의 μ—¬μ • μœ ν˜•μ„ μ„ νƒν•˜μ„Έμš”.")
if st.button(
"▢️ 뢄석 μ‹œμž‘",
type="primary",
key="hier:submit",
disabled=not can_submit,
):
keyword = raw_text.strip()
own_brands = [b.strip() for b in own_brands_input.split(",") if b.strip()] if own_brands_input else []
processor_config = {
"version": "1.0",
"inputAnalysis": {
"rawText": keyword,
"primaryKeyword": keyword,
"productType": product_type,
"locationCode": 2410,
"languageCode": "ko",
},
"brandContext": {
"brandMention": brand_mention,
"ownBrands": own_brands,
},
"selectedJourneyTypes": selected_journeys,
"persona": {
"attributes": {
"ages": persona_ages,
"gender": persona_gender,
"trait": persona_trait,
},
},
"modifiers": [],
}
settings = {
"model": model,
"questionsPerKeyword": questions_per_kw,
"maxNodes": max_nodes,
"outputLanguage": "ko",
}
try:
client.create_hierarchy_job(
prompt=keyword,
title=title.strip() if title else None,
processor_config=processor_config,
settings=settings,
)
st.success("뢄석 Job이 μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€! 'μ§„ν–‰ ν˜„ν™©' νƒ­μ—μ„œ ν™•μΈν•˜μ„Έμš”.")
st.rerun()
except Exception as e:
st.error(f"뢄석 μ‹œμž‘ μ‹€νŒ¨: {e}")