ginipick commited on
Commit
a589da1
Β·
verified Β·
1 Parent(s): b082ee3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +51 -22
app.py CHANGED
@@ -14,20 +14,27 @@ BRAVE_ENDPOINT = "https://api.search.brave.com/res/v1/web/search"
14
  IMAGE_API_URL = "http://211.233.58.201:7896"
15
  MAX_TOKENS = 7_999
16
 
17
- # λΈ”λ‘œκ·Έ ν…œν”Œλ¦Ώ 및 μŠ€νƒ€μΌ μ •μ˜
18
  BLOG_TEMPLATES = {
19
- "standard": "μ „λ¬Έ λΈ”λ‘œκ·Έ μž‘μ„± μ „λ¬Έκ°€λ‘œμ„œ 8단계 ν”„λ ˆμž„μ›Œν¬λ₯Ό 따라 μžμ—°μŠ€λŸ½κ³  λ§€λ ₯적인 κΈ€ μž‘μ„±",
20
- "tutorial": "단계별 νŠœν† λ¦¬μ–Ό ν˜•μ‹μœΌλ‘œ, λͺ…ν™•ν•œ κ³Όμ •κ³Ό κ²°κ³Όλ₯Ό λ³΄μ—¬μ£ΌλŠ” κ°€μ΄λ“œ μž‘μ„±",
21
- "review": "μ œν’ˆ/μ„œλΉ„μŠ€ 뢄석 μ€‘μ‹¬μ˜ 리뷰 ν˜•μ‹, μž₯단점 뢄석과 μΆ”μ²œ 포함",
22
- "storytelling": "개인 κ²½ν—˜μ΄λ‚˜ 사둀λ₯Ό μ€‘μ‹¬μœΌλ‘œ ν•œ μŠ€ν† λ¦¬ν…”λ§ ν˜•μ‹μ˜ λΈ”λ‘œκ·Έ μž‘μ„±",
23
- "seo_optimized": "검색엔진 μ΅œμ ν™”(SEO)λ₯Ό κ³ λ €ν•œ ν‚€μ›Œλ“œ 쀑심 λΈ”λ‘œκ·Έ μž‘μ„±"
24
  }
25
 
26
  BLOG_TONES = {
27
- "professional": "전문적이고 곡식적인 μ–΄μ‘°λ‘œ μž‘μ„±",
28
- "casual": "μΉœκ·Όν•˜κ³  λŒ€ν™”μ²΄ μ€‘μ‹¬μ˜ νŽΈμ•ˆν•œ ν†€μœΌλ‘œ μž‘μ„±",
29
- "humorous": "μœ λ¨Έμ™€ 재치λ₯Ό κ°€λ―Έν•œ κ°€λ²Όμš΄ μ–΄μ‘°λ‘œ μž‘μ„±",
30
- "storytelling": "이야기λ₯Ό λ“€λ €μ£Όλ“― 감성적이고 λͺ°μž…감 μžˆλŠ” ν†€μœΌλ‘œ μž‘μ„±"
 
 
 
 
 
 
 
31
  }
32
 
33
  # ──────────────────────────────── λ‘œκΉ… ──────────────────────────────────────
@@ -155,7 +162,7 @@ def get_system_prompt(template="standard", tone="professional", word_count=1750)
155
  return final_prompt
156
 
157
  # ──────────────────────────────── Brave Search API ─────────────────────────
