Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -34,6 +34,19 @@ except ImportError:
|
|
| 34 |
PPTX_AVAILABLE = False
|
| 35 |
logger.warning("python-pptx ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ค์น๋์ง ์์์ต๋๋ค. pip install python-pptx")
|
| 36 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
##############################################################################
|
| 38 |
# API Configuration
|
| 39 |
##############################################################################
|
|
@@ -94,26 +107,9 @@ def initialize_flux_api():
|
|
| 94 |
return False
|
| 95 |
|
| 96 |
##############################################################################
|
| 97 |
-
# Diagram Generation API Configuration
|
| 98 |
##############################################################################
|
| 99 |
-
|
| 100 |
-
DIAGRAM_API_ENABLED = False
|
| 101 |
-
diagram_api_client = None
|
| 102 |
-
|
| 103 |
-
def initialize_diagram_api():
|
| 104 |
-
"""๋ค์ด์ด๊ทธ๋จ ์์ฑ API ์ด๊ธฐํ"""
|
| 105 |
-
global DIAGRAM_API_ENABLED, diagram_api_client
|
| 106 |
-
|
| 107 |
-
try:
|
| 108 |
-
logger.info("Connecting to Diagram Generation API...")
|
| 109 |
-
diagram_api_client = Client(DIAGRAM_API_URL)
|
| 110 |
-
DIAGRAM_API_ENABLED = True
|
| 111 |
-
logger.info("Diagram API connected successfully")
|
| 112 |
-
return True
|
| 113 |
-
except Exception as e:
|
| 114 |
-
logger.error(f"Failed to connect to Diagram API: {e}")
|
| 115 |
-
DIAGRAM_API_ENABLED = False
|
| 116 |
-
return False
|
| 117 |
|
| 118 |
##############################################################################
|
| 119 |
# Design Themes and Color Schemes
|
|
@@ -354,32 +350,40 @@ def get_emoji_for_content(text: str) -> str:
|
|
| 354 |
return 'โถ๏ธ'
|
| 355 |
|
| 356 |
##############################################################################
|
| 357 |
-
# Diagram Type Detection
|
| 358 |
##############################################################################
|
| 359 |
def detect_diagram_type(title: str, content: str) -> Optional[str]:
|
| 360 |
"""์ฌ๋ผ์ด๋ ๋ด์ฉ์ ๋ถ์ํ์ฌ ์ ์ ํ ๋ค์ด์ด๊ทธ๋จ ํ์
๊ฒฐ์ """
|
| 361 |
combined_text = f"{title} {content}".lower()
|
| 362 |
|
| 363 |
# Process Flow keywords
|
| 364 |
-
if any(word in combined_text for word in ['ํ๋ก์ธ์ค', 'process', '์ ์ฐจ', 'procedure', '๋จ๊ณ', 'step', 'flow', 'ํ๋ฆ', '์ํฌํ๋ก์ฐ', 'workflow']):
|
| 365 |
return "Process Flow"
|
| 366 |
|
| 367 |
# WBS keywords
|
| 368 |
-
elif any(word in combined_text for word in ['wbs', '์์
๋ถํด', 'ํ๋ก์ ํธ', 'project', '์
๋ฌด๋ถํด', 'breakdown', '๊ตฌ์กฐ๋']):
|
| 369 |
return "WBS Diagram"
|
| 370 |
|
| 371 |
# Concept Map keywords
|
| 372 |
-
elif any(word in combined_text for word in ['๊ฐ๋
', 'concept', '๊ด๊ณ', 'relationship', '์ฐ๊ด', 'connection', '๋ง์ธ๋๋งต', 'mindmap']):
|
| 373 |
return "Concept Map"
|
| 374 |
|
| 375 |
# Radial Diagram keywords
|
| 376 |
-
elif any(word in combined_text for word in ['์ค์ฌ', 'central', '๋ฐฉ์ฌํ', 'radial', 'ํต์ฌ', 'core', '์ฃผ์', 'main']):
|
| 377 |
return "Radial Diagram"
|
| 378 |
|
| 379 |
# Synoptic Chart keywords
|
| 380 |
-
elif any(word in combined_text for word in ['๊ฐ์', 'overview', '์ ์ฒด', 'overall', '์์ฝ', 'summary', '์๋ํฑ', 'synoptic']):
|
| 381 |
return "Synoptic Chart"
|
| 382 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 383 |
return None
|
| 384 |
|
| 385 |
##############################################################################
|
|
@@ -390,7 +394,7 @@ def generate_diagram_json(title: str, content: str, diagram_type: str) -> Option
|
|
| 390 |
if not FRIENDLI_TOKEN:
|
| 391 |
return None
|
| 392 |
|
| 393 |
-
# ๋ค์ด์ด๊ทธ๋จ ํ์
๋ณ JSON ๊ตฌ์กฐ ๊ฐ์ด๋
|
| 394 |
json_guides = {
|
| 395 |
"Concept Map": """Generate a JSON for a concept map with the EXACT following structure:
|
| 396 |
{
|
|
@@ -476,7 +480,9 @@ Important rules:
|
|
| 476 |
2. The JSON must follow the EXACT structure shown above
|
| 477 |
3. Create content based on the provided title and content
|
| 478 |
4. Use the user's language (Korean or English) for the content values
|
| 479 |
-
5. Keep it simple with 3-5 main nodes/steps
|
|
|
|
|
|
|
| 480 |
|
| 481 |
messages = [
|
| 482 |
{"role": "system", "content": system_prompt},
|
|
@@ -520,37 +526,40 @@ Important rules:
|
|
| 520 |
return None
|
| 521 |
|
| 522 |
##############################################################################
|
| 523 |
-
# Generate Diagram using
|
| 524 |
##############################################################################
|
| 525 |
-
def
|
| 526 |
-
"""
|
| 527 |
-
if not
|
|
|
|
| 528 |
return None
|
| 529 |
|
| 530 |
try:
|
| 531 |
-
#
|
| 532 |
-
|
| 533 |
-
|
| 534 |
-
|
| 535 |
-
|
| 536 |
-
|
| 537 |
-
|
| 538 |
-
|
| 539 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 540 |
|
| 541 |
-
#
|
| 542 |
-
if isinstance(result,
|
| 543 |
-
|
| 544 |
-
|
| 545 |
-
# ์์ ํ์ผ๋ก ๋ณต์ฌ
|
| 546 |
-
with tempfile.NamedTemporaryFile(delete=False, suffix=".png") as tmp:
|
| 547 |
-
shutil.copy2(image_path, tmp.name)
|
| 548 |
-
return tmp.name
|
| 549 |
|
| 550 |
-
|
|
|
|
| 551 |
|
| 552 |
except Exception as e:
|
| 553 |
-
logger.error(f"Failed to generate diagram
|
| 554 |
return None
|
| 555 |
|
| 556 |
##############################################################################
|
|
@@ -1042,43 +1051,6 @@ def generate_ai_image_via_3d_api(prompt: str) -> Optional[str]:
|
|
| 1042 |
logger.error(f"Failed to generate 3D image: {e}")
|
| 1043 |
return None
|
| 1044 |
|
| 1045 |
-
def generate_flux_image_via_api(prompt: str) -> Optional[str]:
|
| 1046 |
-
"""FLUX API๋ฅผ ํตํด ํฌํ ๋ฆฌ์ผ๋ฆฌ์คํฑ ์ด๋ฏธ์ง ์์ฑ"""
|
| 1047 |
-
if not FLUX_API_ENABLED or not flux_api_client:
|
| 1048 |
-
return None
|
| 1049 |
-
|
| 1050 |
-
try:
|
| 1051 |
-
logger.info(f"Generating FLUX photorealistic image with prompt: {prompt[:100]}...")
|
| 1052 |
-
|
| 1053 |
-
result = flux_api_client.predict(
|
| 1054 |
-
prompt=prompt,
|
| 1055 |
-
width=768,
|
| 1056 |
-
height=768,
|
| 1057 |
-
guidance=3.5,
|
| 1058 |
-
inference_steps=8,
|
| 1059 |
-
seed=random.randint(1, 1000000),
|
| 1060 |
-
do_img2img=False,
|
| 1061 |
-
init_image=None,
|
| 1062 |
-
image2image_strength=0.8,
|
| 1063 |
-
resize_img=True,
|
| 1064 |
-
api_name="/generate_image"
|
| 1065 |
-
)
|
| 1066 |
-
|
| 1067 |
-
if isinstance(result, tuple) and len(result) > 0:
|
| 1068 |
-
image_path = result[0]
|
| 1069 |
-
if image_path and os.path.exists(image_path):
|
| 1070 |
-
with Image.open(image_path) as img:
|
| 1071 |
-
png_tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".png")
|
| 1072 |
-
img.save(png_tmp.name, format="PNG")
|
| 1073 |
-
logger.info(f"FLUX image generated and saved to {png_tmp.name}")
|
| 1074 |
-
return png_tmp.name
|
| 1075 |
-
|
| 1076 |
-
return None
|
| 1077 |
-
|
| 1078 |
-
except Exception as e:
|
| 1079 |
-
logger.error(f"Failed to generate FLUX image: {e}")
|
| 1080 |
-
return None
|
| 1081 |
-
|
| 1082 |
def generate_images_parallel(prompt_3d: str, prompt_photo: str) -> Tuple[Optional[str], Optional[str]]:
|
| 1083 |
"""๋ API๋ฅผ ๋ณ๋ ฌ๋ก ํธ์ถํ์ฌ ์ด๋ฏธ์ง ์์ฑ"""
|
| 1084 |
import concurrent.futures
|
|
@@ -1738,13 +1710,13 @@ def create_advanced_ppt_from_content(
|
|
| 1738 |
# ์ฐ์ธก์ ์๊ฐ์ ์์ ์ถ๊ฐ
|
| 1739 |
visual_added = False
|
| 1740 |
|
| 1741 |
-
if visual_type[0] == 'diagram':
|
| 1742 |
# ๋ค์ด์ด๊ทธ๋จ ์์ฑ
|
| 1743 |
logger.info(f"Generating {visual_type[1]} for slide {i+1}")
|
| 1744 |
diagram_json = generate_diagram_json(slide_title, slide_content, visual_type[1])
|
| 1745 |
|
| 1746 |
if diagram_json:
|
| 1747 |
-
diagram_path =
|
| 1748 |
if diagram_path and os.path.exists(diagram_path):
|
| 1749 |
try:
|
| 1750 |
# ๋ค์ด์ด๊ทธ๋จ ์ด๋ฏธ์ง ์ถ๊ฐ
|
|
@@ -1755,8 +1727,22 @@ def create_advanced_ppt_from_content(
|
|
| 1755 |
)
|
| 1756 |
visual_added = True
|
| 1757 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1758 |
# ์์ ํ์ผ ์ญ์
|
| 1759 |
-
|
|
|
|
|
|
|
|
|
|
| 1760 |
except Exception as e:
|
| 1761 |
logger.error(f"Failed to add diagram: {e}")
|
| 1762 |
|
|
@@ -2264,14 +2250,10 @@ def generate_ppt(
|
|
| 2264 |
include_flux_images = False
|
| 2265 |
yield None, "โ ๏ธ FLUX API ์ฐ๊ฒฐ ์คํจ. ํฌํ ๋ฆฌ์ผ๋ฆฌ์คํฑ ์ด๋ฏธ์ง ์์ด ์งํํฉ๋๋ค.", ""
|
| 2266 |
|
| 2267 |
-
# ๋ค์ด์ด๊ทธ๋จ
|
| 2268 |
-
if include_diagrams and not
|
| 2269 |
-
yield None, "
|
| 2270 |
-
|
| 2271 |
-
yield None, "โ
๋ค์ด์ด๊ทธ๋จ API ์ฐ๊ฒฐ ์ฑ๊ณต!", ""
|
| 2272 |
-
else:
|
| 2273 |
-
include_diagrams = False
|
| 2274 |
-
yield None, "โ ๏ธ ๋ค์ด์ด๊ทธ๋จ API ์ฐ๊ฒฐ ์คํจ. ๋ค์ด์ด๊ทธ๋จ ์์ด ์งํํฉ๋๋ค.", ""
|
| 2275 |
|
| 2276 |
# Process reference files if provided
|
| 2277 |
additional_context = ""
|
|
@@ -2347,7 +2329,7 @@ def generate_ppt(
|
|
| 2347 |
visual_features = []
|
| 2348 |
if include_ai_image and FLUX_API_ENABLED:
|
| 2349 |
visual_features.append("AI 3D ํ์ง ์ด๋ฏธ์ง")
|
| 2350 |
-
if include_diagrams and
|
| 2351 |
visual_features.append("๋ค์ด์ด๊ทธ๋จ")
|
| 2352 |
if include_flux_images and FLUX_API_ENABLED:
|
| 2353 |
visual_features.append("AI ์์ฑ ์ด๋ฏธ์ง")
|
|
@@ -2374,7 +2356,7 @@ def generate_ppt(
|
|
| 2374 |
|
| 2375 |
if include_ai_image and FLUX_API_ENABLED:
|
| 2376 |
success_msg += f"๐ผ๏ธ AI ์์ฑ ํ์ง ์ด๋ฏธ์ง ํฌํจ\n"
|
| 2377 |
-
if include_diagrams and
|
| 2378 |
success_msg += f"๐ AI ์์ฑ ๋ค์ด์ด๊ทธ๋จ ํฌํจ\n"
|
| 2379 |
if include_flux_images and FLUX_API_ENABLED:
|
| 2380 |
success_msg += f"๐จ AI ์์ฑ ์ฌ๋ผ์ด๋ ์ด๋ฏธ์ง ํฌํจ\n"
|
|
@@ -2574,6 +2556,23 @@ input[type="checkbox"] {
|
|
| 2574 |
max-height: 500px;
|
| 2575 |
overflow-y: auto;
|
| 2576 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2577 |
"""
|
| 2578 |
|
| 2579 |
with gr.Blocks(css=css, title="AI PPT Generator Pro") as demo:
|
|
@@ -2582,7 +2581,7 @@ with gr.Blocks(css=css, title="AI PPT Generator Pro") as demo:
|
|
| 2582 |
# ๐ฏ AI ๊ธฐ๋ฐ PPT ์๋ ์์ฑ ์์คํ
Pro
|
| 2583 |
|
| 2584 |
๊ณ ๊ธ ๋์์ธ ํ
๋ง์ ๋ ์ด์์์ ํ์ฉํ ์ ๋ฌธ์ ์ธ ํ๋ ์ ํ
์ด์
์ ์๋์ผ๋ก ์์ฑํฉ๋๋ค.
|
| 2585 |
-
|
| 2586 |
"""
|
| 2587 |
)
|
| 2588 |
|
|
@@ -2678,7 +2677,7 @@ with gr.Blocks(css=css, title="AI PPT Generator Pro") as demo:
|
|
| 2678 |
include_diagrams = gr.Checkbox(
|
| 2679 |
label="๐ AI ๋ค์ด์ด๊ทธ๋จ",
|
| 2680 |
value=False,
|
| 2681 |
-
info="
|
| 2682 |
)
|
| 2683 |
|
| 2684 |
include_flux_images = gr.Checkbox(
|
|
@@ -2733,18 +2732,19 @@ with gr.Blocks(css=css, title="AI PPT Generator Pro") as demo:
|
|
| 2733 |
5. **์ฐธ๊ณ ์๋ฃ ์
๋ก๋**: PDF, CSV, TXT ํ์ผ ์ง์
|
| 2734 |
6. **์์ฑ ๋ฒํผ ํด๋ฆญ**: AI๊ฐ ์๋์ผ๋ก PPT ์์ฑ
|
| 2735 |
|
| 2736 |
-
### ๐จ ์๋ก์ด ๊ธฐ๋ฅ
|
| 2737 |
-
-
|
| 2738 |
-
-
|
| 2739 |
-
- **
|
| 2740 |
-
-
|
| 2741 |
-
-
|
|
|
|
| 2742 |
|
| 2743 |
### ๐ก ๊ณ ๊ธ ํ
|
| 2744 |
-
-
|
| 2745 |
-
- 3D ์คํ์ผ๊ณผ ํฌํ ๋ฆฌ์ผ๋ฆฌ์คํฑ ์ด๋ฏธ์ง๊ฐ
|
| 2746 |
-
-
|
| 2747 |
-
-
|
| 2748 |
"""
|
| 2749 |
)
|
| 2750 |
|
|
@@ -2755,6 +2755,8 @@ with gr.Blocks(css=css, title="AI PPT Generator Pro") as demo:
|
|
| 2755 |
["2024๋
๋์งํธ ๋ง์ผํ
ํธ๋ ๋", 12, True, True, [], "modern", "modern", "consistent", False, True, True, True],
|
| 2756 |
["๊ธฐํ๋ณํ์ ์ง์๊ฐ๋ฅํ ๋ฐ์ ", 15, True, True, [], "nature", "classic", "consistent", False, True, True, True],
|
| 2757 |
["์คํํธ์
์ฌ์
๊ณํ์", 8, False, True, [], "creative", "modern", "varied", False, True, True, True],
|
|
|
|
|
|
|
| 2758 |
],
|
| 2759 |
inputs=[topic_input, num_slides, use_web_search, use_korean, reference_files,
|
| 2760 |
design_theme, font_style, layout_style, include_charts, include_ai_image,
|
|
@@ -2784,14 +2786,12 @@ with gr.Blocks(css=css, title="AI PPT Generator Pro") as demo:
|
|
| 2784 |
# Initialize APIs on startup
|
| 2785 |
if __name__ == "__main__":
|
| 2786 |
# Try to initialize APIs in parallel
|
| 2787 |
-
with concurrent.futures.ThreadPoolExecutor(max_workers=
|
| 2788 |
futures = []
|
| 2789 |
if AI_IMAGE_API_URL:
|
| 2790 |
futures.append(executor.submit(initialize_ai_image_api))
|
| 2791 |
if FLUX_API_URL:
|
| 2792 |
futures.append(executor.submit(initialize_flux_api))
|
| 2793 |
-
if DIAGRAM_API_URL:
|
| 2794 |
-
futures.append(executor.submit(initialize_diagram_api))
|
| 2795 |
|
| 2796 |
# Wait for all to complete
|
| 2797 |
for future in concurrent.futures.as_completed(futures):
|
|
@@ -2800,4 +2800,11 @@ if __name__ == "__main__":
|
|
| 2800 |
except Exception as e:
|
| 2801 |
logger.error(f"API initialization failed: {e}")
|
| 2802 |
|
| 2803 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
PPTX_AVAILABLE = False
|
| 35 |
logger.warning("python-pptx ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ค์น๋์ง ์์์ต๋๋ค. pip install python-pptx")
|
| 36 |
|
| 37 |
+
# ๋ค์ด์ด๊ทธ๋จ ์์ฑ๊ธฐ ๋ชจ๋ import ์ถ๊ฐ
|
| 38 |
+
try:
|
| 39 |
+
from concept_map_generator import generate_concept_map
|
| 40 |
+
from synoptic_chart_generator import generate_synoptic_chart
|
| 41 |
+
from radial_diagram_generator import generate_radial_diagram
|
| 42 |
+
from process_flow_generator import generate_process_flow_diagram
|
| 43 |
+
from wbs_diagram_generator import generate_wbs_diagram
|
| 44 |
+
DIAGRAM_GENERATORS_AVAILABLE = True
|
| 45 |
+
logger.info("๋ค์ด์ด๊ทธ๋จ ์์ฑ๊ธฐ ๋ชจ๋ ๋ก๋ ์ฑ๊ณต")
|
| 46 |
+
except ImportError as e:
|
| 47 |
+
DIAGRAM_GENERATORS_AVAILABLE = False
|
| 48 |
+
logger.warning(f"๋ค์ด์ด๊ทธ๋จ ์์ฑ๊ธฐ ๋ชจ๋์ ์ฐพ์ ์ ์์ต๋๋ค: {e}")
|
| 49 |
+
|
| 50 |
##############################################################################
|
| 51 |
# API Configuration
|
| 52 |
##############################################################################
|
|
|
|
| 107 |
return False
|
| 108 |
|
| 109 |
##############################################################################
|
| 110 |
+
# Diagram Generation API Configuration - REMOVED (using local generators)
|
| 111 |
##############################################################################
|
| 112 |
+
# ๊ธฐ์กด Diagram API ๊ด๋ จ ์ฝ๋ ์ ๊ฑฐํ๊ณ ๋ก์ปฌ ์์ฑ๊ธฐ ์ฌ์ฉ
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
|
| 114 |
##############################################################################
|
| 115 |
# Design Themes and Color Schemes
|
|
|
|
| 350 |
return 'โถ๏ธ'
|
| 351 |
|
| 352 |
##############################################################################
|
| 353 |
+
# Diagram Type Detection - UPDATED for 6 types
|
| 354 |
##############################################################################
|
| 355 |
def detect_diagram_type(title: str, content: str) -> Optional[str]:
|
| 356 |
"""์ฌ๋ผ์ด๋ ๋ด์ฉ์ ๋ถ์ํ์ฌ ์ ์ ํ ๋ค์ด์ด๊ทธ๋จ ํ์
๊ฒฐ์ """
|
| 357 |
combined_text = f"{title} {content}".lower()
|
| 358 |
|
| 359 |
# Process Flow keywords
|
| 360 |
+
if any(word in combined_text for word in ['ํ๋ก์ธ์ค', 'process', '์ ์ฐจ', 'procedure', '๋จ๊ณ', 'step', 'flow', 'ํ๋ฆ', '์ํฌํ๋ก์ฐ', 'workflow', '์์', 'sequence']):
|
| 361 |
return "Process Flow"
|
| 362 |
|
| 363 |
# WBS keywords
|
| 364 |
+
elif any(word in combined_text for word in ['wbs', '์์
๋ถํด', 'ํ๋ก์ ํธ', 'project', '์
๋ฌด๋ถํด', 'breakdown', '๊ตฌ์กฐ๋', '์์
๊ตฌ์กฐ', 'work structure']):
|
| 365 |
return "WBS Diagram"
|
| 366 |
|
| 367 |
# Concept Map keywords
|
| 368 |
+
elif any(word in combined_text for word in ['๊ฐ๋
', 'concept', '๊ด๊ณ', 'relationship', '์ฐ๊ด', 'connection', '๋ง์ธ๋๋งต', 'mindmap', '๊ตฌ์กฐ', 'structure', '์ฒด๊ณ', 'system']):
|
| 369 |
return "Concept Map"
|
| 370 |
|
| 371 |
# Radial Diagram keywords
|
| 372 |
+
elif any(word in combined_text for word in ['์ค์ฌ', 'central', '๋ฐฉ์ฌํ', 'radial', 'ํต์ฌ', 'core', '์ฃผ์', 'main', '์ค์', 'center']):
|
| 373 |
return "Radial Diagram"
|
| 374 |
|
| 375 |
# Synoptic Chart keywords
|
| 376 |
+
elif any(word in combined_text for word in ['๊ฐ์', 'overview', '์ ์ฒด', 'overall', '์์ฝ', 'summary', '์๋ํฑ', 'synoptic', '์ด๊ด', 'comprehensive']):
|
| 377 |
return "Synoptic Chart"
|
| 378 |
|
| 379 |
+
# Default: ํค์๋๊ฐ ์์ผ๋ฉด ๋ด์ฉ์ ํน์ฑ์ ๋ฐ๋ผ ๊ฒฐ์
|
| 380 |
+
# ๋ฆฌ์คํธ๋ ํญ๋ชฉ์ด ๋ง์ผ๋ฉด Concept Map
|
| 381 |
+
if content.count('\n-') > 3 or content.count('\nโข') > 3:
|
| 382 |
+
return "Concept Map"
|
| 383 |
+
# ์ซ์๊ฐ ์์ผ๋ฉด Process Flow
|
| 384 |
+
elif any(char in content for char in ['1.', '2.', '3.', 'โ ', 'โก', 'โข']):
|
| 385 |
+
return "Process Flow"
|
| 386 |
+
|
| 387 |
return None
|
| 388 |
|
| 389 |
##############################################################################
|
|
|
|
| 394 |
if not FRIENDLI_TOKEN:
|
| 395 |
return None
|
| 396 |
|
| 397 |
+
# ๋ค์ด์ด๊ทธ๋จ ํ์
๋ณ JSON ๊ตฌ์กฐ ๊ฐ์ด๋ - paste-2.txt์ ๊ตฌ์กฐ์ ๋์ผํ๊ฒ
|
| 398 |
json_guides = {
|
| 399 |
"Concept Map": """Generate a JSON for a concept map with the EXACT following structure:
|
| 400 |
{
|
|
|
|
| 480 |
2. The JSON must follow the EXACT structure shown above
|
| 481 |
3. Create content based on the provided title and content
|
| 482 |
4. Use the user's language (Korean or English) for the content values
|
| 483 |
+
5. Keep it simple with 3-5 main nodes/steps
|
| 484 |
+
6. For Process Flow: 'type' can be "process", "decision", "start", "end"
|
| 485 |
+
7. Ensure all connections reference existing node IDs"""
|
| 486 |
|
| 487 |
messages = [
|
| 488 |
{"role": "system", "content": system_prompt},
|
|
|
|
| 526 |
return None
|
| 527 |
|
| 528 |
##############################################################################
|
| 529 |
+
# Generate Diagram using Local Generators - NEW
|
| 530 |
##############################################################################
|
| 531 |
+
def generate_diagram_locally(json_data: str, diagram_type: str, output_format: str = "png") -> Optional[str]:
|
| 532 |
+
"""๋ก์ปฌ ์์ฑ๊ธฐ๋ฅผ ์ฌ์ฉํ์ฌ ๋ค์ด์ด๊ทธ๋จ ์์ฑ"""
|
| 533 |
+
if not DIAGRAM_GENERATORS_AVAILABLE:
|
| 534 |
+
logger.error("๋ค์ด์ด๊ทธ๋จ ์์ฑ๊ธฐ ๋ชจ๋์ ์ฌ์ฉํ ์ ์์ต๋๋ค")
|
| 535 |
return None
|
| 536 |
|
| 537 |
try:
|
| 538 |
+
# ์ ์ ํ ์์ฑ๊ธฐ๋ฅผ ์ฌ์ฉํ์ฌ ๋ค์ด์ด๊ทธ๋จ ์์ฑ
|
| 539 |
+
if diagram_type == "Concept Map":
|
| 540 |
+
result = generate_concept_map(json_data, output_format)
|
| 541 |
+
elif diagram_type == "Synoptic Chart":
|
| 542 |
+
result = generate_synoptic_chart(json_data, output_format)
|
| 543 |
+
elif diagram_type == "Radial Diagram":
|
| 544 |
+
result = generate_radial_diagram(json_data, output_format)
|
| 545 |
+
elif diagram_type == "Process Flow":
|
| 546 |
+
result = generate_process_flow_diagram(json_data, output_format)
|
| 547 |
+
elif diagram_type == "WBS Diagram":
|
| 548 |
+
result = generate_wbs_diagram(json_data, output_format)
|
| 549 |
+
else:
|
| 550 |
+
logger.error(f"Unknown diagram type: {diagram_type}")
|
| 551 |
+
return None
|
| 552 |
|
| 553 |
+
# ๊ฒฐ๊ณผ๊ฐ ๋ฌธ์์ด์ด๊ณ ์๋ฌ ๋ฉ์์ง์ธ ๊ฒฝ์ฐ
|
| 554 |
+
if isinstance(result, str) and result.startswith("Error:"):
|
| 555 |
+
logger.error(f"Diagram generation error: {result}")
|
| 556 |
+
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
| 557 |
|
| 558 |
+
# ์ฑ๊ณต์ ์ผ๋ก ์์ฑ๋ ๊ฒฝ์ฐ ํ์ผ ๊ฒฝ๋ก ๋ฐํ
|
| 559 |
+
return result
|
| 560 |
|
| 561 |
except Exception as e:
|
| 562 |
+
logger.error(f"Failed to generate diagram locally: {e}")
|
| 563 |
return None
|
| 564 |
|
| 565 |
##############################################################################
|
|
|
|
| 1051 |
logger.error(f"Failed to generate 3D image: {e}")
|
| 1052 |
return None
|
| 1053 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1054 |
def generate_images_parallel(prompt_3d: str, prompt_photo: str) -> Tuple[Optional[str], Optional[str]]:
|
| 1055 |
"""๋ API๋ฅผ ๋ณ๋ ฌ๋ก ํธ์ถํ์ฌ ์ด๋ฏธ์ง ์์ฑ"""
|
| 1056 |
import concurrent.futures
|
|
|
|
| 1710 |
# ์ฐ์ธก์ ์๊ฐ์ ์์ ์ถ๊ฐ
|
| 1711 |
visual_added = False
|
| 1712 |
|
| 1713 |
+
if visual_type[0] == 'diagram' and DIAGRAM_GENERATORS_AVAILABLE:
|
| 1714 |
# ๋ค์ด์ด๊ทธ๋จ ์์ฑ
|
| 1715 |
logger.info(f"Generating {visual_type[1]} for slide {i+1}")
|
| 1716 |
diagram_json = generate_diagram_json(slide_title, slide_content, visual_type[1])
|
| 1717 |
|
| 1718 |
if diagram_json:
|
| 1719 |
+
diagram_path = generate_diagram_locally(diagram_json, visual_type[1], "png")
|
| 1720 |
if diagram_path and os.path.exists(diagram_path):
|
| 1721 |
try:
|
| 1722 |
# ๋ค์ด์ด๊ทธ๋จ ์ด๋ฏธ์ง ์ถ๊ฐ
|
|
|
|
| 1727 |
)
|
| 1728 |
visual_added = True
|
| 1729 |
|
| 1730 |
+
# ๋ค์ด์ด๊ทธ๋จ ์บก์
์ถ๊ฐ
|
| 1731 |
+
caption_box = slide.shapes.add_textbox(
|
| 1732 |
+
Inches(5.2), Inches(4.6), Inches(4.3), Inches(0.3)
|
| 1733 |
+
)
|
| 1734 |
+
caption_tf = caption_box.text_frame
|
| 1735 |
+
caption_tf.text = f"{visual_type[1]} Diagram"
|
| 1736 |
+
caption_p = caption_tf.paragraphs[0]
|
| 1737 |
+
caption_p.font.size = Pt(10)
|
| 1738 |
+
caption_p.font.color.rgb = theme['colors']['secondary']
|
| 1739 |
+
caption_p.alignment = PP_ALIGN.CENTER
|
| 1740 |
+
|
| 1741 |
# ์์ ํ์ผ ์ญ์
|
| 1742 |
+
try:
|
| 1743 |
+
os.unlink(diagram_path)
|
| 1744 |
+
except:
|
| 1745 |
+
pass
|
| 1746 |
except Exception as e:
|
| 1747 |
logger.error(f"Failed to add diagram: {e}")
|
| 1748 |
|
|
|
|
| 2250 |
include_flux_images = False
|
| 2251 |
yield None, "โ ๏ธ FLUX API ์ฐ๊ฒฐ ์คํจ. ํฌํ ๋ฆฌ์ผ๋ฆฌ์คํฑ ์ด๋ฏธ์ง ์์ด ์งํํฉ๋๋ค.", ""
|
| 2252 |
|
| 2253 |
+
# ๋ค์ด์ด๊ทธ๋จ ์์ฑ๊ธฐ ํ์ธ
|
| 2254 |
+
if include_diagrams and not DIAGRAM_GENERATORS_AVAILABLE:
|
| 2255 |
+
yield None, "โ ๏ธ ๋ค์ด์ด๊ทธ๋จ ์์ฑ๊ธฐ ๋ชจ๋์ ์ฐพ์ ์ ์์ต๋๋ค. ๋ค์ด์ด๊ทธ๋จ ์์ด ์งํํฉ๋๋ค.", ""
|
| 2256 |
+
include_diagrams = False
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2257 |
|
| 2258 |
# Process reference files if provided
|
| 2259 |
additional_context = ""
|
|
|
|
| 2329 |
visual_features = []
|
| 2330 |
if include_ai_image and FLUX_API_ENABLED:
|
| 2331 |
visual_features.append("AI 3D ํ์ง ์ด๋ฏธ์ง")
|
| 2332 |
+
if include_diagrams and DIAGRAM_GENERATORS_AVAILABLE:
|
| 2333 |
visual_features.append("๋ค์ด์ด๊ทธ๋จ")
|
| 2334 |
if include_flux_images and FLUX_API_ENABLED:
|
| 2335 |
visual_features.append("AI ์์ฑ ์ด๋ฏธ์ง")
|
|
|
|
| 2356 |
|
| 2357 |
if include_ai_image and FLUX_API_ENABLED:
|
| 2358 |
success_msg += f"๐ผ๏ธ AI ์์ฑ ํ์ง ์ด๋ฏธ์ง ํฌํจ\n"
|
| 2359 |
+
if include_diagrams and DIAGRAM_GENERATORS_AVAILABLE:
|
| 2360 |
success_msg += f"๐ AI ์์ฑ ๋ค์ด์ด๊ทธ๋จ ํฌํจ\n"
|
| 2361 |
if include_flux_images and FLUX_API_ENABLED:
|
| 2362 |
success_msg += f"๐จ AI ์์ฑ ์ฌ๋ผ์ด๋ ์ด๋ฏธ์ง ํฌํจ\n"
|
|
|
|
| 2556 |
max-height: 500px;
|
| 2557 |
overflow-y: auto;
|
| 2558 |
}
|
| 2559 |
+
|
| 2560 |
+
/* ๋ฐ์ํ ๋์์ธ */
|
| 2561 |
+
@media (max-width: 768px) {
|
| 2562 |
+
.main-container {
|
| 2563 |
+
padding: 15px;
|
| 2564 |
+
margin: 10px;
|
| 2565 |
+
}
|
| 2566 |
+
|
| 2567 |
+
.header-section h1 {
|
| 2568 |
+
font-size: 2em;
|
| 2569 |
+
}
|
| 2570 |
+
|
| 2571 |
+
.gr-tab-item {
|
| 2572 |
+
padding: 10px 15px;
|
| 2573 |
+
font-size: 1em;
|
| 2574 |
+
}
|
| 2575 |
+
}
|
| 2576 |
"""
|
| 2577 |
|
| 2578 |
with gr.Blocks(css=css, title="AI PPT Generator Pro") as demo:
|
|
|
|
| 2581 |
# ๐ฏ AI ๊ธฐ๋ฐ PPT ์๋ ์์ฑ ์์คํ
Pro
|
| 2582 |
|
| 2583 |
๊ณ ๊ธ ๋์์ธ ํ
๋ง์ ๋ ์ด์์์ ํ์ฉํ ์ ๋ฌธ์ ์ธ ํ๋ ์ ํ
์ด์
์ ์๋์ผ๋ก ์์ฑํฉ๋๋ค.
|
| 2584 |
+
6๊ฐ์ง ๋ค์ด์ด๊ทธ๋จ ํ์
๊ณผ AI ์์ฑ ์ด๋ฏธ์ง๋ฅผ ํฌํจํ์ฌ ์๊ฐ์ ์ผ๋ก ํ๋ถํ PPT๋ฅผ ๋ง๋ญ๋๋ค.
|
| 2585 |
"""
|
| 2586 |
)
|
| 2587 |
|
|
|
|
| 2677 |
include_diagrams = gr.Checkbox(
|
| 2678 |
label="๐ AI ๋ค์ด์ด๊ทธ๋จ",
|
| 2679 |
value=False,
|
| 2680 |
+
info="6๊ฐ์ง ํ์
์ ๋ค์ด์ด๊ทธ๋จ ์๋ ์์ฑ"
|
| 2681 |
)
|
| 2682 |
|
| 2683 |
include_flux_images = gr.Checkbox(
|
|
|
|
| 2732 |
5. **์ฐธ๊ณ ์๋ฃ ์
๋ก๋**: PDF, CSV, TXT ํ์ผ ์ง์
|
| 2733 |
6. **์์ฑ ๋ฒํผ ํด๋ฆญ**: AI๊ฐ ์๋์ผ๋ก PPT ์์ฑ
|
| 2734 |
|
| 2735 |
+
### ๐จ ์๋ก์ด ๊ธฐ๋ฅ - 6๊ฐ์ง ๋ค์ด์ด๊ทธ๋จ ํ์
|
| 2736 |
+
- **Process Flow**: ํ๋ก์ธ์ค์ ์ํฌํ๋ก์ฐ๋ฅผ ์๊ฐํ
|
| 2737 |
+
- **Concept Map**: ๊ฐ๋
๊ณผ ๊ด๊ณ๋ฅผ ๊ตฌ์กฐํํ์ฌ ํํ
|
| 2738 |
+
- **WBS Diagram**: ํ๋ก์ ํธ ์์
๋ถํด ๊ตฌ์กฐ๋
|
| 2739 |
+
- **Radial Diagram**: ์ค์ฌ ๊ฐ๋
๊ณผ ๊ด๋ จ ์์๋ค์ ๋ฐฉ์ฌํ ํํ
|
| 2740 |
+
- **Synoptic Chart**: ์ ์ฒด ๊ฐ์์ ์์ฝ์ ํ๋์ ๋ณด์ฌ์ฃผ๋ ์ฐจํธ
|
| 2741 |
+
- **์๋ ๊ฐ์ง**: ์ฌ๋ผ์ด๋ ๋ด์ฉ์ ๋ถ์ํ์ฌ ์ ์ ํ ๋ค์ด์ด๊ทธ๋จ ํ์
์๋ ์ ํ
|
| 2742 |
|
| 2743 |
### ๐ก ๊ณ ๊ธ ํ
|
| 2744 |
+
- **๋ค์ด์ด๊ทธ๋จ ์๋ ์์ฑ**: ์ฌ๋ผ์ด๋ ๋ด์ฉ์ ๋ฐ๋ผ 6๊ฐ์ง ๋ค์ด์ด๊ทธ๋จ ์ค ๊ฐ์ฅ ์ ํฉํ ํ์
์ด ์๋์ผ๋ก ์ ํ๋ฉ๋๋ค
|
| 2745 |
+
- **๋ณ๋ ฌ ์ด๋ฏธ์ง ์์ฑ**: 3D ์คํ์ผ๊ณผ ํฌํ ๋ฆฌ์ผ๋ฆฌ์คํฑ ์ด๋ฏธ์ง๊ฐ ๋์์ ์์ฑ๋์ด ์๊ฐ์ด ์ ์ฝ๋ฉ๋๋ค
|
| 2746 |
+
- **์ค๋งํธ ๋ฐฐ์น**: ๋ค์ด์ด๊ทธ๋จ๊ณผ ์ด๋ฏธ์ง๋ ํ
์คํธ์ ๊ฒน์น์ง ์๋๋ก ์ฐ์ธก์ ์๋ ๋ฐฐ์น๋ฉ๋๋ค
|
| 2747 |
+
- **ํค์๋ ๊ธฐ๋ฐ ๊ฐ์ง**: ํ๋ก์ธ์ค, ๊ฐ๋
, ํ๋ก์ ํธ, ์ค์ฌ, ๊ฐ์ ๋ฑ์ ํค์๋๋ฅผ ๊ฐ์งํ์ฌ ๋ค์ด์ด๊ทธ๋จ ํ์
์ ๊ฒฐ์ ํฉ๋๋ค
|
| 2748 |
"""
|
| 2749 |
)
|
| 2750 |
|
|
|
|
| 2755 |
["2024๋
๋์งํธ ๋ง์ผํ
ํธ๋ ๋", 12, True, True, [], "modern", "modern", "consistent", False, True, True, True],
|
| 2756 |
["๊ธฐํ๋ณํ์ ์ง์๊ฐ๋ฅํ ๋ฐ์ ", 15, True, True, [], "nature", "classic", "consistent", False, True, True, True],
|
| 2757 |
["์คํํธ์
์ฌ์
๊ณํ์", 8, False, True, [], "creative", "modern", "varied", False, True, True, True],
|
| 2758 |
+
["ํ๋ก์ ํธ ๊ด๋ฆฌ ๋ฐฉ๋ฒ๋ก ", 10, False, True, [], "professional", "modern", "consistent", False, False, True, False],
|
| 2759 |
+
["๋จธ์ ๋ฌ๋ ํ๋ก์ธ์ค ๊ฐ์ด๋", 12, False, True, [], "modern", "modern", "consistent", False, False, True, False],
|
| 2760 |
],
|
| 2761 |
inputs=[topic_input, num_slides, use_web_search, use_korean, reference_files,
|
| 2762 |
design_theme, font_style, layout_style, include_charts, include_ai_image,
|
|
|
|
| 2786 |
# Initialize APIs on startup
|
| 2787 |
if __name__ == "__main__":
|
| 2788 |
# Try to initialize APIs in parallel
|
| 2789 |
+
with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
|
| 2790 |
futures = []
|
| 2791 |
if AI_IMAGE_API_URL:
|
| 2792 |
futures.append(executor.submit(initialize_ai_image_api))
|
| 2793 |
if FLUX_API_URL:
|
| 2794 |
futures.append(executor.submit(initialize_flux_api))
|
|
|
|
|
|
|
| 2795 |
|
| 2796 |
# Wait for all to complete
|
| 2797 |
for future in concurrent.futures.as_completed(futures):
|
|
|
|
| 2800 |
except Exception as e:
|
| 2801 |
logger.error(f"API initialization failed: {e}")
|
| 2802 |
|
| 2803 |
+
# ๋ค์ด์ด๊ทธ๋จ ์์ฑ๊ธฐ ๋ชจ๋ ์ํ ํ์ธ
|
| 2804 |
+
if DIAGRAM_GENERATORS_AVAILABLE:
|
| 2805 |
+
logger.info("โ
๋ค์ด์ด๊ทธ๋จ ์์ฑ๊ธฐ ๋ชจ๋์ด ์ ์์ ์ผ๋ก ๋ก๋๋์์ต๋๋ค")
|
| 2806 |
+
logger.info("์ง์๋๋ ๋ค์ด์ด๊ทธ๋จ ํ์
: Process Flow, Concept Map, WBS, Radial, Synoptic Chart")
|
| 2807 |
+
else:
|
| 2808 |
+
logger.warning("โ ๏ธ ๋ค์ด์ด๊ทธ๋จ ์์ฑ๊ธฐ ๋ชจ๋์ ์ฐพ์ ์ ์์ต๋๋ค. ๋ค์ด์ด๊ทธ๋จ ๊ธฐ๋ฅ์ด ๋นํ์ฑํ๋ฉ๋๋ค.")
|
| 2809 |
+
|
| 2810 |
+
demo.launch()
|