openfree commited on
Commit
f87c30a
ยท
verified ยท
1 Parent(s): 3bd0feb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +65 -72
app.py CHANGED
@@ -81,7 +81,7 @@ BRAVE_ENDPOINT = "https://api.search.brave.com/res/v1/web/search"
81
 
82
  @dataclass
83
  class ConversationConfig:
84
- max_words: int = 6000
85
  prefix_url: str = "https://r.jina.ai/"
86
  api_model_name: str = "meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo"
87
  legacy_local_model_name: str = "NousResearch/Hermes-2-Pro-Llama-3-8B"
@@ -89,8 +89,8 @@ class ConversationConfig:
89
  local_model_name: str = "Private-BitSix-Mistral-Small-3.1-24B-Instruct-2503.gguf"
90
  local_model_repo: str = "ginigen/Private-BitSix-Mistral-Small-3.1-24B-Instruct-2503"
91
  # ํ† ํฐ ์ˆ˜ ์ฆ๊ฐ€
92
- max_tokens: int = 6000 # 2048์—์„œ 6000์œผ๋กœ ์ฆ๊ฐ€
93
- max_new_tokens: int = 8000 # 4000์—์„œ 8000์œผ๋กœ ์ฆ๊ฐ€
94
 
95
 
96
  def brave_search(query: str, count: int = 8, freshness_days: int | None = None):
@@ -120,41 +120,42 @@ def brave_search(query: str, count: int = 8, freshness_days: int | None = None):
120
  return []
121
 
122
  def format_search_results(query: str) -> str:
123
- """๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๋ฅผ ํฌ๋งทํŒ…ํ•˜์—ฌ ๋ฐ˜ํ™˜"""
124
- rows = brave_search(query, 6, freshness_days=3)
125
  if not rows:
126
- return f'# [Web-Search] No live results for "{query}".\n'
127
- hdr = f'# [Web-Search] Top results for "{query}" (last 3 days)\n\n'
128
- body = "\n".join(
129
- f"- **{r['title']}** ({r['host']})\n {r['snippet']}\n [link]({r['url']})"
130
- for r in rows
131
- )
132
- return hdr + body + "\n"
133
-
 
 
134
 
135
  def extract_keywords_for_search(text: str, language: str = "English") -> List[str]:
136
- """ํ…์ŠคํŠธ์—์„œ ๊ฒ€์ƒ‰ํ•  ํ‚ค์›Œ๋“œ ์ถ”์ถœ"""
137
- # ๊ฐ„๋‹จํ•œ ํ‚ค์›Œ๋“œ ์ถ”์ถœ (์‹ค์ œ๋กœ๋Š” ๋” ์ •๊ตํ•œ ๋ฐฉ๋ฒ• ์‚ฌ์šฉ ๊ฐ€๋Šฅ)
138
- lines = text.split('\n')[:5] # ์ฒซ 5์ค„์—์„œ ํ‚ค์›Œ๋“œ ์ถ”์ถœ
139
- text_sample = ' '.join(lines)
140
 
141
- # ์–ธ์–ด๋ณ„ ์ค‘์š” ํ‚ค์›Œ๋“œ ํŒจํ„ด
142
  if language == "Korean":
143
- # ํ•œ๊ตญ์–ด ํ‚ค์›Œ๋“œ ํŒจํ„ด (๋ช…์‚ฌํ˜• ๋‹จ์–ด๋“ค)
144
  import re
 
145
  keywords = re.findall(r'[๊ฐ€-ํžฃ]{2,}', text_sample)
146
- # ์ค‘๋ณต ์ œ๊ฑฐ ๋ฐ ์ƒ์œ„ 3๊ฐœ ์„ ํƒ
147
- unique_keywords = list(dict.fromkeys(keywords))[:3]
 
 
 
148
  else:
149
- # ์˜์–ด ํ‚ค์›Œ๋“œ ํŒจํ„ด
150
  words = text_sample.split()
151
- # ๊ธธ์ด 3 ์ด์ƒ, ๋Œ€๋ฌธ์ž๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋‹จ์–ด๋“ค ์šฐ์„ 
152
  keywords = [word.strip('.,!?;:') for word in words
153
- if len(word) > 3 and (word[0].isupper() or word.isupper())]
154
- unique_keywords = list(dict.fromkeys(keywords))[:3]
155
-
156
- return unique_keywords
157
-
158
 