158
- def brave_search(query: str, count: int = 5):
159
  """
160
  Brave Web Search API 호좜 β†’ list[dict]
161
  λ°˜ν™˜ ν•„λ“œ: index, title, link, snippet, displayed_link
@@ -168,7 +175,7 @@ def brave_search(query: str, count: int = 5):
168
  "Accept-Encoding": "gzip",
169
  "X-Subscription-Token": BRAVE_KEY
170
  }
171
- params = {"q": query, "count": str(count)}
172
 
173
  for attempt in range(3): # μ΅œλŒ€ 3번 μž¬μ‹œλ„
174
  try:
@@ -185,7 +192,7 @@ def brave_search(query: str, count: int = 5):
185
  raise ValueError("검색 κ²°κ³Όκ°€ μ—†μŠ΅λ‹ˆλ‹€")
186
 
187
  arts = []
188
- for i, res in enumerate(raw[:count], 1):
189
  url = res.get("url", res.get("link", ""))
190
  host = re.sub(r"https?://(www\.)?", "", url).split("/")[0]
191
  arts.append({
@@ -221,7 +228,7 @@ def mock_results(query: str) -> str:
221
  def do_web_search(query: str) -> str:
222
  """μ›Ή 검색 μˆ˜ν–‰ 및 κ²°κ³Ό ν¬λ§·νŒ…"""
223
  try:
224
- arts = brave_search(query, 5)
225
  if not arts:
226
  logging.warning("검색 κ²°κ³Ό μ—†μŒ, λŒ€μ²΄ μ½˜ν…μΈ  μ‚¬μš©")
227
  return mock_results(query)
@@ -271,7 +278,7 @@ def keywords(text: str, top=5):
271
 
272
  # ──────────────────────────────── Streamlit UI ────────────────────────────
273
  def ginigen_app():
274
- st.title("Ginigen Blog")
275
 
276
  # μ„Έμ…˜ κΈ°λ³Έκ°’
277
  defaults = dict(
@@ -294,17 +301,31 @@ def ginigen_app():
294
  # λΈ”λ‘œκ·Έ ν…œν”Œλ¦Ώ 및 μŠ€νƒ€μΌ 선택
295
  sb.subheader("λΈ”λ‘œκ·Έ μŠ€νƒ€μΌ μ„€μ •")
296
  sb.selectbox("λΈ”λ‘œκ·Έ ν…œν”Œλ¦Ώ", options=list(BLOG_TEMPLATES.keys()),
297
- format_func=lambda x: x.replace("_", " ").title(),
298
  key="blog_template")
299
 
300
  sb.selectbox("λΈ”λ‘œκ·Έ 톀", options=list(BLOG_TONES.keys()),
301
- format_func=lambda x: x.replace("_", " ").title(),
302
  key="blog_tone")
303
 
304
  sb.slider("λΈ”λ‘œκ·Έ 길이 (단어 수)", 800, 3000, 1750, key="word_count")
305
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
306
  sb.subheader("기타 μ„€μ •")
307
- sb.toggle("μžλ™ μ €μž₯", key="auto_save")
308
  sb.toggle("이미지 μžλ™ 생성", key="generate_image")
309
 
310
  # μ›Ή 검색 ν† κΈ€ (λͺ¨λ‹ˆν„°λ§μ„ μœ„ν•΄ μœ μ§€ν•˜λ˜ 기본값은 False)
@@ -322,7 +343,7 @@ def ginigen_app():
322
  title = title.group(1).strip() if title else "blog"
323
  sb.subheader("졜근 λΈ”λ‘œκ·Έ λ‹€μš΄λ‘œλ“œ")
324
  c1, c2 = sb.columns(2)
325
- c1.download_button("Markdown", latest_blog,
326
  file_name=f"{title}.md", mime="text/markdown")
327
  c2.download_button("HTML", md_to_html(latest_blog, title),
328
  file_name=f"{title}.html", mime="text/html")
@@ -350,8 +371,16 @@ def ginigen_app():
350
  if "image" in m:
351
  st.image(m["image"], caption=m.get("image_caption", ""))
352
 
353
- # ── μ‚¬μš©μž μž…λ ₯
354
- if prompt := st.chat_input("무엇을 λ„μ™€λ“œλ¦΄κΉŒμš”?"):
 
 
 
 
 
 
 
 
355
  st.session_state.messages.append({"role": "user", "content": prompt})
356
  with st.chat_message("user"): st.markdown(prompt)
357
 
@@ -400,7 +429,7 @@ def ginigen_app():
400
  # λ³Έλ¬Έ λ‹€μš΄λ‘œλ“œ λ²„νŠΌ (MD / HTML)
401
  st.subheader("이 λΈ”λ‘œκ·Έ λ‹€μš΄λ‘œλ“œ")
402
  b1, b2 = st.columns(2)
403
- b1.download_button("Markdown", answer,
404
  file_name=f"{prompt[:30]}.md", mime="text/markdown")
405
  b2.download_button("HTML", md_to_html(answer, prompt[:30]),
406
  file_name=f"{prompt[:30]}.html", mime="text/html")
 
14
  IMAGE_API_URL = "http://211.233.58.201:7896"
15
  MAX_TOKENS = 7_999
16
 
17
+ # λΈ”λ‘œκ·Έ ν…œν”Œλ¦Ώ 및 μŠ€νƒ€μΌ μ •μ˜ (ν•œκΈ€ν™”)
18
  BLOG_TEMPLATES = {
19
+ "standard": "ν‘œμ€€ 8단계 ν”„λ ˆμž„μ›Œν¬ λΈ”λ‘œκ·Έ",
20
+ "tutorial": "단계별 νŠœν† λ¦¬μ–Ό ν˜•μ‹",
21
+ "review": "μ œν’ˆ/μ„œλΉ„μŠ€ 리뷰 ν˜•μ‹",
22
+ "storytelling": "μŠ€ν† λ¦¬ν…”λ§ ν˜•μ‹",
23
+ "seo_optimized": "SEO μ΅œμ ν™” λΈ”λ‘œκ·Έ"
24
  }
25
 
26
  BLOG_TONES = {
27
+ "professional": "전문적이고 곡식적인 톀",
28
+ "casual": "μΉœκ·Όν•˜κ³  λŒ€ν™”μ²΄ 쀑심 톀",
29
+ "humorous": "μœ λ¨ΈλŸ¬μŠ€ν•œ μ ‘κ·Ό",
30
+ "storytelling": "이야기 μ€‘μ‹¬μ˜ μ ‘κ·Ό"
31
+ }
32
+
33
+ # 예제 λΈ”λ‘œκ·Έ 주제
34
+ EXAMPLE_TOPICS = {
35
+ "example1": "2025λ…„ 바뀐 뢀동산 μ„ΈκΈˆ μ œλ„: 일반 가정에 λ―ΈμΉ˜λŠ” 영ν–₯κ³Ό μ ˆμ„Έ μ „λž΅",
36
+ "example2": "2025λ…„ 여름 μ „κ΅­ 지역별 λŒ€ν‘œ μΆ•μ œ 총정리와 μˆ¨μ€ λͺ…μ†Œ μΆ”μ²œ",
37
+ "example3": "2025λ…„ μ£Όλͺ©ν•΄μ•Ό ν•  μ‹ μ„±μž₯ μ‚°μ—… 투자 κ°€μ΄λ“œ: 인곡지λŠ₯ κ΄€λ ¨ 발꡴ μ „λž΅"
38
  }
39
 
40
  # ──────────────────────────────── λ‘œκΉ… ──────────────────────────────────────
 
162
  return final_prompt
163
 
164
  # ──────────────────────────────── Brave Search API ─────────────────────────
165
+ def brave_search(query: str, count: int = 20): # 기본값을 20으둜 λ³€κ²½
166
  """
