openfree commited on
Commit
8c93ab4
Β·
verified Β·
1 Parent(s): ab3556d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +100 -65
app.py CHANGED
@@ -563,10 +563,11 @@ class NovelWritingSystem:
563
  return full_content
564
 
565
  def call_llm_streaming(self, messages: List[Dict[str, str]], role: str, language: str) -> Generator[str, None, None]:
566
- """LLM 슀트리밍 호좜 (μ•ˆμ „ν•œ μ—λŸ¬ 처리)"""
567
  try:
568
  system_prompts = self.get_system_prompts(language)
569
  full_messages = [{"role": "system", "content": system_prompts.get(role, "You are a helpful assistant.")}, *messages]
 
570
  payload = {
571
  "model": self.model_id,
572
  "messages": full_messages,
@@ -579,80 +580,114 @@ class NovelWritingSystem:
579
  "stream_options": {"include_usage": True}
580
  }
581
 
582
- logger.info(f"API 호좜 μ‹œμž‘ - μ—­ν• : {role}")
583
- response = requests.post(self.api_url, headers=self.create_headers(), json=payload, stream=True, timeout=180)
584
- response.raise_for_status()
 
 
 
 
 
 
 
585
 
 
 
 
 
 
 
 
 
 
 
586
  buffer = ""
 
587
  chunk_count = 0
588
-
 
589
  for line in response.iter_lines():
590
- if line:
591
- line_str = line.decode('utf-8')
592
- if line_str.startswith("data: "):
593
- data = line_str[6:]
594
- if data == "[DONE]":
595
- logger.info(f"슀트리밍 μ™„λ£Œ - 총 {chunk_count}개 청크 처리")
596
- break
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
597
 
598
- try:
599
- chunk = json.loads(data)
600
-
601
- # 디버깅을 μœ„ν•΄ 첫 λͺ‡ 개 청크 λ‘œκΉ…
602
- if chunk_count < 3:
603
- logger.debug(f"청크 {chunk_count}: {json.dumps(chunk, ensure_ascii=False)[:200]}")
604
-
605
- # μ•ˆμ „ν•œ content μΆ”μΆœ
606
- choices = chunk.get("choices", [])
607
- if choices and len(choices) > 0:
608
- delta = choices[0].get("delta", {})
609
- content = delta.get("content", "")
610
-
611
- if content:
612
- buffer += content
613
- chunk_count += 1
614
-
615
- # 버퍼가 μΆ©λΆ„νžˆ μ°¨κ±°λ‚˜ μ€„λ°”κΏˆμ΄ μžˆμ„ λ•Œ yield
616
- if len(buffer) > 100 or "\n" in buffer:
617
- yield buffer
618
- buffer = ""
619
-
620
- # μ—λŸ¬ λ©”μ‹œμ§€ 체크
621
- elif chunk.get("error"):
622
- error_msg = chunk.get("error", {}).get("message", "Unknown error")
623
- logger.error(f"API μ—λŸ¬ 응닡: {error_msg}")
624
- yield f"❌ API 였λ₯˜: {error_msg}"
625
- return
626
-
627
- except json.JSONDecodeError as e:
628
- logger.warning(f"JSON νŒŒμ‹± 였λ₯˜: {e}, 데이터: {data[:100]}")
629
- continue
630
- except Exception as e:
631
- logger.error(f"청크 처리 쀑 μ˜ˆμ™Έ: {e}")
632
- continue
633
 
634
- # 남은 버퍼 λ‚΄μš© 좜λ ₯
635
- if buffer:
636
  yield buffer
637
-
638
- # 청크가 ν•˜λ‚˜λ„ μ²˜λ¦¬λ˜μ§€ μ•Šμ•˜λ‹€λ©΄
639
  if chunk_count == 0:
640
- logger.error("μ‘λ‹΅μ—μ„œ μ½˜ν…μΈ λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.")
641
- yield "❌ API 응닡에 μ½˜ν…μΈ κ°€ μ—†μŠ΅λ‹ˆλ‹€."
 
 
642
 
643
- except requests.exceptions.HTTPError as e:
644
- logger.error(f"HTTP 였λ₯˜: {e}")
645
- logger.error(f"응닡 μƒνƒœ μ½”λ“œ: {e.response.status_code if e.response else 'N/A'}")
646
- logger.error(f"응닡 λ‚΄μš©: {e.response.text[:500] if e.response else 'N/A'}")
647
- yield f"❌ HTTP 였λ₯˜: {e}"
648
- except requests.exceptions.RequestException as e:
649
- logger.error(f"API μš”μ²­ 였λ₯˜: {e}")
650
- yield f"❌ API 였λ₯˜: {e}"
651
  except Exception as e:
652
- logger.error(f"예기치 μ•Šμ€ 였λ₯˜: {e}", exc_info=True)
653
- yield f"❌ 처리 였λ₯˜: {e}"
654
-
655
-
 
656
 
657
 
658
  def get_system_prompts(self, language: str) -> Dict[str, str]:
 
563
  return full_content
564
 
565
  def call_llm_streaming(self, messages: List[Dict[str, str]], role: str, language: str) -> Generator[str, None, None]:
566
+ """LLM 슀트리밍 호좜 (μ™„μ „ν•œ μ—λŸ¬ 처리 및 디버깅)"""
567
  try:
568
  system_prompts = self.get_system_prompts(language)
569
  full_messages = [{"role": "system", "content": system_prompts.get(role, "You are a helpful assistant.")}, *messages]