159
  class UnifiedAudioConverter:
160
  def __init__(self, config: ConversationConfig):
@@ -199,7 +200,7 @@ class UnifiedAudioConverter:
199
  flash_attn=True,
200
  n_gpu_layers=81 if torch.cuda.is_available() else 0,
201
  n_batch=1024,
202
- n_ctx=8192,
203
  )
204
  self.local_llm_model = self.config.local_model_name
205
  print(f"Local LLM initialized: {model_path_local}")
@@ -307,10 +308,15 @@ class UnifiedAudioConverter:
307
  else:
308
  return MessagesFormatterType.LLAMA_3
309
 
 
310
  def _build_prompt(self, text: str, language: str = "English", search_context: str = "") -> str:
311
  """Build prompt for conversation generation with search context"""
 
 
 
 
 
312
  if language == "Korean":
313
- # ๊ฐ•ํ™”๋œ ํ•œ๊ตญ์–ด ํ”„๋กฌํ”„ํŠธ (์กด๋Œ“๋ง ๊ฐ•ํ™” ๋ฐ ํ•œ๊ตญ์  ํŠน์„ฑ ๋ฐ˜์˜)
314
  template = """
315
  {
316
  "conversation": [
@@ -322,36 +328,22 @@ class UnifiedAudioConverter:
322
  }
323
  """
