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):
|