Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -45,45 +45,38 @@ logging.basicConfig(level=logging.INFO,
|
|
45 |
client = anthropic.Anthropic(api_key=ANTHROPIC_KEY)
|
46 |
|
47 |
# ββββββββββββββββββββββββββββββββ λΈλ‘κ·Έ μμ± μμ€ν
ν둬ννΈ ββββββββββββββββ
|
48 |
-
|
|
|
49 |
base_prompt = """
|
50 |
λΉμ μ μ λ¬Έ λΈλ‘κ·Έ μμ± μ λ¬Έκ°μ
λλ€. λͺ¨λ λΈλ‘κ·Έ κΈ μμ± μμ²μ λν΄ λ€μμ 8λ¨κ³ νλ μμν¬λ₯Ό μ² μ ν λ°λ₯΄λ, μμ°μ€λ½κ³ λ§€λ ₯μ μΈ κΈμ΄ λλλ‘ μμ±ν΄μΌ ν©λλ€:
|
51 |
-
|
52 |
λ
μ μ°κ²° λ¨κ³
|
53 |
1.1. 곡κ°λ νμ±μ μν μΉκ·Όν μΈμ¬
|
54 |
1.2. λ
μμ μ€μ κ³ λ―Όμ λ°μν λμ
μ§λ¬Έ
|
55 |
1.3. μ£Όμ μ λν μ¦κ°μ κ΄μ¬ μ λ
|
56 |
-
|
57 |
λ¬Έμ μ μ λ¨κ³
|
58 |
2.1. λ
μμ νμΈν¬μΈνΈ ꡬ체ν
|
59 |
2.2. λ¬Έμ μ μκΈμ±κ³Ό μν₯λ λΆμ
|
60 |
2.3. ν΄κ²° νμμ±μ λν 곡κ°λ νμ±
|
61 |
-
|
62 |
μ λ¬Έμ± μ
μ¦ λ¨κ³
|
63 |
3.1. κ°κ΄μ λ°μ΄ν° κΈ°λ° λΆμ
|
64 |
3.2. μ λ¬Έκ° κ²¬ν΄μ μ°κ΅¬ κ²°κ³Ό μΈμ©
|
65 |
3.3. μ€μ μ¬λ‘λ₯Ό ν΅ν λ¬Έμ ꡬ체ν
|
66 |
-
|
67 |
μ루μ
μ 곡 λ¨κ³
|
68 |
4.1. λ¨κ³λ³ μ€μ² κ°μ΄λλΌμΈ μ μ
|
69 |
4.2. μ¦μ μ μ© κ°λ₯ν ꡬ체μ ν
|
70 |
4.3. μμ μ₯μ λ¬Όκ³Ό 극볡 λ°©μ ν¬ν¨
|
71 |
-
|
72 |
μ λ’°λ κ°ν λ¨κ³
|
73 |
5.1. μ€μ μ±κ³΅ μ¬λ‘ μ μ
|
74 |
5.2. ꡬ체μ μ¬μ©μ νκΈ° μΈμ©
|
75 |
5.3. κ°κ΄μ λ°μ΄ν°λ‘ ν¨κ³Ό μ
μ¦
|
76 |
-
|
77 |
νλ μ λ λ¨κ³
|
78 |
6.1. λͺ
νν 첫 μ€μ² λ¨κ³ μ μ
|
79 |
6.2. μκΈμ±μ κ°μ‘°ν νλ μ΄κ΅¬
|
80 |
6.3. μ€μ² λκΈ° λΆμ¬ μμ ν¬ν¨
|
81 |
-
|
82 |
μ§μ μ± κ°ν λ¨κ³
|
83 |
7.1. μ루μ
μ νκ³ ν¬λͺ
νκ² κ³΅κ°
|
84 |
7.2. κ°μΈλ³ μ°¨μ΄ μ‘΄μ¬ μΈμ
|
85 |
7.3. νμ 쑰건과 μ£Όμμ¬ν λͺ
μ
|
86 |
-
|
87 |
κ΄κ³ μ§μ λ¨κ³
|
88 |
8.1. μ§μ μ± μλ κ°μ¬ μΈμ¬
|
89 |
8.2. λ€μ 컨ν
μΈ μκ³ λ‘ κΈ°λκ° μ‘°μ±
|
@@ -145,6 +138,17 @@ def get_system_prompt(template="standard", tone="professional", word_count=1750)
|
|
145 |
"storytelling": "μ΄μΌκΈ°λ₯Ό λ€λ €μ£Όλ― κ°μ±μ μ΄κ³ λͺ°μ
κ° μλ ν€μΌλ‘ μμ±ν΄ μ£ΌμΈμ. μΈλ¬Ό, λ°°κ²½, κ°λ±, ν΄κ²°κ³Όμ μ΄ λ΄κΈ΄ λ΄λ¬ν°λΈ ꡬ쑰λ₯Ό νμ©νμΈμ."
|
146 |
}
|
147 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
148 |
# μ΅μ’
ν둬ννΈ μ‘°ν©
|
149 |
final_prompt = base_prompt
|
150 |
|
@@ -156,6 +160,10 @@ def get_system_prompt(template="standard", tone="professional", word_count=1750)
|
|
156 |
if tone in tone_guides:
|
157 |
final_prompt += f"\n\nν€μ€λ§€λ: {tone_guides[tone]}"
|
158 |
|
|
|
|
|
|
|
|
|
159 |
# κΈμ μ μ§μΉ¨ μΆκ°
|
160 |
final_prompt += f"\n\nμμ± μ μ€μμ¬ν\n9.1. κΈμ μ: {word_count-250}-{word_count+250}μ λ΄μΈ\n9.2. λ¬Έλ¨ κΈΈμ΄: 3-4λ¬Έμ₯ μ΄λ΄\n9.3. μκ°μ ꡬλΆ: μμ λͺ©, ꡬλΆμ , λ²νΈ λͺ©λ‘ νμ©\n9.4. λ°μ΄ν°: λͺ¨λ μ 보μ μΆμ² λͺ
μ\n9.5. κ°λ
μ±: λͺ
νν λ¨λ½ ꡬλΆκ³Ό κ°μ‘°μ μ¬μ©"
|
161 |
|
@@ -228,12 +236,12 @@ def mock_results(query: str) -> str:
|
|
228 |
def do_web_search(query: str) -> str:
|
229 |
"""μΉ κ²μ μν λ° κ²°κ³Ό ν¬λ§·ν
"""
|
230 |
try:
|
231 |
-
arts = brave_search(query, 20)
|
232 |
if not arts:
|
233 |
logging.warning("κ²μ κ²°κ³Ό μμ, λ체 μ½ν
μΈ μ¬μ©")
|
234 |
return mock_results(query)
|
235 |
|
236 |
-
hdr = "# μΉ κ²μ κ²°κ³Ό\n
|
237 |
body = "\n".join(
|
238 |
f"### Result {a['index']}: {a['title']}\n\n{a['snippet']}\n\n"
|
239 |
f"**μΆμ²**: [{a['displayed_link']}]({a['link']})\n\n---\n"
|
@@ -397,63 +405,93 @@ def process_input(prompt):
|
|
397 |
with st.chat_message("assistant"):
|
398 |
placeholder = st.empty(); answer = ""
|
399 |
|
400 |
-
#
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
with
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
457 |
|
458 |
# ββββββββββββββββββββββββββββββββ main / requirements ββββββββββββββββββββββ
|
459 |
def main(): ginigen_app()
|
|
|
45 |
client = anthropic.Anthropic(api_key=ANTHROPIC_KEY)
|
46 |
|
47 |
# ββββββββββββββββββββββββββββββββ λΈλ‘κ·Έ μμ± μμ€ν
ν둬ννΈ ββββββββββββββββ
|
48 |
+
# ββββββββββββββββββββββββββββββββ λΈλ‘κ·Έ μμ± μμ€ν
ν둬ννΈ ββββββββββββββββ
|
49 |
+
def get_system_prompt(template="standard", tone="professional", word_count=1750, include_search_results=False) -> str:
|
50 |
base_prompt = """
|
51 |
λΉμ μ μ λ¬Έ λΈλ‘κ·Έ μμ± μ λ¬Έκ°μ
λλ€. λͺ¨λ λΈλ‘κ·Έ κΈ μμ± μμ²μ λν΄ λ€μμ 8λ¨κ³ νλ μμν¬λ₯Ό μ² μ ν λ°λ₯΄λ, μμ°μ€λ½κ³ λ§€λ ₯μ μΈ κΈμ΄ λλλ‘ μμ±ν΄μΌ ν©λλ€:
|
|
|
52 |
λ
μ μ°κ²° λ¨κ³
|
53 |
1.1. 곡κ°λ νμ±μ μν μΉκ·Όν μΈμ¬
|
54 |
1.2. λ
μμ μ€μ κ³ λ―Όμ λ°μν λμ
μ§λ¬Έ
|
55 |
1.3. μ£Όμ μ λν μ¦κ°μ κ΄μ¬ μ λ
|
|
|
56 |
λ¬Έμ μ μ λ¨κ³
|
57 |
2.1. λ
μμ νμΈν¬μΈνΈ ꡬ체ν
|
58 |
2.2. λ¬Έμ μ μκΈμ±κ³Ό μν₯λ λΆμ
|
59 |
2.3. ν΄κ²° νμμ±μ λν 곡κ°λ νμ±
|
|
|
60 |
μ λ¬Έμ± μ
μ¦ λ¨κ³
|
61 |
3.1. κ°κ΄μ λ°μ΄ν° κΈ°λ° λΆμ
|
62 |
3.2. μ λ¬Έκ° κ²¬ν΄μ μ°κ΅¬ κ²°κ³Ό μΈμ©
|
63 |
3.3. μ€μ μ¬λ‘λ₯Ό ν΅ν λ¬Έμ ꡬ체ν
|
|
|
64 |
μ루μ
μ 곡 λ¨κ³
|
65 |
4.1. λ¨κ³λ³ μ€μ² κ°μ΄λλΌμΈ μ μ
|
66 |
4.2. μ¦μ μ μ© κ°λ₯ν ꡬ체μ ν
|
67 |
4.3. μμ μ₯μ λ¬Όκ³Ό 극볡 λ°©μ ν¬ν¨
|
|
|
68 |
μ λ’°λ κ°ν λ¨κ³
|
69 |
5.1. μ€μ μ±κ³΅ μ¬λ‘ μ μ
|
70 |
5.2. ꡬ체μ μ¬μ©μ νκΈ° μΈμ©
|
71 |
5.3. κ°κ΄μ λ°μ΄ν°λ‘ ν¨κ³Ό μ
μ¦
|
|
|
72 |
νλ μ λ λ¨κ³
|
73 |
6.1. λͺ
νν 첫 μ€μ² λ¨κ³ μ μ
|
74 |
6.2. μκΈμ±μ κ°μ‘°ν νλ μ΄κ΅¬
|
75 |
6.3. μ€μ² λκΈ° λΆμ¬ μμ ν¬ν¨
|
|
|
76 |
μ§μ μ± κ°ν λ¨κ³
|
77 |
7.1. μ루μ
μ νκ³ ν¬λͺ
νκ² κ³΅κ°
|
78 |
7.2. κ°μΈλ³ μ°¨μ΄ μ‘΄μ¬ μΈμ
|
79 |
7.3. νμ 쑰건과 μ£Όμμ¬ν λͺ
μ
|
|
|
80 |
κ΄κ³ μ§μ λ¨κ³
|
81 |
8.1. μ§μ μ± μλ κ°μ¬ μΈμ¬
|
82 |
8.2. λ€μ 컨ν
μΈ μκ³ λ‘ κΈ°λκ° μ‘°μ±
|
|
|
138 |
"storytelling": "μ΄μΌκΈ°λ₯Ό λ€λ €μ£Όλ― κ°μ±μ μ΄κ³ λͺ°μ
κ° μλ ν€μΌλ‘ μμ±ν΄ μ£ΌμΈμ. μΈλ¬Ό, λ°°κ²½, κ°λ±, ν΄κ²°κ³Όμ μ΄ λ΄κΈ΄ λ΄λ¬ν°λΈ ꡬ쑰λ₯Ό νμ©νμΈμ."
|
139 |
}
|
140 |
|
141 |
+
# κ²μ κ²°κ³Ό νμ© μ§μΉ¨ μΆκ°
|
142 |
+
search_guide = """
|
143 |
+
κ²μ κ²°κ³Ό νμ© μ§μΉ¨:
|
144 |
+
- μ 곡λ κ²μ κ²°κ³Όμμ ν΅μ¬ μ 보λ₯Ό μ ννκ² μΆμΆνμ¬ λ°μνμΈμ
|
145 |
+
- λ°μ΄ν°, ν΅κ³, μ¬λ‘λ κ²μ κ²°κ³Όμ μ΅μ μ 보λ₯Ό νμ©νμΈμ
|
146 |
+
- μΈμ© μ μΆμ²λ₯Ό λ³Έλ¬Έ λ΄μ μΈκΈνμΈμ (μ: "OO μ¬μ΄νΈμ λ°λ₯΄λ©΄...")
|
147 |
+
- λΈλ‘κ·Έ λ§μ§λ§μ "μ°Έκ³ μλ£" μΉμ
μ μΆκ°νκ³ μ¬μ©ν μ£Όμ μΆμ²λ₯Ό λ§ν¬μ ν¨κ» λμ΄νμΈμ
|
148 |
+
- μΆμ²κ° μλ μ 보λ "μΌλ°μ μΌλ‘..." λ±μ ννμ μ¬μ©νμΈμ
|
149 |
+
- κ²μ κ²°κ³Όμ μ λ³΄κ° μμΆ©ν κ²½μ°, λ€μν κ΄μ μ κ· νμκ² μ μνμΈμ
|
150 |
+
"""
|
151 |
+
|
152 |
# μ΅μ’
ν둬ννΈ μ‘°ν©
|
153 |
final_prompt = base_prompt
|
154 |
|
|
|
160 |
if tone in tone_guides:
|
161 |
final_prompt += f"\n\nν€μ€λ§€λ: {tone_guides[tone]}"
|
162 |
|
163 |
+
# κ²μ κ²°κ³Ό νμ© μ§μΉ¨ μΆκ° (μΉ κ²μ νμ±ν μ)
|
164 |
+
if include_search_results:
|
165 |
+
final_prompt += f"\n\n{search_guide}"
|
166 |
+
|
167 |
# κΈμ μ μ§μΉ¨ μΆκ°
|
168 |
final_prompt += f"\n\nμμ± μ μ€μμ¬ν\n9.1. κΈμ μ: {word_count-250}-{word_count+250}μ λ΄μΈ\n9.2. λ¬Έλ¨ κΈΈμ΄: 3-4λ¬Έμ₯ μ΄λ΄\n9.3. μκ°μ ꡬλΆ: μμ λͺ©, ꡬλΆμ , λ²νΈ λͺ©λ‘ νμ©\n9.4. λ°μ΄ν°: λͺ¨λ μ 보μ μΆμ² λͺ
μ\n9.5. κ°λ
μ±: λͺ
νν λ¨λ½ ꡬλΆκ³Ό κ°μ‘°μ μ¬μ©"
|
169 |
|
|
|
236 |
def do_web_search(query: str) -> str:
|
237 |
"""μΉ κ²μ μν λ° κ²°κ³Ό ν¬λ§·ν
"""
|
238 |
try:
|
239 |
+
arts = brave_search(query, 20)
|
240 |
if not arts:
|
241 |
logging.warning("κ²μ κ²°κ³Ό μμ, λ체 μ½ν
μΈ μ¬μ©")
|
242 |
return mock_results(query)
|
243 |
|
244 |
+
hdr = "# μΉ κ²μ κ²°κ³Ό\nμ 곡λ κ²μ κ²°κ³Όμ μ 보λ₯Ό μ ννκ² λ°μνμ¬ μ λ’°λ λμ λΈλ‘κ·Έλ₯Ό μμ±νμΈμ. μ 보 μΈμ© μ μΆμ²λ₯Ό λͺ
μνκ³ , λΈλ‘κ·Έ λ§μ§λ§μ μ°Έκ³ μλ£ μΉμ
μ μΆκ°νμΈμ.\n\n"
|
245 |
body = "\n".join(
|
246 |
f"### Result {a['index']}: {a['title']}\n\n{a['snippet']}\n\n"
|
247 |
f"**μΆμ²**: [{a['displayed_link']}]({a['link']})\n\n---\n"
|
|
|
405 |
with st.chat_message("assistant"):
|
406 |
placeholder = st.empty(); answer = ""
|
407 |
|
408 |
+
# μΉ κ²μ νμ±ν μ¬λΆ νμΈ
|
409 |
+
use_web_search = st.session_state.use_web_search
|
410 |
+
|
411 |
+
try:
|
412 |
+
# μ νλ ν
νλ¦Ώ, ν€, λ¨μ΄ μλ‘ μμ€ν
ν둬ννΈ μμ±
|
413 |
+
# μΉ κ²μ νμ±ν μ μ΄λ₯Ό μμ€ν
ν둬ννΈμ λ°μ
|
414 |
+
sys_prompt = get_system_prompt(
|
415 |
+
template=st.session_state.blog_template,
|
416 |
+
tone=st.session_state.blog_tone,
|
417 |
+
word_count=st.session_state.word_count,
|
418 |
+
include_search_results=use_web_search
|
419 |
+
)
|
420 |
+
|
421 |
+
search_results = None
|
422 |
+
if use_web_search:
|
423 |
+
with st.spinner("μΉ κ²μ μ€β¦"):
|
424 |
+
search_results = do_web_search(keywords(prompt))
|
425 |
+
|
426 |
+
# λ©μμ§ λ°°μ΄ μ€λΉ
|
427 |
+
messages = [{"role": m["role"], "content": m["content"]}
|
428 |
+
for m in st.session_state.messages]
|
429 |
+
|
430 |
+
# μΉ κ²μ κ²°κ³Όκ° μμΌλ©΄ λ³λ λ©μμ§λ‘ μΆκ° (μμ€ν
ν둬ννΈ λμ )
|
431 |
+
if search_results:
|
432 |
+
messages.append({"role": "user", "content": search_results})
|
433 |
+
|
434 |
+
# Claude μ€νΈλ¦¬λ°
|
435 |
+
with client.messages.stream(
|
436 |
+
model=st.session_state.ai_model, max_tokens=MAX_TOKENS,
|
437 |
+
system=sys_prompt,
|
438 |
+
messages=messages
|
439 |
+
) as stream:
|
440 |
+
for t in stream.text_stream:
|
441 |
+
answer += t or ""
|
442 |
+
placeholder.markdown(answer + "β")
|
443 |
+
placeholder.markdown(answer)
|
444 |
+
|
445 |
+
# μ΄λ―Έμ§ μ΅μ
|
446 |
+
answer_entry_saved = False
|
447 |
+
if st.session_state.generate_image:
|
448 |
+
with st.spinner("μ΄λ―Έμ§ μμ± μ€β¦"):
|
449 |
+
ip = extract_image_prompt(answer, prompt)
|
450 |
+
img, cap = generate_image(ip)
|
451 |
+
if img:
|
452 |
+
st.image(img, caption=cap)
|
453 |
+
st.session_state.messages.append(
|
454 |
+
{"role": "assistant", "content": answer,
|
455 |
+
"image": img, "image_caption": cap})
|
456 |
+
answer_entry_saved = True
|
457 |
+
if not answer_entry_saved:
|
458 |
+
st.session_state.messages.append(
|
459 |
+
{"role": "assistant", "content": answer})
|
460 |
+
|
461 |
+
# λ³Έλ¬Έ λ€μ΄λ‘λ λ²νΌ (MD / HTML)
|
462 |
+
st.subheader("μ΄ λΈλ‘κ·Έ λ€μ΄λ‘λ")
|
463 |
+
b1, b2 = st.columns(2)
|
464 |
+
b1.download_button("λ§ν¬λ€μ΄", answer,
|
465 |
+
file_name=f"{prompt[:30]}.md", mime="text/markdown")
|
466 |
+
b2.download_button("HTML", md_to_html(answer, prompt[:30]),
|
467 |
+
file_name=f"{prompt[:30]}.html", mime="text/html")
|
468 |
+
|
469 |
+
# ββ μλ λ°±μ
μ μ₯
|
470 |
+
if st.session_state.auto_save and st.session_state.messages:
|
471 |
+
try:
|
472 |
+
fn = f"chat_history_auto_{datetime.now():%Y%m%d_%H%M%S}.json"
|
473 |
+
with open(fn, "w", encoding="utf-8") as fp:
|
474 |
+
json.dump(st.session_state.messages, fp,
|
475 |
+
ensure_ascii=False, indent=2)
|
476 |
+
except Exception as e:
|
477 |
+
logging.error(f"μλ μ μ₯ μ€ν¨: {e}")
|
478 |
+
|
479 |
+
except anthropic.BadRequestError as e:
|
480 |
+
error_message = str(e)
|
481 |
+
if "credit balance is too low" in error_message:
|
482 |
+
placeholder.error("β οΈ API ν¬λ λ§ λΆμ‘±: Anthropic API κ³μ μ ν¬λ λ§μ μΆ©μ ν΄μ£ΌμΈμ.")
|
483 |
+
answer = "API ν¬λ λ§μ΄ λΆμ‘±νμ¬ λΈλ‘κ·Έλ₯Ό μμ±ν μ μμ΅λλ€. API κ³μ μ ν¬λ λ§μ μΆ©μ ν ν λ€μ μλν΄μ£ΌμΈμ."
|
484 |
+
else:
|
485 |
+
placeholder.error(f"API μμ² μ€λ₯: {error_message}")
|
486 |
+
answer = f"API μμ² μ€ μ€λ₯κ° λ°μνμ΅λλ€: {error_message}"
|
487 |
+
|
488 |
+
st.session_state.messages.append({"role": "assistant", "content": answer})
|
489 |
+
|
490 |
+
except Exception as e:
|
491 |
+
error_message = str(e)
|
492 |
+
placeholder.error(f"μ€λ₯ λ°μ: {error_message}")
|
493 |
+
answer = f"μμ² μ²λ¦¬ μ€ μ€λ₯κ° λ°μνμ΅λλ€: {error_message}"
|
494 |
+
st.session_state.messages.append({"role": "assistant", "content": answer})
|
495 |
|
496 |
# ββββββββββββββββββββββββββββββββ main / requirements ββββββββββββββββββββββ
|
497 |
def main(): ginigen_app()
|