324
 
 
 
 
 
 
325
  base_prompt = (
326
  f"# ์›๋ณธ ์ฝ˜ํ…์ธ :\n{text}\n\n"
327
- f"# ์ตœ์‹  ๊ด€๋ จ ์ •๋ณด:\n{search_context}\n\n" if search_context else f"# ์›๋ณธ ์ฝ˜ํ…์ธ :\n{text}\n\n"
328
- f"์œ„ ๋‚ด์šฉ์„ ๋ฐ”ํƒ•์œผ๋กœ 30๋Œ€ ํ•œ๊ตญ์ธ ๋‘ ๋ช…์ด ์ง„ํ–‰ํ•˜๋Š” ์ž์—ฐ์Šค๋Ÿฝ๊ณ  ํฅ๋ฏธ๋กœ์šด ํ•œ๊ตญ์–ด ํŒŸ์บ์ŠคํŠธ ๋Œ€ํ™”๋ฅผ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”.\n\n"
329
- f"## ํ•„์ˆ˜ ์ง€์นจ:\n\n"
330
- f"### ๐Ÿ‘ฅ ์บ๋ฆญํ„ฐ ์„ค์ •:\n"
331
- f"- **์ค€์ˆ˜(์ง„ํ–‰์ž)**: ์นœ๊ทผํ•˜๊ณ  ํ˜ธ๊ธฐ์‹ฌ ๋งŽ์€ ์„ฑ๊ฒฉ, ์ฒญ์ทจ์ž์˜ ๊ถ๊ธˆ์ฆ์„ ๋Œ€๋ณ€ํ•˜๋Š” 30๋Œ€ ๋‚จ์„ฑ\n"
332
- f"- **๋ฏผํ˜ธ(์ „๋ฌธ๊ฐ€)**: ํ•ด๋‹น ์ฃผ์ œ์— ๋Œ€ํ•œ ๊นŠ์€ ์ง€์‹์„ ๊ฐ€์ง„ ์ „๋ฌธ๊ฐ€, ์‰ฝ๊ฒŒ ์„ค๋ช…ํ•˜๋Š” ๋Šฅ๋ ฅ์„ ๊ฐ€์ง„ 30๋Œ€ ๋‚จ์„ฑ\n\n"
333
- f"### ๐Ÿ—ฃ๏ธ ์–ธ์–ด ์Šคํƒ€์ผ (์ค‘์š”!):\n"
334
- f"- **์กด๋Œ“๋ง ํ•„์ˆ˜**: ๋‘ ํ™”์ž๋Š” ์„œ๋กœ์—๊ฒŒ ์ตœ์†Œํ•œ์˜ ์กด๋Œ“๋ง์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค ('~์Šต๋‹ˆ๋‹ค', '~์„ธ์š”', '~๊ฑฐ๋“ ์š”')\n"
335
- f"- **๋ฐ˜๋ง ์ ˆ๋Œ€ ๊ธˆ์ง€**: '~์•ผ', '~๋‹ค', '~ํ•ด' ๋“ฑ์˜ ๋ฐ˜๋ง์€ ์ ˆ๋Œ€ ์‚ฌ์šฉํ•˜์ง€ ๋งˆ์„ธ์š”\n"
336
- f"- **์ž์—ฐ์Šค๋Ÿฌ์šด ์กด๋Œ“๋ง**: ๋”ฑ๋”ฑํ•˜์ง€ ์•Š๊ณ  ์นœ๊ทผํ•œ ์กด๋Œ“๋ง ์‚ฌ์šฉ ('๊ทธ๋ ‡๊ตฐ์š”', '๋งž์œผ์„ธ์š”', '๊ทธ๋Ÿฐ๋ฐ์š”')\n"
337
- f"- **๊ฐํƒ„์‚ฌ ํ™œ์šฉ**: '์•„~', '๊ทธ๋ ‡๊ตฌ๋‚˜์š”', '์™€~', '์ง„์งœ์š”?', '์–ด๋จธ๋‚˜' ๋“ฑ ์ž์—ฐ์Šค๋Ÿฌ์šด ๋ฐ˜์‘\n\n"
338
- f"### ๐Ÿ“ ๋Œ€ํ™” ๊ตฌ์„ฑ:\n"
339
- f"1. **ํ•œ๊ตญ ๋ฌธํ™” ๋งž์ถค**: ํ•œ๊ตญ์ธ์˜ ์ •์„œ์™€ ์ผ์ƒ์— ๋งž๋Š” ๊ตฌ์ฒด์  ์˜ˆ์‹œ์™€ ๋น„์œ  ์‚ฌ์šฉ\n"
340
- f"2. **๊ณต๊ฐ๋Œ€ ํ˜•์„ฑ**: '์šฐ๋ฆฌ๋‚˜๋ผ์—์„œ๋Š”', 'ํ•œ๊ตญ ์‚ฌ๋žŒ๋“ค์ด', '์š”์ฆ˜ ์‚ฌ๋žŒ๋“ค' ๋“ฑ์˜ ํ‘œํ˜„์œผ๋กœ ์นœ๋ฐ€๊ฐ ์กฐ์„ฑ\n"
341
- f"3. **์ถฉ๋ถ„ํ•œ ๋ถ„๋Ÿ‰**: ๊ฐ ๋Œ€ํ™”๋Š” ์ตœ์†Œ 3-4๋ฌธ์žฅ ์ด์ƒ, ์ „์ฒด 10ํšŒ ์ด์ƒ ์ฃผ๊ณ ๋ฐ›๊ธฐ\n"
342
- f"4. **์‹ค์šฉ์  ์กฐ์–ธ**: ์ฒญ์ทจ์ž๊ฐ€ ์‹ค์ œ๋กœ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ตฌ์ฒด์ ์ด๊ณ  ์œ ์šฉํ•œ ์ •๋ณด ์ œ๊ณต\n"
343
- f"5. **์ตœ์‹  ์ •๋ณด ๋ฐ˜์˜**: ์ œ๊ณต๋œ ์ตœ์‹  ๊ด€๋ จ ์ •๋ณด๋ฅผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋Œ€ํ™”์— ํฌํ•จ\n\n"
344
- f"### ๐ŸŽฏ ํŒŸ์บ์ŠคํŠธ ํ’ˆ์งˆ:\n"
345
- f"- **์˜คํ”„๋‹**: ๋”ฐ๋œปํ•œ ์ธ์‚ฌ์™€ ์ฃผ์ œ ์†Œ๊ฐœ\n"
346
- f"- **๋ฉ”์ธ**: ํ•ต์‹ฌ ๋‚ด์šฉ์„ ์žฌ๋ฏธ์žˆ๊ณ  ์ดํ•ดํ•˜๊ธฐ ์‰ฝ๊ฒŒ ์ „๋‹ฌ\n"
347
- f"- **์ƒํ˜ธ์ž‘์šฉ**: '์ฒญ์ทจ์ž ์—ฌ๋Ÿฌ๋ถ„์€ ์–ด๋–ป๊ฒŒ ์ƒ๊ฐํ•˜์„ธ์š”?' ๊ฐ™์€ ์ฐธ์—ฌ ์œ ๋„\n"
348
- f"- **ํด๋กœ์ง•**: ํ•ต์‹ฌ ์š”์•ฝ๊ณผ ์‹ค์šฉ์  ์กฐ์–ธ์œผ๋กœ ๋งˆ๋ฌด๋ฆฌ\n\n"
349
- f"### ๐Ÿ’ก ํ•œ๊ตญ์–ด ํŠนํ™” ์š”์†Œ:\n"
350
- f"- **ํ˜ธ์นญ**: '์ค€์ˆ˜์”จ', '๋ฏผํ˜ธ์”จ' ๋“ฑ ์ ์ ˆํ•œ ํ˜ธ์นญ ์‚ฌ์šฉ\n"
351
- f"- **๊ด€์šฉ์–ด๊ตฌ**: ์ž์—ฐ์Šค๋Ÿฌ์šด ํ•œ๊ตญ์–ด ๊ด€์šฉํ‘œํ˜„ ํ™œ์šฉ\n"
352
- f"- **์ •์„œ์  ์—ฐ๊ฒฐ**: ํ•œ๊ตญ์ธ์˜ '์ •', '๋ˆˆ์น˜', '์ฒด๋ฉด' ๋“ฑ์˜ ๋ฌธํ™”์  ์ฝ”๋“œ ๋ฐ˜์˜\n"
353
- f"- **๊ณ„์ ˆ๊ฐ**: ํ˜„์žฌ ๊ณ„์ ˆ์ด๋‚˜ ์‹œ๊ธฐ์  ํŠน์„ฑ ๋ฐ˜์˜\n\n"
354
- f"๋‹ค์Œ JSON ํ˜•์‹์œผ๋กœ๋งŒ ๋ฐ˜ํ™˜ํ•˜์„ธ์š”:\n{template}"
355
  )