570
+
571
  payload = {
572
  "model": self.model_id,
573
  "messages": full_messages,
 
580
  "stream_options": {"include_usage": True}
581
  }
582
 
583
+ logger.info(f"[{role}] API 슀트리밍 μ‹œμž‘")
584
+
585
+ # API 호좜
586
+ response = requests.post(
587
+ self.api_url,
588
+ headers=self.create_headers(),
589
+ json=payload,
590
+ stream=True,
591
+ timeout=180
592
+ )
593
 
594
+ # μƒνƒœ μ½”λ“œ 확인
595
+ if response.status_code != 200:
596
+ logger.error(f"API 응닡 였λ₯˜: {response.status_code}")
597
+ logger.error(f"응닡 λ‚΄μš©: {response.text[:500]}")
598
+ yield f"❌ API 였λ₯˜ (μƒνƒœ μ½”λ“œ: {response.status_code})"
599
+ return
600
+
601
+ response.raise_for_status()
602
+
603
+ # 슀트리밍 처리
604
  buffer = ""
605
+ total_content = ""
606
  chunk_count = 0
607
+ error_count = 0
608
+
609
  for line in response.iter_lines():
610
+ if not line:
611
+ continue
612
+
613
+ try:
614
+ line_str = line.decode('utf-8').strip()
615
+
616
+ # SSE ν˜•μ‹ 확인
617
+ if not line_str.startswith("data: "):
618
+ continue
619
+
620
+ data_str = line_str[6:] # "data: " 제거
621
+
622
+ # 슀트림 μ’…λ£Œ 확인
623
+ if data_str == "[DONE]":
624
+ logger.info(f"[{role}] 슀트리밍 μ™„λ£Œ - 총 {len(total_content)} 문자")
625
+ break
626
+
627
+ # JSON νŒŒμ‹±
628
+ try:
629
+ data = json.loads(data_str)
630
+ except json.JSONDecodeError:
631
+ logger.warning(f"JSON νŒŒμ‹± μ‹€νŒ¨: {data_str[:100]}")
632
+ continue
633
+
634
+ # choices λ°°μ—΄ μ•ˆμ „ν•˜κ²Œ 확인
635
+ choices = data.get("choices", None)
636
+ if not choices or not isinstance(choices, list) or len(choices) == 0:
637
+ # μ—λŸ¬ 응닡 확인
638
+ if "error" in data:
639
+ error_msg = data.get("error", {}).get("message", "Unknown error")
640
+ logger.error(f"API μ—λŸ¬: {error_msg}")
641
+ yield f"❌ API μ—λŸ¬: {error_msg}"
642
+ return
643
+ continue
644
+
645
+ # deltaμ—μ„œ content μΆ”μΆœ
646
+ delta = choices[0].get("delta", {})
647
+ content = delta.get("content", "")
648
+
649
+ if content:
650
+ buffer += content
651
+ total_content += content
652
+ chunk_count += 1
653
 
654
+ # 100자 λ˜λŠ” μ€„λ°”κΏˆλ§ˆλ‹€ yield
655
+ if len(buffer) >= 100 or '\n' in buffer:
656
+ yield buffer
657
+ buffer = ""
658
+ time.sleep(0.01) # UI μ—…λ°μ΄νŠΈλ₯Ό μœ„ν•œ 짧은 λŒ€κΈ°
659
+
660
+ except Exception as e:
661
+ error_count += 1
662
+ logger.error(f"청크 처리 였λ₯˜ #{error_count}: {str(e)}")
663
+ if error_count > 10: # λ„ˆλ¬΄ λ§Žμ€ μ—λŸ¬μ‹œ 쀑단
664
+ yield f"❌ 슀트리밍 쀑 κ³Όλ„ν•œ 였λ₯˜ λ°œμƒ"
665
+ return
666
+ continue
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
667
 
668
+ # 남은 버퍼 처리
669
+ if buffer:
670
  yield buffer
671
+
672
+ # κ²°κ³Ό 확인
673
  if chunk_count == 0:
674
+ logger.error(f"[{role}] μ½˜ν…μΈ κ°€ μ „ν˜€ μˆ˜μ‹ λ˜μ§€ μ•ŠμŒ")
675
+ yield "❌ APIλ‘œλΆ€ν„° 응닡을 λ°›μ§€ λͺ»ν–ˆμŠ΅λ‹ˆλ‹€."
676
+ else:
677
+ logger.info(f"[{role}] μ„±κ³΅μ μœΌλ‘œ {chunk_count}개 청크, 총 {len(total_content)}자 μˆ˜μ‹ ")
678
 
679
+ except requests.exceptions.Timeout:
680
+ logger.error("API μš”μ²­ μ‹œκ°„ 초과")
681
+ yield "❌ API μš”μ²­ μ‹œκ°„μ΄ μ΄ˆκ³Όλ˜μ—ˆμŠ΅λ‹ˆλ‹€."
682
+ except requests.exceptions.ConnectionError:
683
+ logger.error("API μ—°κ²° μ‹€νŒ¨")
684
+ yield "❌ API μ„œλ²„μ— μ—°κ²°ν•  수 μ—†μŠ΅λ‹ˆλ‹€."
 
 
685
  except Exception as e:
686
+ logger.error(f"예기치 μ•Šμ€ 였λ₯˜: {type(e).__name__}: {str(e)}", exc_info=True)
687
+ yield f"❌ 였λ₯˜ λ°œμƒ: {str(e)}"
688
+
689
+
690
+
691
 
692
 
693
  def get_system_prompts(self, language: str) -> Dict[str, str]: