Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -1098,10 +1098,9 @@ def generate_images_parallel(prompt_3d: str, prompt_photo: str) -> Tuple[Optiona
|
|
| 1098 |
# PPT Generation Functions - FIXED VERSION
|
| 1099 |
##############################################################################
|
| 1100 |
def parse_llm_ppt_response(response: str, layout_style: str = "consistent") -> list:
|
| 1101 |
-
"""Parse LLM response to extract slide content -
|
| 1102 |
slides = []
|
| 1103 |
|
| 1104 |
-
# Debug: ์ ์ฒด ์๋ต ํ์ธ
|
| 1105 |
logger.info(f"Parsing LLM response, total length: {len(response)}")
|
| 1106 |
logger.debug(f"First 500 chars: {response[:500]}")
|
| 1107 |
|
|
@@ -1114,24 +1113,45 @@ def parse_llm_ppt_response(response: str, layout_style: str = "consistent") -> l
|
|
| 1114 |
except:
|
| 1115 |
pass
|
| 1116 |
|
| 1117 |
-
#
|
| 1118 |
-
#
|
| 1119 |
-
|
| 1120 |
-
|
| 1121 |
-
# ์ฌ๋ผ์ด๋
|
| 1122 |
-
|
| 1123 |
-
|
| 1124 |
-
|
| 1125 |
-
|
| 1126 |
-
|
| 1127 |
-
|
| 1128 |
-
|
| 1129 |
-
|
| 1130 |
-
|
| 1131 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1132 |
continue
|
| 1133 |
|
| 1134 |
-
logger.debug(f"Processing
|
| 1135 |
|
| 1136 |
slide = {
|
| 1137 |
'title': '',
|
|
@@ -1142,9 +1162,9 @@ def parse_llm_ppt_response(response: str, layout_style: str = "consistent") -> l
|
|
| 1142 |
}
|
| 1143 |
|
| 1144 |
# ์น์
๋ด์์ ์ ๋ชฉ, ๋ด์ฉ, ๋
ธํธ ์ถ์ถ
|
| 1145 |
-
lines = section.
|
| 1146 |
current_part = None
|
| 1147 |
-
|
| 1148 |
content_lines = []
|
| 1149 |
notes_lines = []
|
| 1150 |
|
|
@@ -1154,11 +1174,11 @@ def parse_llm_ppt_response(response: str, layout_style: str = "consistent") -> l
|
|
| 1154 |
continue
|
| 1155 |
|
| 1156 |
# ์ ๋ชฉ ์น์
๊ฐ์ง
|
| 1157 |
-
if line.startswith('์ ๋ชฉ:') or line.startswith('Title:'):
|
| 1158 |
current_part = 'title'
|
| 1159 |
title_text = line.split(':', 1)[1].strip() if ':' in line else ''
|
| 1160 |
-
|
| 1161 |
-
|
| 1162 |
# ๋ด์ฉ ์น์
๊ฐ์ง
|
| 1163 |
elif line.startswith('๋ด์ฉ:') or line.startswith('Content:'):
|
| 1164 |
current_part = 'content'
|
|
@@ -1173,57 +1193,91 @@ def parse_llm_ppt_response(response: str, layout_style: str = "consistent") -> l
|
|
| 1173 |
notes_lines.append(notes_text)
|
| 1174 |
# ํ์ฌ ์น์
์ ๋ฐ๋ผ ๋ด์ฉ ์ถ๊ฐ
|
| 1175 |
else:
|
| 1176 |
-
if current_part == 'title' and not
|
| 1177 |
-
|
| 1178 |
elif current_part == 'content':
|
| 1179 |
content_lines.append(line)
|
| 1180 |
elif current_part == 'notes':
|
| 1181 |
notes_lines.append(line)
|
| 1182 |
-
elif not
|
| 1183 |
# ์ฒซ ๋ฒ์งธ ์ค์ ์ ๋ชฉ์ผ๋ก
|
| 1184 |
-
|
| 1185 |
-
|
| 1186 |
-
|
|
|
|
|
|
|
| 1187 |
content_lines.append(line)
|
| 1188 |
|
| 1189 |
# ์ฌ๋ผ์ด๋ ๋ฐ์ดํฐ ์ค์
|
| 1190 |
-
slide['title'] = ' '.join(title_lines).strip()
|
| 1191 |
slide['content'] = '\n'.join(content_lines).strip()
|
| 1192 |
slide['notes'] = ' '.join(notes_lines).strip()
|
| 1193 |
|
| 1194 |
-
# ์ ๋ชฉ ์ ๋ฆฌ
|
| 1195 |
-
slide['title'] = re.sub(r'^(์ฌ๋ผ์ด๋|Slide)\s*\d+\s*[:๏ผ\-]?\s*', '', slide['title'], flags=re.IGNORECASE)
|
| 1196 |
-
slide['title'] = re.sub(r'^(์ ๋ชฉ|Title)\s*[:๏ผ]\s*', '', slide['title'], flags=re.IGNORECASE)
|
| 1197 |
-
|
| 1198 |
# ๋ด์ฉ์ด ์๋ ๊ฒฝ์ฐ์๋ง ์ถ๊ฐ
|
| 1199 |
if slide['title'] or slide['content']:
|
| 1200 |
logger.info(f"Slide {len(slides)+1}: Title='{slide['title'][:30]}...', Content length={len(slide['content'])}")
|
| 1201 |
slides.append(slide)
|
| 1202 |
|
| 1203 |
-
# ๋ง์ฝ ์ ๋ฐฉ๋ฒ์ผ๋ก ํ์ฑ์ด ์ ๋์๋ค๋ฉด, ๋
|
| 1204 |
-
if not slides:
|
| 1205 |
-
logger.warning("Primary parsing
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1206 |
|
| 1207 |
-
# ๋๋ธ ๋ด๋ผ์ธ์ผ๋ก ๊ตฌ๋ถ
|
| 1208 |
-
sections = response.split('\n\n')
|
| 1209 |
for section in sections:
|
| 1210 |
-
|
| 1211 |
-
|
| 1212 |
-
slide = {
|
| 1213 |
-
'title': lines[0].strip(),
|
| 1214 |
-
'content': '\n'.join(lines[1:]).strip(),
|
| 1215 |
-
'notes': '',
|
| 1216 |
-
'layout': 'title_content',
|
| 1217 |
-
'chart_data': None
|
| 1218 |
-
}
|
| 1219 |
|
| 1220 |
-
|
| 1221 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1222 |
|
| 1223 |
-
if
|
| 1224 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1225 |
|
| 1226 |
logger.info(f"Total slides parsed: {len(slides)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1227 |
return slides
|
| 1228 |
|
| 1229 |
def force_font_size(text_frame, font_size_pt: int, theme: Dict):
|
|
|
|
| 1098 |
# PPT Generation Functions - FIXED VERSION
|
| 1099 |
##############################################################################
|
| 1100 |
def parse_llm_ppt_response(response: str, layout_style: str = "consistent") -> list:
|
| 1101 |
+
"""Parse LLM response to extract slide content - FIXED VERSION"""
|
| 1102 |
slides = []
|
| 1103 |
|
|
|
|
| 1104 |
logger.info(f"Parsing LLM response, total length: {len(response)}")
|
| 1105 |
logger.debug(f"First 500 chars: {response[:500]}")
|
| 1106 |
|
|
|
|
| 1113 |
except:
|
| 1114 |
pass
|
| 1115 |
|
| 1116 |
+
# ๋ ์ ํํ ์ฌ๋ผ์ด๋ ๊ตฌ๋ถ ํจํด
|
| 1117 |
+
# "์ฌ๋ผ์ด๋ 1", "์ฌ๋ผ์ด๋ 2" ๋๋ "Slide 1", "Slide 2" ํ์์ ์ฐพ์
|
| 1118 |
+
slide_markers = []
|
| 1119 |
+
|
| 1120 |
+
# ์ฌ๋ผ์ด๋ ๋ง์ปค์ ์์น๋ฅผ ๋จผ์ ์ฐพ์
|
| 1121 |
+
for match in re.finditer(r'^(?:์ฌ๋ผ์ด๋|Slide)\s*(\d+)\s*$', response, re.MULTILINE):
|
| 1122 |
+
slide_markers.append({
|
| 1123 |
+
'index': int(match.group(1)),
|
| 1124 |
+
'start': match.start(),
|
| 1125 |
+
'end': match.end()
|
| 1126 |
+
})
|
| 1127 |
+
|
| 1128 |
+
logger.info(f"Found {len(slide_markers)} slide markers")
|
| 1129 |
+
|
| 1130 |
+
# ์ฌ๋ผ์ด๋ ๋ง์ปค๊ฐ ์์ผ๋ฉด ๋ค๋ฅธ ํจํด ์๋
|
| 1131 |
+
if not slide_markers:
|
| 1132 |
+
# ์ซ์๋ง์ผ๋ก ์์ํ๋ ํจํด๋ ์ฐพ๊ธฐ (์: "1.", "2." ๋ฑ)
|
| 1133 |
+
for match in re.finditer(r'^(\d+)[.)]\s*$', response, re.MULTILINE):
|
| 1134 |
+
slide_markers.append({
|
| 1135 |
+
'index': int(match.group(1)),
|
| 1136 |
+
'start': match.start(),
|
| 1137 |
+
'end': match.end()
|
| 1138 |
+
})
|
| 1139 |
+
|
| 1140 |
+
# ๊ฐ ์ฌ๋ผ์ด๋ ๋ง์ปค ์ฌ์ด์ ๋ด์ฉ์ ์ถ์ถ
|
| 1141 |
+
for i, marker in enumerate(slide_markers):
|
| 1142 |
+
# ํ์ฌ ์ฌ๋ผ์ด๋์ ์์๊ณผ ๋ ์์น
|
| 1143 |
+
start = marker['end']
|
| 1144 |
+
if i < len(slide_markers) - 1:
|
| 1145 |
+
end = slide_markers[i + 1]['start']
|
| 1146 |
+
else:
|
| 1147 |
+
end = len(response)
|
| 1148 |
+
|
| 1149 |
+
section = response[start:end].strip()
|
| 1150 |
+
|
| 1151 |
+
if not section:
|
| 1152 |
continue
|
| 1153 |
|
| 1154 |
+
logger.debug(f"Processing slide {marker['index']}: {section[:100]}...")
|
| 1155 |
|
| 1156 |
slide = {
|
| 1157 |
'title': '',
|
|
|
|
| 1162 |
}
|
| 1163 |
|
| 1164 |
# ์น์
๋ด์์ ์ ๋ชฉ, ๋ด์ฉ, ๋
ธํธ ์ถ์ถ
|
| 1165 |
+
lines = section.split('\n')
|
| 1166 |
current_part = None
|
| 1167 |
+
title_found = False
|
| 1168 |
content_lines = []
|
| 1169 |
notes_lines = []
|
| 1170 |
|
|
|
|
| 1174 |
continue
|
| 1175 |
|
| 1176 |
# ์ ๋ชฉ ์น์
๊ฐ์ง
|
| 1177 |
+
if (line.startswith('์ ๋ชฉ:') or line.startswith('Title:')) and not title_found:
|
| 1178 |
current_part = 'title'
|
| 1179 |
title_text = line.split(':', 1)[1].strip() if ':' in line else ''
|
| 1180 |
+
slide['title'] = title_text
|
| 1181 |
+
title_found = True
|
| 1182 |
# ๋ด์ฉ ์น์
๊ฐ์ง
|
| 1183 |
elif line.startswith('๋ด์ฉ:') or line.startswith('Content:'):
|
| 1184 |
current_part = 'content'
|
|
|
|
| 1193 |
notes_lines.append(notes_text)
|
| 1194 |
# ํ์ฌ ์น์
์ ๋ฐ๋ผ ๋ด์ฉ ์ถ๊ฐ
|
| 1195 |
else:
|
| 1196 |
+
if current_part == 'title' and not slide['title']:
|
| 1197 |
+
slide['title'] = line
|
| 1198 |
elif current_part == 'content':
|
| 1199 |
content_lines.append(line)
|
| 1200 |
elif current_part == 'notes':
|
| 1201 |
notes_lines.append(line)
|
| 1202 |
+
elif not title_found and not slide['title']:
|
| 1203 |
# ์ฒซ ๋ฒ์งธ ์ค์ ์ ๋ชฉ์ผ๋ก
|
| 1204 |
+
slide['title'] = line
|
| 1205 |
+
title_found = True
|
| 1206 |
+
current_part = 'content'
|
| 1207 |
+
elif current_part is None and title_found:
|
| 1208 |
+
current_part = 'content'
|
| 1209 |
content_lines.append(line)
|
| 1210 |
|
| 1211 |
# ์ฌ๋ผ์ด๋ ๋ฐ์ดํฐ ์ค์
|
|
|
|
| 1212 |
slide['content'] = '\n'.join(content_lines).strip()
|
| 1213 |
slide['notes'] = ' '.join(notes_lines).strip()
|
| 1214 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1215 |
# ๋ด์ฉ์ด ์๋ ๊ฒฝ์ฐ์๋ง ์ถ๊ฐ
|
| 1216 |
if slide['title'] or slide['content']:
|
| 1217 |
logger.info(f"Slide {len(slides)+1}: Title='{slide['title'][:30]}...', Content length={len(slide['content'])}")
|
| 1218 |
slides.append(slide)
|
| 1219 |
|
| 1220 |
+
# ๋ง์ฝ ์ ๋ฐฉ๋ฒ์ผ๋ก ํ์ฑ์ด ์ ๋์๋ค๋ฉด, ๋ ์ ์ฐํ ๋ฐฉ๋ฒ ์๋
|
| 1221 |
+
if not slides or len(slides) < 3:
|
| 1222 |
+
logger.warning(f"Primary parsing resulted in only {len(slides)} slides, trying alternative method...")
|
| 1223 |
+
slides = []
|
| 1224 |
+
|
| 1225 |
+
# "์ ๋ชฉ:" ํจํด์ผ๋ก ์ฌ๋ผ์ด๋ ๊ตฌ๋ถ ์๋
|
| 1226 |
+
sections = re.split(r'\n(?=์ ๋ชฉ:|Title:)', response)
|
| 1227 |
|
|
|
|
|
|
|
| 1228 |
for section in sections:
|
| 1229 |
+
if not section.strip():
|
| 1230 |
+
continue
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1231 |
|
| 1232 |
+
slide = {
|
| 1233 |
+
'title': '',
|
| 1234 |
+
'content': '',
|
| 1235 |
+
'notes': '',
|
| 1236 |
+
'layout': 'title_content',
|
| 1237 |
+
'chart_data': None
|
| 1238 |
+
}
|
| 1239 |
+
|
| 1240 |
+
lines = section.strip().split('\n')
|
| 1241 |
+
current_part = None
|
| 1242 |
+
content_lines = []
|
| 1243 |
+
notes_lines = []
|
| 1244 |
+
|
| 1245 |
+
for line in lines:
|
| 1246 |
+
line = line.strip()
|
| 1247 |
+
if not line:
|
| 1248 |
+
continue
|
| 1249 |
|
| 1250 |
+
if line.startswith('์ ๋ชฉ:') or line.startswith('Title:'):
|
| 1251 |
+
slide['title'] = line.split(':', 1)[1].strip() if ':' in line else ''
|
| 1252 |
+
current_part = 'content'
|
| 1253 |
+
elif line.startswith('๋ด์ฉ:') or line.startswith('Content:'):
|
| 1254 |
+
current_part = 'content'
|
| 1255 |
+
elif line.startswith('๋
ธํธ:') or line.startswith('Notes:'):
|
| 1256 |
+
current_part = 'notes'
|
| 1257 |
+
notes_text = line.split(':', 1)[1].strip() if ':' in line else ''
|
| 1258 |
+
if notes_text:
|
| 1259 |
+
notes_lines.append(notes_text)
|
| 1260 |
+
elif current_part == 'content':
|
| 1261 |
+
content_lines.append(line)
|
| 1262 |
+
elif current_part == 'notes':
|
| 1263 |
+
notes_lines.append(line)
|
| 1264 |
+
|
| 1265 |
+
slide['content'] = '\n'.join(content_lines).strip()
|
| 1266 |
+
slide['notes'] = ' '.join(notes_lines).strip()
|
| 1267 |
+
|
| 1268 |
+
# ์ฌ๋ผ์ด๋ ๋ฒํธ ์ ๊ฑฐ
|
| 1269 |
+
slide['title'] = re.sub(r'^(์ฌ๋ผ์ด๋|Slide)\s*\d+\s*[:๏ผ\-]?\s*', '', slide['title'], flags=re.IGNORECASE)
|
| 1270 |
+
|
| 1271 |
+
if slide['title'] or slide['content']:
|
| 1272 |
+
slides.append(slide)
|
| 1273 |
|
| 1274 |
logger.info(f"Total slides parsed: {len(slides)}")
|
| 1275 |
+
|
| 1276 |
+
# ํ์ฑ ๊ฒฐ๊ณผ ๊ฒ์ฆ
|
| 1277 |
+
if len(slides) < 3:
|
| 1278 |
+
logger.error("Parsing resulted in too few slides. Raw response preview:")
|
| 1279 |
+
logger.error(response[:1000])
|
| 1280 |
+
|
| 1281 |
return slides
|
| 1282 |
|
| 1283 |
def force_font_size(text_frame, font_size_pt: int, theme: Dict):
|