356
 
357
  return base_prompt
@@ -368,26 +360,27 @@ class UnifiedAudioConverter:
368
  }
369
  """
370
 
 
 
 
 
371
  base_prompt = (
372
- f"# Original Content:\n{text}\n\n"
373
- f"# Latest Related Information:\n{search_context}\n\n" if search_context else f"# Original Content:\n{text}\n\n"
374
- f"Convert the provided text into an engaging, natural podcast conversation between two experts.\n\n"
375
  f"Guidelines:\n"
376
- f"1. Alex (Host): Curious, engaging personality representing audience questions\n"
377
- f"2. Jordan (Expert): Knowledgeable but approachable, explains complex topics simply\n"
378
- f"3. Use natural conversational English with appropriate reactions ('Wow', 'That's interesting', 'Really?')\n"
379
- f"4. Include concrete examples and relatable analogies\n"
380
- f"5. Each response should be substantial (minimum 3-4 sentences)\n"
381
- f"6. Create at least 10 back-and-forth exchanges\n"
382
- f"7. Address common questions and misconceptions\n"
383
- f"8. Maintain an informative yet entertaining tone\n"
384
- f"9. Incorporate the latest related information naturally into the conversation\n"
385
- f"10. End with key takeaways and practical advice\n\n"
386
- f"Return ONLY the JSON in this format:\n{template}"
387
  )
388
 
389
  return base_prompt
390
 
 
 
391
  def _build_messages_for_local(self, text: str, language: str = "English", search_context: str = "") -> List[Dict]:
392
  """Build messages for local LLM with enhanced Korean guidelines"""
393
  if language == "Korean":
 
81
 
82
  @dataclass
83
  class ConversationConfig:
84
+ max_words: int = 4000
85
  prefix_url: str = "https://r.jina.ai/"
86
  api_model_name: str = "meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo"
87
  legacy_local_model_name: str = "NousResearch/Hermes-2-Pro-Llama-3-8B"
 
89
  local_model_name: str = "Private-BitSix-Mistral-Small-3.1-24B-Instruct-2503.gguf"
90
  local_model_repo: str = "ginigen/Private-BitSix-Mistral-Small-3.1-24B-Instruct-2503"
91
  # ํ† ํฐ ์ˆ˜ ์ฆ๊ฐ€
92
+ max_tokens: int = 3000 # 2048์—์„œ 6000์œผ๋กœ ์ฆ๊ฐ€
93
+ max_new_tokens: int = 6000 # 4000์—์„œ 8000์œผ๋กœ ์ฆ๊ฐ€
94
 
95
 
96
  def brave_search(query: str, count: int = 8, freshness_days: int | None = None):
 
120
  return []
121
 
122
  def format_search_results(query: str) -> str:
123
+ """๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๋ฅผ ํฌ๋งทํŒ…ํ•˜์—ฌ ๋ฐ˜ํ™˜ (๊ฐ„๋žตํ•˜๊ฒŒ)"""
124
+ rows = brave_search(query, 3, freshness_days=3) # 6๊ฐœ์—์„œ 3๊ฐœ๋กœ ์ค„์ž„
125
  if not rows:
126
+ return "" # ๋นˆ ๋ฌธ์ž์—ด ๋ฐ˜ํ™˜
127
+
128
+ # ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๋ฅผ ๋” ๊ฐ„๋žตํ•˜๊ฒŒ ์š”์•ฝ
129
+ results = []
130
+ for r in rows[:2]: # ์ตœ๋Œ€ 2๊ฐœ๋งŒ ์‚ฌ์šฉ
131
+ # ์Šค๋‹ˆํŽซ์„ 100์ž๋กœ ์ œํ•œ
132
+ snippet = r['snippet'][:100] + "..." if len(r['snippet']) > 100 else r['snippet']
133
+ results.append(f"- {r['title']}: {snippet}")
134
+
135
+ return "\n".join(results) + "\n"
136
 
137
  def extract_keywords_for_search(text: str, language: str = "English") -> List[str]:
138
+ """ํ…์ŠคํŠธ์—์„œ ๊ฒ€์ƒ‰ํ•  ํ‚ค์›Œ๋“œ ์ถ”์ถœ (๊ฐœ์„ )"""
139
+ # ํ…์ŠคํŠธ ์•ž๋ถ€๋ถ„๋งŒ ์‚ฌ์šฉ (๋„ˆ๋ฌด ๋งŽ์€ ํ…์ŠคํŠธ ์ฒ˜๋ฆฌ ๋ฐฉ์ง€)
140
+ text_sample = text[:500]
 
141
 
 
142
  if language == "Korean":
 
143
  import re
144
+ # ํ•œ๊ตญ์–ด ๋ช…์‚ฌ ์ถ”์ถœ (2๊ธ€์ž ์ด์ƒ)
145
  keywords = re.findall(r'[๊ฐ€-ํžฃ]{2,}', text_sample)
146
+ # ์ค‘๋ณต ์ œ๊ฑฐํ•˜๊ณ  ๊ฐ€์žฅ ๊ธด ๋‹จ์–ด 1๊ฐœ๋งŒ ์„ ํƒ
147
+ unique_keywords = list(dict.fromkeys(keywords))
148
+ # ๊ธธ์ด ์ˆœ์œผ๋กœ ์ •๋ ฌํ•˜๊ณ  ๊ฐ€์žฅ ์˜๋ฏธ์žˆ์„ ๊ฒƒ ๊ฐ™์€ ๋‹จ์–ด ์„ ํƒ
149
+ unique_keywords.sort(key=len, reverse=True)
150
+ return unique_keywords[:1] # 1๊ฐœ๋งŒ ๋ฐ˜ํ™˜
151
  else:
152
+ # ์˜์–ด๋Š” ๋Œ€๋ฌธ์ž๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋‹จ์–ด ์ค‘ ๊ฐ€์žฅ ๊ธด ๊ฒƒ 1๊ฐœ
153
  words = text_sample.split()
 
154
  keywords = [word.strip('.,!?;:') for word in words
155
+ if len(word) > 4 and word[0].isupper()]
156
+ if keywords:
157
+ return [max(keywords, key=len)] # ๊ฐ€์žฅ ๊ธด ๋‹จ์–ด 1๊ฐœ
158
+ return []
 
159
 
160
  class UnifiedAudioConverter:
161
  def __init__(self, config: ConversationConfig):
 
200
  flash_attn=True,
201
  n_gpu_layers=81 if torch.cuda.is_available() else 0,
202
  n_batch=1024,
203
+ n_ctx=16384,
204
  )
205
  self.local_llm_model = self.config.local_model_name
206
  print(f"Local LLM initialized: {model_path_local}")
 
308
  else:
309
  return MessagesFormatterType.LLAMA_3
310
 
311
+
312
  def _build_prompt(self, text: str, language: str = "English", search_context: str = "") -> str:
313
  """Build prompt for conversation generation with search context"""
314
+ # ํ…์ŠคํŠธ ๊ธธ์ด ์ œํ•œ์„ ๋” ๊ฐ•ํ•˜๊ฒŒ ์ ์šฉ
315
+ max_text_length = 3000 if search_context else 4000
316
+ if len(text) > max_text_length:
317
+ text = text[:max_text_length] + "..."
318
+
319
  if language == "Korean":
 
320
  template = """