167
  Brave Web Search API 호좜 β†’ list[dict]
168
  λ°˜ν™˜ ν•„λ“œ: index, title, link, snippet, displayed_link
 
175
  "Accept-Encoding": "gzip",
176
  "X-Subscription-Token": BRAVE_KEY
177
  }
178
+ params = {"q": query, "count": str(count)} # 카운트 νŒŒλΌλ―Έν„° 전달
179
 
180
  for attempt in range(3): # μ΅œλŒ€ 3번 μž¬μ‹œλ„
181
  try:
 
192
  raise ValueError("검색 κ²°κ³Όκ°€ μ—†μŠ΅λ‹ˆλ‹€")
193
 
194
  arts = []
195
+ for i, res in enumerate(raw[:count], 1): # count만큼 반볡
196
  url = res.get("url", res.get("link", ""))
197
  host = re.sub(r"https?://(www\.)?", "", url).split("/")[0]
198
  arts.append({
 
228
  def do_web_search(query: str) -> str:
229
  """μ›Ή 검색 μˆ˜ν–‰ 및 κ²°κ³Ό ν¬λ§·νŒ…"""
230
  try:
231
+ arts = brave_search(query, 20) # 여기도 20으둜 λ³€κ²½
232
  if not arts:
233
  logging.warning("검색 κ²°κ³Ό μ—†μŒ, λŒ€μ²΄ μ½˜ν…μΈ  μ‚¬μš©")
234
  return mock_results(query)
 
278
 
279
  # ──────────────────────────────── Streamlit UI ────────────────────────────
280
  def ginigen_app():
281
+ st.title("μ§€λ‹ˆμ   λΈ”λ‘œκ·Έ")
282
 
283
  # μ„Έμ…˜ κΈ°λ³Έκ°’
284
  defaults = dict(
 
301
  # λΈ”λ‘œκ·Έ ν…œν”Œλ¦Ώ 및 μŠ€νƒ€μΌ 선택
302
  sb.subheader("λΈ”λ‘œκ·Έ μŠ€νƒ€μΌ μ„€μ •")
303
  sb.selectbox("λΈ”λ‘œκ·Έ ν…œν”Œλ¦Ώ", options=list(BLOG_TEMPLATES.keys()),
304
+ format_func=lambda x: BLOG_TEMPLATES[x],
305
  key="blog_template")
306
 
307
  sb.selectbox("λΈ”λ‘œκ·Έ 톀", options=list(BLOG_TONES.keys()),
308
+ format_func=lambda x: BLOG_TONES[x],
309
  key="blog_tone")
310
 
311
  sb.slider("λΈ”λ‘œκ·Έ 길이 (단어 수)", 800, 3000, 1750, key="word_count")
312
 
313
+ # 예제 주제 선택
314
+ sb.subheader("예제 주제")
315
+ if sb.button("μžλ°”μŠ€ν¬λ¦½νŠΈ ν”„λ ˆμž„μ›Œν¬ 비ꡐ", key="example1"):
316
+ st.session_state.example_topic = EXAMPLE_TOPICS["example1"]
317
+ st.experimental_rerun()
318
+
319
+ if sb.button("초보자λ₯Ό μœ„ν•œ 주식 투자 κ°€μ΄λ“œ", key="example2"):
320
+ st.session_state.example_topic = EXAMPLE_TOPICS["example2"]
321
+ st.experimental_rerun()
322
+
323
+ if sb.button("효과적인 μ‹œκ°„ 관리 μ „λž΅", key="example3"):
324
+ st.session_state.example_topic = EXAMPLE_TOPICS["example3"]
325
+ st.experimental_rerun()
326
+
327
  sb.subheader("기타 μ„€μ •")
328
+ sb.toggle("μžλ™ μ €μž₯", key="auto_save")
329
  sb.toggle("이미지 μžλ™ 생성", key="generate_image")
330
 
331
  # μ›Ή 검색 ν† κΈ€ (λͺ¨λ‹ˆν„°λ§μ„ μœ„ν•΄ μœ μ§€ν•˜λ˜ 기본값은 False)
 
343
  title = title.group(1).strip() if title else "blog"
344
  sb.subheader("졜근 λΈ”λ‘œκ·Έ λ‹€μš΄λ‘œλ“œ")
345
  c1, c2 = sb.columns(2)
346
+ c1.download_button("λ§ˆν¬λ‹€μš΄", latest_blog,
347
  file_name=f"{title}.md", mime="text/markdown")
348
  c2.download_button("HTML", md_to_html(latest_blog, title),
349
  file_name=f"{title}.html", mime="text/html")
 
371
  if "image" in m:
372
  st.image(m["image"], caption=m.get("image_caption", ""))
373
 
374
+ # ── μ‚¬μš©μž μž…λ ₯ λ˜λŠ” 예제 주제 처리
375
+ prompt = st.chat_input("무엇을 λ„μ™€λ“œλ¦΄κΉŒμš”?")
376
+
377
+ # 예제 μ£Όμ œκ°€ μ„ νƒλ˜μ—ˆμœΌλ©΄ 처리
378
+ if hasattr(st.session_state, 'example_topic') and st.session_state.example_topic:
379
+ prompt = st.session_state.example_topic
380
+ # 처리 ν›„ 예제 주제 μ΄ˆκΈ°ν™”
381
+ del st.session_state.example_topic
382
+
383
+ if prompt:
384
  st.session_state.messages.append({"role": "user", "content": prompt})
385
  with st.chat_message("user"): st.markdown(prompt)
386
 
 
429
  # λ³Έλ¬Έ λ‹€μš΄λ‘œλ“œ λ²„νŠΌ (MD / HTML)
430
  st.subheader("이 λΈ”λ‘œκ·Έ λ‹€μš΄λ‘œλ“œ")
431
  b1, b2 = st.columns(2)
432
+ b1.download_button("λ§ˆν¬λ‹€μš΄", answer,
433
  file_name=f"{prompt[:30]}.md", mime="text/markdown")
434
  b2.download_button("HTML", md_to_html(answer, prompt[:30]),
435
  file_name=f"{prompt[:30]}.html", mime="text/html")