seawolf2357 commited on
Commit
ff48136
Β·
verified Β·
1 Parent(s): 78bdfe0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +218 -32
app.py CHANGED
@@ -135,6 +135,30 @@ footer, .footer, .gradio-container footer, .built-with, [class*="footer"], .grad
135
  box-shadow: 2px 2px 0 #1F2937;
136
  }
137
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  .gr-panel, .gr-box, .gr-form, .block, .gr-group {
139
  background: #FFF !important;
140
  border: 3px solid #1F2937 !important;
@@ -225,6 +249,58 @@ textarea:focus, input[type="text"]:focus {
225
  font-size: 1.5rem;
226
  }
227
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
228
  label, .gr-input-label, .gr-block-label {
229
  color: #1F2937 !important;
230
  font-family: 'Comic Neue', cursive !important;
@@ -799,14 +875,14 @@ def convert_hwp_to_markdown(input_path: str) -> tuple:
799
  # ============== LLM API ==============
800
  def call_groq_api_stream(messages: List[Dict], api_key: str) -> Generator[str, None, None]:
801
  if not api_key:
802
- yield "❌ Groq API ν‚€κ°€ μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€."
803
  return
804
  try:
805
  response = requests.post(
806
  "https://api.groq.com/openai/v1/chat/completions",
807
  headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
808
  json={
809
- "model": "meta-llama/llama-4-scout-17b-16e-instruct",
810
  "messages": messages,
811
  "temperature": 0.7,
812
  "max_tokens": 8192,
@@ -833,7 +909,7 @@ def call_groq_api_stream(messages: List[Dict], api_key: str) -> Generator[str, N
833
 
834
  def call_fireworks_api_stream(messages: List[Dict], image_base64: str, mime_type: str, api_key: str) -> Generator[str, None, None]:
835
  if not api_key:
836
- yield "❌ Fireworks API ν‚€κ°€ μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€."
837
  return
838
  try:
839
  formatted_messages = [{"role": m["role"], "content": m["content"]} for m in messages[:-1]]
@@ -849,7 +925,7 @@ def call_fireworks_api_stream(messages: List[Dict], image_base64: str, mime_type
849
  headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
850
  json={
851
  "model": "accounts/fireworks/models/qwen3-vl-235b-a22b-thinking",
852
- "max_tokens": 4096,
853
  "temperature": 0.6,
854
  "messages": formatted_messages,
855
  "stream": True
@@ -885,25 +961,28 @@ def process_file(file_path: str) -> tuple:
885
  if is_hwp_file(file_path) or is_hwpx_file(file_path):
886
  text, error = extract_text_from_hwp_or_hwpx(file_path)
887
  if text and len(text.strip()) > 20:
888
- return "text", f"[πŸ“„ ν•œκΈ€ λ¬Έμ„œ: {filename}]\n\n{text}", None
 
 
889
  return "error", f"ν•œκΈ€ λ¬Έμ„œ μΆ”μΆœ μ‹€νŒ¨: {error}", None
890
 
891
  if is_pdf_file(file_path):
892
  text = extract_text_from_pdf(file_path)
893
  if text:
894
- return "text", f"[πŸ“‘ PDF λ¬Έμ„œ: {filename}]\n\n{text}", None
 
895
  return "error", "PDF μΆ”μΆœ μ‹€νŒ¨", None
896
 
897
  if is_text_file(file_path):
898
  text = extract_text_from_txt(file_path)
899
  if text:
900
- return "text", f"[πŸ“ ν…μŠ€νŠΈ 파일: {filename}]\n\n{text}", None
901
  return "error", "ν…μŠ€νŠΈ 읽기 μ‹€νŒ¨", None
902
 
903
  return "unsupported", f"μ§€μ›ν•˜μ§€ μ•ŠλŠ” ν˜•μ‹: {filename}", None
904
 
905
  def chat_response(message: str, history: List[Dict], file: Optional[str],
906
- session_id: str, groq_key: str, fireworks_key: str) -> Generator[tuple, None, None]:
907
  if history is None:
908
  history = []
909
  if not message.strip() and not file:
@@ -914,10 +993,12 @@ def chat_response(message: str, history: List[Dict], file: Optional[str],
914
 
915
  file_type, file_content, file_mime = None, None, None
916
  file_info = None
 
917
 
918
  if file:
 
919
  file_type, file_content, file_mime = process_file(file)
920
- file_info = json.dumps({"type": file_type, "filename": os.path.basename(file)})
921
 
922
  if file_type == "error":
923
  history = history + [
@@ -934,41 +1015,104 @@ def chat_response(message: str, history: List[Dict], file: Optional[str],
934
  yield history, session_id
935
  return
936
 
 
937
  user_msg = message
938
  if file:
939
- filename = os.path.basename(file)
940
  user_msg = f"πŸ“Ž {filename}\n\n{message}" if message else f"πŸ“Ž {filename}"
941
 
942
  history = history + [{"role": "user", "content": user_msg}, {"role": "assistant", "content": ""}]
943
  yield history, session_id
944
 
 
945
  db_messages = get_session_messages(session_id, limit=10)
946
- api_messages = [{
947
- "role": "system",
948
- "content": "당신은 도움이 λ˜λŠ” AI μ–΄μ‹œμŠ€ν„΄νŠΈμž…λ‹ˆλ‹€. ν•œκ΅­μ–΄λ‘œ μžμ—°μŠ€λŸ½κ²Œ λŒ€ν™”ν•˜λ©°, 파일이 μ²¨λΆ€λ˜λ©΄ λ‚΄μš©μ„ μƒμ„Ένžˆ λΆ„μ„ν•˜μ—¬ λ‹΅λ³€ν•©λ‹ˆλ‹€. λ¬Έμ„œμ˜ 핡심 λ‚΄μš©μ„ νŒŒμ•…ν•˜κ³ , μ‚¬μš©μžμ˜ μ§ˆλ¬Έμ— μ •ν™•ν•˜κ²Œ λ‹΅λ³€ν•˜μ„Έμš”."
949
- }]
950
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
951
  for m in db_messages:
952
  api_messages.append({"role": m["role"], "content": m["content"]})
953
 
954
- current_content = message or ""
955
  if file_type == "text" and file_content:
956
- current_content = f"{file_content}\n\nμ‚¬μš©μž 질문: {message}" if message else f"{file_content}\n\nμœ„ λ¬Έμ„œ λ‚΄μš©μ„ μš”μ•½ν•΄μ£Όμ„Έμš”."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
957
 
958
  api_messages.append({"role": "user", "content": current_content})
959
 
 
 
 
 
 
 
 
 
 
960
  full_response = ""
961
  if file_type == "image":
962
- for chunk in call_fireworks_api_stream(api_messages, file_content, file_mime, fireworks_key):
963
  full_response += chunk
964
  history[-1] = {"role": "assistant", "content": full_response}
965
  yield history, session_id
966
  else:
967
- for chunk in call_groq_api_stream(api_messages, groq_key):
968
  full_response += chunk
969
  history[-1] = {"role": "assistant", "content": full_response}
970
  yield history, session_id
971
 
 
972
  save_message(session_id, "user", current_content, file_info)
973
  save_message(session_id, "assistant", full_response)
974
 
@@ -1056,7 +1200,7 @@ def convert_hwp(file, output_format, progress=gr.Progress()):
1056
  f.write(text)
1057
  ext = ".txt"
1058
 
1059
- elif output_format == "Markdown":
1060
  text, error = convert_hwp_to_markdown(input_path)
1061
  if text:
1062
  output_path = os.path.join(tmp_dir, "output.md")
@@ -1146,6 +1290,14 @@ with gr.Blocks(title="HWP AI μ–΄μ‹œμŠ€ν„΄νŠΈ", css=COMIC_CSS, delete_cache=(60,
1146
  </div>
1147
  """)
1148
 
 
 
 
 
 
 
 
 
1149
  session_state = gr.State("")
1150
 
1151
  with gr.Tabs():
@@ -1167,12 +1319,6 @@ with gr.Blocks(title="HWP AI μ–΄μ‹œμŠ€ν„΄νŠΈ", css=COMIC_CSS, delete_cache=(60,
1167
 
1168
  with gr.Row():
1169
  with gr.Column(scale=1):
1170
- gr.HTML('<div class="info-box">βš™οΈ <b>μ„€μ •</b></div>')
1171
-
1172
- with gr.Accordion("πŸ”‘ API ν‚€ μ„€μ •", open=True):
1173
- groq_key = gr.Textbox(label="Groq API Key", type="password", value=GROQ_API_KEY, placeholder="gsk_...")
1174
- fireworks_key = gr.Textbox(label="Fireworks API Key", type="password", value=FIREWORKS_API_KEY, placeholder="fw_...")
1175
-
1176
  gr.HTML("""
1177
  <div class="info-box">
1178
  πŸ“ <b>지원 파일 ν˜•μ‹</b><br><br>
@@ -1220,6 +1366,45 @@ with gr.Blocks(title="HWP AI μ–΄μ‹œμŠ€ν„΄νŠΈ", css=COMIC_CSS, delete_cache=(60,
1220
  </div>
1221
  """)
1222
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1223
  with gr.Row():
1224
  with gr.Column():
1225
  gr.HTML('<div class="info-box">πŸ“€ <b>파일 μ—…λ‘œλ“œ</b></div>')
@@ -1229,8 +1414,8 @@ with gr.Blocks(title="HWP AI μ–΄μ‹œμŠ€ν„΄νŠΈ", css=COMIC_CSS, delete_cache=(60,
1229
  elem_classes=["upload-box"]
1230
  )
1231
  format_select = gr.Radio(
1232
- ["HTML", "ODT (OpenDocument)", "TXT (ν…μŠ€νŠΈ)", "Markdown", "XML"],
1233
- value="TXT (ν…μŠ€νŠΈ)",
1234
  label="πŸ“‹ λ³€ν™˜ ν˜•μ‹"
1235
  )
1236
  convert_btn = gr.Button("πŸ”„ λ³€ν™˜ν•˜κΈ°", variant="primary", size="lg")
@@ -1245,7 +1430,7 @@ with gr.Blocks(title="HWP AI μ–΄μ‹œμŠ€ν„΄νŠΈ", css=COMIC_CSS, delete_cache=(60,
1245
 
1246
  gr.HTML("""
1247
  <div class="info-box">
1248
- ℹ️ <b>μ•ˆλ‚΄</b>: HWPX νŒŒμΌμ€ TXT, Markdown, XML λ³€ν™˜λ§Œ μ§€μ›λ©λ‹ˆλ‹€.
1249
  </div>
1250
  """)
1251
 
@@ -1255,20 +1440,21 @@ with gr.Blocks(title="HWP AI μ–΄μ‹œμŠ€ν„΄νŠΈ", css=COMIC_CSS, delete_cache=(60,
1255
  <p style="font-family:'Bangers',cursive;font-size:1.8rem;letter-spacing:2px">πŸ“„ HWP AI μ–΄μ‹œμŠ€ν„΄νŠΈ πŸ€–</p>
1256
  <p>AIκ°€ HWP νŒŒμΌμ„ 읽고, 보고, λ§ν•˜λ©°, μƒκ°ν•˜κ³  κΈ°μ–΅ν•©λ‹ˆλ‹€!</p>
1257
  <p>πŸ“– READ β€’ πŸ‘οΈ SEE β€’ πŸ’¬ SPEAK β€’ 🧠 THINK β€’ πŸ’Ύ MEMORY</p>
 
1258
  <p style="margin-top:10px"><a href="https://www.humangen.ai" target="_blank" style="color:#FACC15;text-decoration:none;font-weight:bold;">🏠 www.humangen.ai</a></p>
1259
  </div>
1260
  """)
1261
 
1262
  # ============== 이벀트 ν•Έλ“€λŸ¬ ==============
1263
- def on_submit(msg, hist, f, sid, gk, fk):
1264
  if hist is None:
1265
  hist = []
1266
- for r in chat_response(msg, hist, f, sid, gk, fk):
1267
  yield r[0], r[1], "", None
1268
 
1269
- submit_btn.click(on_submit, [msg_input, chatbot, file_upload, session_state, groq_key, fireworks_key],
1270
  [chatbot, session_state, msg_input, file_upload])
1271
- msg_input.submit(on_submit, [msg_input, chatbot, file_upload, session_state, groq_key, fireworks_key],
1272
  [chatbot, session_state, msg_input, file_upload])
1273
 
1274
  new_btn.click(lambda: ([], create_session(), None, ""), outputs=[chatbot, session_state, file_upload, msg_input])
 
135
  box-shadow: 2px 2px 0 #1F2937;
136
  }
137
 
138
+ /* 무료 μ„œλΉ„μŠ€ μ•ˆλ‚΄ λ°•μŠ€ */
139
+ .free-service-notice {
140
+ text-align: center;
141
+ padding: 10px 15px;
142
+ background: linear-gradient(135deg, #FEE2E2 0%, #FECACA 100%);
143
+ border: 3px solid #1F2937;
144
+ border-radius: 8px;
145
+ margin: 10px 0;
146
+ box-shadow: 4px 4px 0 #1F2937;
147
+ font-family: 'Comic Neue', cursive;
148
+ font-weight: 700;
149
+ color: #991B1B;
150
+ }
151
+
152
+ .free-service-notice a {
153
+ color: #1D4ED8;
154
+ text-decoration: none;
155
+ font-weight: 700;
156
+ }
157
+
158
+ .free-service-notice a:hover {
159
+ text-decoration: underline;
160
+ }
161
+
162
  .gr-panel, .gr-box, .gr-form, .block, .gr-group {
163
  background: #FFF !important;
164
  border: 3px solid #1F2937 !important;
 
249
  font-size: 1.5rem;
250
  }
251
 
252
+ /* Markdown κ°•μ‘° λ°•μŠ€ */
253
+ .markdown-highlight-box {
254
+ background: linear-gradient(135deg, #EC4899 0%, #F472B6 100%) !important;
255
+ border: 4px solid #1F2937 !important;
256
+ border-radius: 12px !important;
257
+ padding: 20px !important;
258
+ margin: 15px 0 !important;
259
+ box-shadow: 6px 6px 0 #1F2937 !important;
260
+ animation: pulse-glow 2s ease-in-out infinite;
261
+ }
262
+
263
+ @keyframes pulse-glow {
264
+ 0%, 100% { box-shadow: 6px 6px 0 #1F2937; }
265
+ 50% { box-shadow: 8px 8px 0 #1F2937, 0 0 20px rgba(236, 72, 153, 0.5); }
266
+ }
267
+
268
+ .markdown-title {
269
+ font-family: 'Bangers', cursive !important;
270
+ font-size: 2rem !important;
271
+ color: #FFF !important;
272
+ text-shadow: 3px 3px 0 #1F2937 !important;
273
+ letter-spacing: 2px !important;
274
+ margin-bottom: 15px !important;
275
+ text-align: center !important;
276
+ }
277
+
278
+ .markdown-benefits {
279
+ display: grid;
280
+ grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
281
+ gap: 12px;
282
+ margin-top: 10px;
283
+ }
284
+
285
+ .markdown-benefit-item {
286
+ background: rgba(255,255,255,0.95) !important;
287
+ border: 3px solid #1F2937 !important;
288
+ border-radius: 8px !important;
289
+ padding: 12px !important;
290
+ box-shadow: 3px 3px 0 #1F2937 !important;
291
+ font-family: 'Comic Neue', cursive !important;
292
+ font-weight: 700 !important;
293
+ font-size: 0.95rem !important;
294
+ color: #1F2937 !important;
295
+ text-align: center !important;
296
+ }
297
+
298
+ .markdown-benefit-icon {
299
+ font-size: 1.8rem !important;
300
+ display: block !important;
301
+ margin-bottom: 5px !important;
302
+ }
303
+
304
  label, .gr-input-label, .gr-block-label {
305
  color: #1F2937 !important;
306
  font-family: 'Comic Neue', cursive !important;
 
875
  # ============== LLM API ==============
876
  def call_groq_api_stream(messages: List[Dict], api_key: str) -> Generator[str, None, None]:
877
  if not api_key:
878
+ yield "❌ Groq API ν‚€κ°€ μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€. ν™˜κ²½λ³€μˆ˜ GROQ_API_KEYλ₯Ό μ„€μ •ν•΄μ£Όμ„Έμš”."
879
  return
880
  try:
881
  response = requests.post(
882
  "https://api.groq.com/openai/v1/chat/completions",
883
  headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
884
  json={
885
+ "model": "mopenai/gpt-oss-120b",
886
  "messages": messages,
887
  "temperature": 0.7,
888
  "max_tokens": 8192,
 
909
 
910
  def call_fireworks_api_stream(messages: List[Dict], image_base64: str, mime_type: str, api_key: str) -> Generator[str, None, None]:
911
  if not api_key:
912
+ yield "❌ Fireworks API ν‚€κ°€ μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€. ν™˜κ²½λ³€μˆ˜ FIREWORKS_API_KEYλ₯Ό μ„€μ •ν•΄μ£Όμ„Έμš”."
913
  return
914
  try:
915
  formatted_messages = [{"role": m["role"], "content": m["content"]} for m in messages[:-1]]
 
925
  headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
926
  json={
927
  "model": "accounts/fireworks/models/qwen3-vl-235b-a22b-thinking",
928
+ "max_tokens": 8000,
929
  "temperature": 0.6,
930
  "messages": formatted_messages,
931
  "stream": True
 
961
  if is_hwp_file(file_path) or is_hwpx_file(file_path):
962
  text, error = extract_text_from_hwp_or_hwpx(file_path)
963
  if text and len(text.strip()) > 20:
964
+ print(f"πŸ“„ [λ¬Έμ„œ λ‚΄μš© μΆ”μΆœ μ™„λ£Œ] {len(text)} κΈ€μž")
965
+ print(f"πŸ“„ [λ¬Έμ„œ 미리보기] {text[:500]}...")
966
+ return "text", text, None
967
  return "error", f"ν•œκΈ€ λ¬Έμ„œ μΆ”μΆœ μ‹€νŒ¨: {error}", None
968
 
969
  if is_pdf_file(file_path):
970
  text = extract_text_from_pdf(file_path)
971
  if text:
972
+ print(f"πŸ“„ [PDF λ‚΄μš© μΆ”μΆœ μ™„λ£Œ] {len(text)} κΈ€μž")
973
+ return "text", text, None
974
  return "error", "PDF μΆ”μΆœ μ‹€νŒ¨", None
975
 
976
  if is_text_file(file_path):
977
  text = extract_text_from_txt(file_path)
978
  if text:
979
+ return "text", text, None
980
  return "error", "ν…μŠ€νŠΈ 읽기 μ‹€νŒ¨", None
981
 
982
  return "unsupported", f"μ§€μ›ν•˜μ§€ μ•ŠλŠ” ν˜•μ‹: {filename}", None
983
 
984
  def chat_response(message: str, history: List[Dict], file: Optional[str],
985
+ session_id: str) -> Generator[tuple, None, None]:
986
  if history is None:
987
  history = []
988
  if not message.strip() and not file:
 
993
 
994
  file_type, file_content, file_mime = None, None, None
995
  file_info = None
996
+ filename = None
997
 
998
  if file:
999
+ filename = os.path.basename(file)
1000
  file_type, file_content, file_mime = process_file(file)
1001
+ file_info = json.dumps({"type": file_type, "filename": filename})
1002
 
1003
  if file_type == "error":
1004
  history = history + [
 
1015
  yield history, session_id
1016
  return
1017
 
1018
+ # μ‚¬μš©μž λ©”μ‹œμ§€ ν‘œμ‹œ
1019
  user_msg = message
1020
  if file:
 
1021
  user_msg = f"πŸ“Ž {filename}\n\n{message}" if message else f"πŸ“Ž {filename}"
1022
 
1023
  history = history + [{"role": "user", "content": user_msg}, {"role": "assistant", "content": ""}]
1024
  yield history, session_id
1025
 
1026
+ # 이전 λŒ€ν™” 뢈러였기
1027
  db_messages = get_session_messages(session_id, limit=10)
 
 
 
 
1028
 
1029
+ # μ‹œμŠ€ν…œ ν”„λ‘¬ν”„νŠΈ - λ¬Έμ„œ 뢄석 κ°•ν™”
1030
+ system_prompt = """당신은 λ¬Έμ„œ 뢄석 μ „λ¬Έ AI μ–΄μ‹œμŠ€ν„΄νŠΈμž…λ‹ˆλ‹€.
1031
+
1032
+ ## 핡심 μ—­ν• 
1033
+ - μ‚¬μš©μžκ°€ μ—…λ‘œλ“œν•œ λ¬Έμ„œμ˜ λ‚΄μš©μ„ **μ •ν™•ν•˜κ²Œ 뢄석**ν•˜κ³  **ꡬ체적으둜 λ‹΅λ³€**ν•©λ‹ˆλ‹€.
1034
+ - λ¬Έμ„œμ— μžˆλŠ” **μ‹€μ œ λ‚΄μš©**을 기반으둜만 λ‹΅λ³€ν•©λ‹ˆλ‹€.
1035
+ - λ¬Έμ„œμ— μ—†λŠ” λ‚΄μš©μ€ μΆ”μΈ‘ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
1036
+
1037
+ ## λ¬Έμ„œ 뢄석 방법
1038
+ 1. **λ¬Έμ„œκ°€ 제곡되면**: λ¬Έμ„œ 전체 λ‚΄μš©μ„ 꼼꼼히 읽고 핡심 정보λ₯Ό νŒŒμ•…ν•©λ‹ˆλ‹€.
1039
+ 2. **μš”μ•½ μš”μ²­ μ‹œ**: λ¬Έμ„œμ˜ 주제, λͺ©μ , 핡심 λ‚΄μš©, μ£Όμš” ν•­λͺ©μ„ κ΅¬μ‘°ν™”ν•˜μ—¬ μš”μ•½ν•©λ‹ˆλ‹€.
1040
+ 3. **질문 응닡 μ‹œ**: λ¬Έμ„œμ—μ„œ κ΄€λ ¨ λ‚΄μš©μ„ μ°Ύμ•„ **직접 μΈμš©ν•˜κ±°λ‚˜ ꡬ체적으둜 μ„€λͺ…**ν•©λ‹ˆλ‹€.
1041
+
1042
+ ## λ‹΅λ³€ ν˜•μ‹
1043
+ - ν•œκ΅­μ–΄λ‘œ μžμ—°μŠ€λŸ½κ³  λͺ…ν™•ν•˜κ²Œ λ‹΅λ³€ν•©λ‹ˆλ‹€.
1044
+ - λ¬Έμ„œ λ‚΄μš©μ„ μΈμš©ν•  λ•ŒλŠ” ꡬ체적으둜 μ–ΈκΈ‰ν•©λ‹ˆλ‹€.
1045
+ - κΈ΄ λ¬Έμ„œλŠ” μ„Ήμ…˜λ³„λ‘œ λ‚˜λˆ„μ–΄ μ •λ¦¬ν•©λ‹ˆλ‹€.
1046
+
1047
+ ## μ£Όμ˜μ‚¬ν•­
1048
+ - λ¬Έμ„œμ— **μ‹€μ œλ‘œ μžˆλŠ” λ‚΄μš©λ§Œ** 닡변에 ν¬ν•¨ν•©λ‹ˆλ‹€.
1049
+ - λΆˆν™•μ‹€ν•œ λ‚΄μš©μ€ "λ¬Έμ„œμ—μ„œ ν™•μΈλ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€"라고 λͺ…μ‹œν•©λ‹ˆλ‹€."""
1050
+
1051
+ api_messages = [{"role": "system", "content": system_prompt}]
1052
+
1053
+ # 이전 λŒ€ν™” μΆ”κ°€
1054
  for m in db_messages:
1055
  api_messages.append({"role": m["role"], "content": m["content"]})
1056
 
1057
+ # ν˜„μž¬ λ©”μ‹œμ§€ ꡬ성 - λ¬Έμ„œ λ‚΄μš©μ„ λͺ…ν™•ν•˜κ²Œ ꡬ뢄
1058
  if file_type == "text" and file_content:
1059
+ if message:
1060
+ current_content = f"""## πŸ“„ μ—…λ‘œλ“œλœ λ¬Έμ„œ λ‚΄μš© ({filename})
1061
+
1062
+ λ‹€μŒμ€ μ‚¬μš©μžκ°€ μ—…λ‘œλ“œν•œ λ¬Έμ„œμ˜ 전체 λ‚΄μš©μž…λ‹ˆλ‹€:
1063
+
1064
+ ---
1065
+ {file_content}
1066
+ ---
1067
+
1068
+ ## πŸ’¬ μ‚¬μš©μž 질문
1069
+ {message}
1070
+
1071
+ μœ„ λ¬Έμ„œ λ‚΄μš©μ„ λ°”νƒ•μœΌλ‘œ μ‚¬μš©μžμ˜ μ§ˆλ¬Έμ— **ꡬ체적이고 μ •ν™•ν•˜κ²Œ** λ‹΅λ³€ν•΄μ£Όμ„Έμš”."""
1072
+ else:
1073
+ current_content = f"""## πŸ“„ μ—…λ‘œλ“œλœ λ¬Έμ„œ λ‚΄μš© ({filename})
1074
+
1075
+ λ‹€μŒμ€ μ‚¬μš©μžκ°€ μ—…λ‘œλ“œν•œ λ¬Έμ„œμ˜ 전체 λ‚΄μš©μž…λ‹ˆλ‹€:
1076
+
1077
+ ---
1078
+ {file_content}
1079
+ ---
1080
+
1081
+ ## πŸ“‹ μš”μ²­μ‚¬ν•­
1082
+ μœ„ λ¬Έμ„œμ˜ λ‚΄μš©μ„ λ‹€μŒ ν˜•μ‹μœΌλ‘œ **μƒμ„Έν•˜κ²Œ μš”μ•½**ν•΄μ£Όμ„Έμš”:
1083
+
1084
+ 1. **λ¬Έμ„œ 제λͺ©/주제**: λ¬Έμ„œκ°€ λ‹€λ£¨λŠ” μ£Όμš” 주제
1085
+ 2. **λ¬Έμ„œ λͺ©μ **: 이 λ¬Έμ„œμ˜ μž‘μ„± λͺ©μ 
1086
+ 3. **핡심 λ‚΄μš©**: κ°€μž₯ μ€‘μš”ν•œ λ‚΄μš© 3-5οΏ½οΏ½οΏ½μ§€
1087
+ 4. **μ„ΈλΆ€ ν•­λͺ©**: λ¬Έμ„œμ— ν¬ν•¨λœ μ£Όμš” μ„Ήμ…˜μ΄λ‚˜ ν•­λͺ©
1088
+ 5. **κ²°λ‘ /μš”μ•½**: λ¬Έμ„œμ˜ 핡심 λ©”μ‹œμ§€"""
1089
+ else:
1090
+ current_content = message or ""
1091
 
1092
  api_messages.append({"role": "user", "content": current_content})
1093
 
1094
+ # 디버그 둜그
1095
+ print(f"\nπŸ€– [API μš”μ²­]")
1096
+ print(f" - λ©”μ‹œμ§€ 수: {len(api_messages)}")
1097
+ print(f" - 파일 νƒ€μž…: {file_type}")
1098
+ print(f" - λ¬Έμ„œ 길이: {len(file_content) if file_content else 0} κΈ€μž")
1099
+ if file_content:
1100
+ print(f" - λ¬Έμ„œ 미리보기: {file_content[:200]}...")
1101
+
1102
+ # 응닡 생성
1103
  full_response = ""
1104
  if file_type == "image":
1105
+ for chunk in call_fireworks_api_stream(api_messages, file_content, file_mime, FIREWORKS_API_KEY):
1106
  full_response += chunk
1107
  history[-1] = {"role": "assistant", "content": full_response}
1108
  yield history, session_id
1109
  else:
1110
+ for chunk in call_groq_api_stream(api_messages, GROQ_API_KEY):
1111
  full_response += chunk
1112
  history[-1] = {"role": "assistant", "content": full_response}
1113
  yield history, session_id
1114
 
1115
+ # DB μ €μž₯
1116
  save_message(session_id, "user", current_content, file_info)
1117
  save_message(session_id, "assistant", full_response)
1118
 
 
1200
  f.write(text)
1201
  ext = ".txt"
1202
 
1203
+ elif output_format == "⭐ MARKDOWN (μΆ”μ²œ)":
1204
  text, error = convert_hwp_to_markdown(input_path)
1205
  if text:
1206
  output_path = os.path.join(tmp_dir, "output.md")
 
1290
  </div>
1291
  """)
1292
 
1293
+ # 무료 μ„œλΉ„μŠ€ μ•ˆλ‚΄
1294
+ gr.HTML("""
1295
+ <div class="free-service-notice">
1296
+ πŸ†“ λ³Έ μ„œλΉ„μŠ€λŠ” <b>무료 버전</b>으둜 일뢀 κΈ°λŠ₯에 μ œμ•½μ΄ μžˆμŠ΅λ‹ˆλ‹€.<br>
1297
+ πŸ“§ 문의: <a href="mailto:arxivgpt@gmail.com">arxivgpt@gmail.com</a>
1298
+ </div>
1299
+ """)
1300
+
1301
  session_state = gr.State("")
1302
 
1303
  with gr.Tabs():
 
1319
 
1320
  with gr.Row():
1321
  with gr.Column(scale=1):
 
 
 
 
 
 
1322
  gr.HTML("""
1323
  <div class="info-box">
1324
  πŸ“ <b>지원 파일 ν˜•μ‹</b><br><br>
 
1366
  </div>
1367
  """)
1368
 
1369
+ # Markdown κ°•μ‘° λ°•μŠ€
1370
+ gr.HTML("""
1371
+ <div class="markdown-highlight-box">
1372
+ <div class="markdown-title">⭐ MARKDOWN λ³€ν™˜ μΆ”μ²œ! ⭐</div>
1373
+ <div class="markdown-benefits">
1374
+ <div class="markdown-benefit-item">
1375
+ <span class="markdown-benefit-icon">πŸ€–</span>
1376
+ <b>AI/LLM μ΅œμ ν™”</b><br>
1377
+ ChatGPT, Claude λ“± AI에 λ°”λ‘œ μž…λ ₯ κ°€λŠ₯
1378
+ </div>
1379
+ <div class="markdown-benefit-item">
1380
+ <span class="markdown-benefit-icon">πŸ“</span>
1381
+ <b>λ²”μš© 포맷</b><br>
1382
+ GitHub, Notion, λΈ”λ‘œκ·Έ λ“± μ–΄λ””μ„œλ‚˜ μ‚¬μš©
1383
+ </div>
1384
+ <div class="markdown-benefit-item">
1385
+ <span class="markdown-benefit-icon">πŸ”</span>
1386
+ <b>ꡬ쑰 μœ μ§€</b><br>
1387
+ 제λͺ©, λͺ©λ‘, ν‘œ λ“± λ¬Έμ„œ ꡬ쑰 보쑴
1388
+ </div>
1389
+ <div class="markdown-benefit-item">
1390
+ <span class="markdown-benefit-icon">⚑</span>
1391
+ <b>가볍고 빠름</b><br>
1392
+ μš©λŸ‰μ΄ μž‘κ³  처리 속도 빠름
1393
+ </div>
1394
+ <div class="markdown-benefit-item">
1395
+ <span class="markdown-benefit-icon">πŸ”„</span>
1396
+ <b>λ³€ν™˜ 용이</b><br>
1397
+ HTML, PDF, Word λ“±μœΌλ‘œ μž¬λ³€ν™˜ κ°€λŠ₯
1398
+ </div>
1399
+ <div class="markdown-benefit-item">
1400
+ <span class="markdown-benefit-icon">✏️</span>
1401
+ <b>νŽΈμ§‘ κ°„νŽΈ</b><br>
1402
+ λ©”λͺ¨μž₯μœΌλ‘œλ„ λ°”λ‘œ μˆ˜μ • κ°€λŠ₯
1403
+ </div>
1404
+ </div>
1405
+ </div>
1406
+ """)
1407
+
1408
  with gr.Row():
1409
  with gr.Column():
1410
  gr.HTML('<div class="info-box">πŸ“€ <b>파일 μ—…λ‘œλ“œ</b></div>')
 
1414
  elem_classes=["upload-box"]
1415
  )
1416
  format_select = gr.Radio(
1417
+ ["⭐ MARKDOWN (μΆ”μ²œ)", "TXT (ν…μŠ€νŠΈ)", "HTML", "ODT (OpenDocument)", "XML"],
1418
+ value="⭐ MARKDOWN (μΆ”μ²œ)",
1419
  label="πŸ“‹ λ³€ν™˜ ν˜•μ‹"
1420
  )
1421
  convert_btn = gr.Button("πŸ”„ λ³€ν™˜ν•˜κΈ°", variant="primary", size="lg")
 
1430
 
1431
  gr.HTML("""
1432
  <div class="info-box">
1433
+ ℹ️ <b>μ•ˆλ‚΄</b>: HWPX νŒŒμΌμ€ MARKDOWN, TXT, XML λ³€ν™˜λ§Œ μ§€μ›λ©λ‹ˆλ‹€.
1434
  </div>
1435
  """)
1436
 
 
1440
  <p style="font-family:'Bangers',cursive;font-size:1.8rem;letter-spacing:2px">πŸ“„ HWP AI μ–΄μ‹œμŠ€ν„΄νŠΈ πŸ€–</p>
1441
  <p>AIκ°€ HWP νŒŒμΌμ„ 읽고, 보고, λ§ν•˜λ©°, μƒκ°ν•˜κ³  κΈ°μ–΅ν•©λ‹ˆλ‹€!</p>
1442
  <p>πŸ“– READ β€’ πŸ‘οΈ SEE β€’ πŸ’¬ SPEAK β€’ 🧠 THINK β€’ πŸ’Ύ MEMORY</p>
1443
+ <p style="margin-top:8px;font-size:0.9rem;">πŸ†“ 무료 μ„œλΉ„μŠ€ (일뢀 κΈ°λŠ₯ μ œν•œ) | πŸ“§ arxivgpt@gmail.com</p>
1444
  <p style="margin-top:10px"><a href="https://www.humangen.ai" target="_blank" style="color:#FACC15;text-decoration:none;font-weight:bold;">🏠 www.humangen.ai</a></p>
1445
  </div>
1446
  """)
1447
 
1448
  # ============== 이벀트 ν•Έλ“€λŸ¬ ==============
1449
+ def on_submit(msg, hist, f, sid):
1450
  if hist is None:
1451
  hist = []
1452
+ for r in chat_response(msg, hist, f, sid):
1453
  yield r[0], r[1], "", None
1454
 
1455
+ submit_btn.click(on_submit, [msg_input, chatbot, file_upload, session_state],
1456
  [chatbot, session_state, msg_input, file_upload])
1457
+ msg_input.submit(on_submit, [msg_input, chatbot, file_upload, session_state],
1458
  [chatbot, session_state, msg_input, file_upload])
1459
 
1460
  new_btn.click(lambda: ([], create_session(), None, ""), outputs=[chatbot, session_state, file_upload, msg_input])