321
  {
322
  "conversation": [
 
328
  }
329
  """
330
 
331
+ # ๊ฒ€์ƒ‰ ์ปจํ…์ŠคํŠธ๊ฐ€ ์žˆ์„ ๋•Œ๋งŒ ํฌํ•จ
332
+ context_part = ""
333
+ if search_context:
334
+ context_part = f"# ์ตœ์‹  ๊ด€๋ จ ์ •๋ณด:\n{search_context}\n"
335
+
336
  base_prompt = (
337
  f"# ์›๋ณธ ์ฝ˜ํ…์ธ :\n{text}\n\n"
338
+ f"{context_part}"
339
+ f"์œ„ ๋‚ด์šฉ์œผ๋กœ ํ•œ๊ตญ์–ด ํŒŸ์บ์ŠคํŠธ ๋Œ€ํ™”๋ฅผ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”.\n\n"
340
+ f"## ํ•ต์‹ฌ ์ง€์นจ:\n"
341
+ f"- ์ค€์ˆ˜(์ง„ํ–‰์ž)์™€ ๋ฏผํ˜ธ(์ „๋ฌธ๊ฐ€) ๋‘ 30๋Œ€ ๋‚จ์„ฑ์˜ ๋Œ€ํ™”\n"
342
+ f"- ์„œ๋กœ ์กด๋Œ“๋ง ์‚ฌ์šฉ ํ•„์ˆ˜ (๋ฐ˜๋ง ์ ˆ๋Œ€ ๊ธˆ์ง€)\n"
343
+ f"- ์ž์—ฐ์Šค๋Ÿฌ์šด ํ•œ๊ตญ์–ด ํ‘œํ˜„ ์‚ฌ์šฉ\n"
344
+ f"- ๊ฐ ๋Œ€ํ™” 2-3๋ฌธ์žฅ, ์ „์ฒด 8-10ํšŒ ์ฃผ๊ณ ๋ฐ›๊ธฐ\n"
345
+ f"- ์ตœ์‹  ์ •๋ณด๊ฐ€ ์žˆ๋‹ค๋ฉด ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํฌํ•จ\n\n"
346
+ f"JSON ํ˜•์‹์œผ๋กœ๋งŒ ๋ฐ˜ํ™˜:\n{template}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
347
  )
348
 
349
  return base_prompt
 
360
  }
361
  """
362
 
363
+ context_part = ""
364
+ if search_context:
365
+ context_part = f"# Latest Information:\n{search_context}\n"
366
+
367
  base_prompt = (
368
+ f"# Content:\n{text}\n\n"
369
+ f"{context_part}"
370
+ f"Create a podcast conversation.\n\n"
371
  f"Guidelines:\n"
372
+ f"- Alex (Host) and Jordan (Expert)\n"
373
+ f"- Natural conversational English\n"
374
+ f"- Each response 2-3 sentences\n"
375
+ f"- 8-10 exchanges total\n"
376
+ f"- Include latest info if available\n\n"
377
+ f"Return JSON only:\n{template}"
 
 
 
 
 
378
  )
379
 
380
  return base_prompt
381
 
382
+
383
+
384
  def _build_messages_for_local(self, text: str, language: str = "English", search_context: str = "") -> List[Dict]:
385
  """Build messages for local LLM with enhanced Korean guidelines"""
386
  if language == "Korean":