Update app.py
Browse files
app.py
CHANGED
@@ -467,6 +467,9 @@ with gr.Blocks(theme=gr.themes.Soft()) as app:
|
|
467 |
if __name__ == "__main__":
|
468 |
app.launch(share=True)'''
|
469 |
|
|
|
|
|
|
|
470 |
import gradio as gr
|
471 |
import os
|
472 |
import time
|
@@ -479,15 +482,22 @@ from dotenv import load_dotenv
|
|
479 |
from huggingface_hub import HfApi, upload_file
|
480 |
import json
|
481 |
from functools import partial
|
|
|
|
|
|
|
|
|
|
|
482 |
|
483 |
load_dotenv()
|
484 |
|
485 |
# Configuration
|
486 |
HF_TOKEN = os.getenv("HF_TOKEN")
|
|
|
|
|
487 |
HF_USERNAME = "jsakshi"
|
488 |
HEADERS = {"Authorization": f"Bearer {HF_TOKEN}"}
|
489 |
|
490 |
-
# Simulated user authentication (
|
491 |
AUTHORIZED_USERS = {"admin": "password123"} # username: password
|
492 |
|
493 |
# Store edit history for undo/redo
|
@@ -495,49 +505,79 @@ edit_history = []
|
|
495 |
current_history_index = -1
|
496 |
|
497 |
def generate_initial_content(topic):
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
"
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
506 |
return response.json()[0]['generated_text']
|
507 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
508 |
|
509 |
def create_or_update_space(content_data, space_name=None, images=[]):
|
510 |
-
|
511 |
-
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
if part.
|
525 |
-
if
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
|
|
|
|
|
|
541 |
<head>
|
542 |
<meta charset="UTF-8">
|
543 |
<title>Editable Blog</title>
|
@@ -548,8 +588,9 @@ def create_or_update_space(content_data, space_name=None, images=[]):
|
|
548 |
.image-container {{ position: relative; margin: 20px 0; }}
|
549 |
.editable-image {{ width: 100%; max-width: 500px; cursor: move; }}
|
550 |
.delete-image {{ position: absolute; top: 5px; right: 5px; }}
|
551 |
-
.editing-tools {{ position: fixed; top: 10px; left: 10px; background: white; padding: 10px; border: 1px solid #ccc; }}
|
552 |
[contenteditable]:focus {{ outline: 2px solid #2D68C4; }}
|
|
|
553 |
</style>
|
554 |
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
555 |
</head>
|
@@ -569,6 +610,7 @@ def create_or_update_space(content_data, space_name=None, images=[]):
|
|
569 |
<button onclick="redo()">Redo</button>
|
570 |
</div>
|
571 |
{html_content}
|
|
|
572 |
<script>
|
573 |
let currentSpace = "{space_name}";
|
574 |
let images = {json.dumps(images)};
|
@@ -618,7 +660,7 @@ def create_or_update_space(content_data, space_name=None, images=[]):
|
|
618 |
}}
|
619 |
}}
|
620 |
|
621 |
-
// Drag and drop
|
622 |
document.querySelectorAll('.image-container').forEach(container => {{
|
623 |
container.addEventListener('dragstart', e => {{
|
624 |
e.dataTransfer.setData('text/plain', container.dataset.index);
|
@@ -658,30 +700,76 @@ def create_or_update_space(content_data, space_name=None, images=[]):
|
|
658 |
</script>
|
659 |
</body>
|
660 |
</html>"""
|
661 |
-
|
662 |
-
|
663 |
-
|
664 |
-
|
665 |
-
|
666 |
-
|
667 |
-
|
668 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
669 |
|
670 |
def authenticate(username, password):
|
671 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
672 |
|
673 |
def generate_and_edit(topic, username, password):
|
|
|
|
|
|
|
|
|
674 |
if not authenticate(username, password):
|
675 |
-
return "Authentication failed", "", ""
|
676 |
|
|
|
|
|
|
|
|
|
|
|
677 |
initial_content = generate_initial_content(topic)
|
678 |
-
|
679 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
680 |
|
681 |
-
#
|
682 |
-
|
683 |
-
|
684 |
-
|
|
|
|
|
685 |
|
686 |
# Gradio interface
|
687 |
with gr.Blocks(theme=gr.themes.Soft()) as app:
|
@@ -691,7 +779,7 @@ with gr.Blocks(theme=gr.themes.Soft()) as app:
|
|
691 |
with gr.Row():
|
692 |
with gr.Column():
|
693 |
username = gr.Textbox(label="Username", placeholder="admin")
|
694 |
-
password = gr.Textbox(label="Password", type="password")
|
695 |
topic_input = gr.Textbox(label="Blog Topic", placeholder="e.g., Future of AI")
|
696 |
generate_btn = gr.Button("Generate & Edit", variant="primary")
|
697 |
|
@@ -700,9 +788,6 @@ with gr.Blocks(theme=gr.themes.Soft()) as app:
|
|
700 |
blog_link = gr.Markdown("Blog link will appear here...")
|
701 |
blog_preview = gr.Markdown(label="Preview", value="Content preview will appear here...")
|
702 |
|
703 |
-
# Update function (simulated)
|
704 |
-
update_btn = gr.Button("Update Content", visible=False)
|
705 |
-
|
706 |
generate_btn.click(
|
707 |
fn=generate_and_edit,
|
708 |
inputs=[topic_input, username, password],
|
@@ -710,4 +795,5 @@ with gr.Blocks(theme=gr.themes.Soft()) as app:
|
|
710 |
)
|
711 |
|
712 |
if __name__ == "__main__":
|
713 |
-
app.launch(share=True)
|
|
|
|
467 |
if __name__ == "__main__":
|
468 |
app.launch(share=True)'''
|
469 |
|
470 |
+
|
471 |
+
|
472 |
+
|
473 |
import gradio as gr
|
474 |
import os
|
475 |
import time
|
|
|
482 |
from huggingface_hub import HfApi, upload_file
|
483 |
import json
|
484 |
from functools import partial
|
485 |
+
import logging
|
486 |
+
|
487 |
+
# Set up logging
|
488 |
+
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
|
489 |
+
logger = logging.getLogger(__name__)
|
490 |
|
491 |
load_dotenv()
|
492 |
|
493 |
# Configuration
|
494 |
HF_TOKEN = os.getenv("HF_TOKEN")
|
495 |
+
if not HF_TOKEN:
|
496 |
+
logger.error("HF_TOKEN not found in .env file")
|
497 |
HF_USERNAME = "jsakshi"
|
498 |
HEADERS = {"Authorization": f"Bearer {HF_TOKEN}"}
|
499 |
|
500 |
+
# Simulated user authentication (replace with proper auth in production)
|
501 |
AUTHORIZED_USERS = {"admin": "password123"} # username: password
|
502 |
|
503 |
# Store edit history for undo/redo
|
|
|
505 |
current_history_index = -1
|
506 |
|
507 |
def generate_initial_content(topic):
|
508 |
+
"""Generate initial blog content using Hugging Face API."""
|
509 |
+
logger.info(f"Generating content for topic: {topic}")
|
510 |
+
try:
|
511 |
+
prompt = f"""Create a detailed blog post about {topic} including:
|
512 |
+
- A compelling title and subtitle
|
513 |
+
- An introduction
|
514 |
+
- 3 main sections with descriptive headings
|
515 |
+
- A conclusion
|
516 |
+
Use an informative tone."""
|
517 |
+
response = requests.post(
|
518 |
+
"https://api-inference.huggingface.co/models/mistralai/Mistral-7B-Instruct-v0.2",
|
519 |
+
headers=HEADERS,
|
520 |
+
json={"inputs": prompt, "parameters": {"max_length": 2000}}
|
521 |
+
)
|
522 |
+
if response.status_code != 200:
|
523 |
+
logger.error(f"API request failed: {response.status_code} - {response.text}")
|
524 |
+
return f"Error: API request failed with status {response.status_code}"
|
525 |
return response.json()[0]['generated_text']
|
526 |
+
except Exception as e:
|
527 |
+
logger.error(f"Error generating content: {str(e)}")
|
528 |
+
return f"Error generating content: {str(e)}"
|
529 |
+
|
530 |
+
def generate_image(prompt):
|
531 |
+
"""Generate an image using Stable Diffusion API."""
|
532 |
+
try:
|
533 |
+
response = requests.post(
|
534 |
+
"https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0",
|
535 |
+
headers=HEADERS,
|
536 |
+
json={"inputs": prompt}
|
537 |
+
)
|
538 |
+
if response.status_code == 200:
|
539 |
+
return response.content
|
540 |
+
logger.error(f"Image generation failed: {response.status_code} - {response.text}")
|
541 |
+
return None
|
542 |
+
except Exception as e:
|
543 |
+
logger.error(f"Error generating image: {str(e)}")
|
544 |
+
return None
|
545 |
|
546 |
def create_or_update_space(content_data, space_name=None, images=[]):
|
547 |
+
"""Create or update a Hugging Face Space with editable content."""
|
548 |
+
try:
|
549 |
+
api = HfApi(token=HF_TOKEN)
|
550 |
+
if not space_name:
|
551 |
+
space_id = f"blog-{uuid.uuid4().hex[:8]}"
|
552 |
+
space_name = f"{HF_USERNAME}/{space_id}"
|
553 |
+
api.create_repo(repo_id=space_name, repo_type="space", space_sdk="static", private=False)
|
554 |
+
logger.info(f"Created new space: {space_name}")
|
555 |
+
|
556 |
+
# Process content into editable sections
|
557 |
+
sections = re.split(r'(## .+)', content_data)
|
558 |
+
html_content = '<div class="editable-container">'
|
559 |
+
current_section = ""
|
560 |
+
for part in sections:
|
561 |
+
if part.strip():
|
562 |
+
if part.startswith('## '):
|
563 |
+
if current_section:
|
564 |
+
html_content += f'<div class="section-content" contenteditable="true">{markdown.markdown(current_section)}</div></div>'
|
565 |
+
html_content += f'<div class="section"><h2 class="editable-header" contenteditable="true">{part[3:]}</h2>'
|
566 |
+
current_section = ""
|
567 |
+
else:
|
568 |
+
current_section += part
|
569 |
+
if current_section:
|
570 |
+
html_content += f'<div class="section-content" contenteditable="true">{markdown.markdown(current_section)}</div></div>'
|
571 |
+
html_content += '</div>'
|
572 |
+
|
573 |
+
# Add images
|
574 |
+
image_html = ""
|
575 |
+
for i, img_path in enumerate(images):
|
576 |
+
image_html += f'<div class="image-container" draggable="true" data-index="{i}"><img src="{img_path}" class="editable-image" alt="Blog image" /><button class="delete-image">Delete</button></div>'
|
577 |
+
|
578 |
+
# Complete HTML with editing features
|
579 |
+
complete_html = f"""<!DOCTYPE html>
|
580 |
+
<html lang="en">
|
581 |
<head>
|
582 |
<meta charset="UTF-8">
|
583 |
<title>Editable Blog</title>
|
|
|
588 |
.image-container {{ position: relative; margin: 20px 0; }}
|
589 |
.editable-image {{ width: 100%; max-width: 500px; cursor: move; }}
|
590 |
.delete-image {{ position: absolute; top: 5px; right: 5px; }}
|
591 |
+
.editing-tools {{ position: fixed; top: 10px; left: 10px; background: white; padding: 10px; border: 1px solid #ccc; z-index: 1000; }}
|
592 |
[contenteditable]:focus {{ outline: 2px solid #2D68C4; }}
|
593 |
+
body {{ font-family: Arial, sans-serif; line-height: 1.6; }}
|
594 |
</style>
|
595 |
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
596 |
</head>
|
|
|
610 |
<button onclick="redo()">Redo</button>
|
611 |
</div>
|
612 |
{html_content}
|
613 |
+
{image_html}
|
614 |
<script>
|
615 |
let currentSpace = "{space_name}";
|
616 |
let images = {json.dumps(images)};
|
|
|
660 |
}}
|
661 |
}}
|
662 |
|
663 |
+
// Drag and drop images
|
664 |
document.querySelectorAll('.image-container').forEach(container => {{
|
665 |
container.addEventListener('dragstart', e => {{
|
666 |
e.dataTransfer.setData('text/plain', container.dataset.index);
|
|
|
700 |
</script>
|
701 |
</body>
|
702 |
</html>"""
|
703 |
+
|
704 |
+
# Upload HTML
|
705 |
+
upload_file(
|
706 |
+
path_or_fileobj=complete_html.encode(),
|
707 |
+
path_in_repo="index.html",
|
708 |
+
repo_id=space_name,
|
709 |
+
repo_type="space"
|
710 |
+
)
|
711 |
+
|
712 |
+
# Upload images if provided as bytes
|
713 |
+
for i, img in enumerate(images):
|
714 |
+
if isinstance(img, bytes):
|
715 |
+
upload_file(
|
716 |
+
path_or_fileobj=img,
|
717 |
+
path_in_repo=f"image_{i}.png",
|
718 |
+
repo_id=space_name,
|
719 |
+
repo_type="space"
|
720 |
+
)
|
721 |
+
images[i] = f"image_{i}.png"
|
722 |
+
|
723 |
+
logger.info(f"Updated space: {space_name}")
|
724 |
+
return f"https://huggingface.co/spaces/{space_name}"
|
725 |
+
except Exception as e:
|
726 |
+
logger.error(f"Error creating/updating space: {str(e)}")
|
727 |
+
return None
|
728 |
|
729 |
def authenticate(username, password):
|
730 |
+
"""Authenticate user against hardcoded credentials."""
|
731 |
+
if not username or not password:
|
732 |
+
logger.warning("Empty username or password provided")
|
733 |
+
return False
|
734 |
+
is_valid = username in AUTHORIZED_USERS and AUTHORIZED_USERS[username] == password
|
735 |
+
logger.info(f"Authentication attempt for {username}: {'Success' if is_valid else 'Failed'}")
|
736 |
+
return is_valid
|
737 |
|
738 |
def generate_and_edit(topic, username, password):
|
739 |
+
"""Generate blog and create editable space."""
|
740 |
+
logger.info(f"Starting generate_and_edit for topic: {topic}")
|
741 |
+
|
742 |
+
# Check authentication
|
743 |
if not authenticate(username, password):
|
744 |
+
return "Authentication failed: Incorrect username or password", "", "Error"
|
745 |
|
746 |
+
# Check HF_TOKEN
|
747 |
+
if not HF_TOKEN:
|
748 |
+
return "Authentication failed: HF_TOKEN not set in .env", "", "Error"
|
749 |
+
|
750 |
+
# Generate content
|
751 |
initial_content = generate_initial_content(topic)
|
752 |
+
if "Error" in initial_content:
|
753 |
+
return "Failed to generate content", initial_content, "Error"
|
754 |
+
|
755 |
+
# Generate images
|
756 |
+
image_prompts = [
|
757 |
+
f"Professional illustration about {topic}, clean design, minimalist style.",
|
758 |
+
f"Data visualization related to {topic}, infographic style."
|
759 |
+
]
|
760 |
+
images = []
|
761 |
+
for prompt in image_prompts:
|
762 |
+
img_data = generate_image(prompt)
|
763 |
+
if img_data:
|
764 |
+
images.append(img_data)
|
765 |
+
time.sleep(2) # Prevent rate limiting
|
766 |
|
767 |
+
# Create space
|
768 |
+
space_url = create_or_update_space(initial_content, images=images)
|
769 |
+
if not space_url:
|
770 |
+
return "Failed to create space", initial_content, "Error"
|
771 |
+
|
772 |
+
return space_url, initial_content, "Blog generated successfully"
|
773 |
|
774 |
# Gradio interface
|
775 |
with gr.Blocks(theme=gr.themes.Soft()) as app:
|
|
|
779 |
with gr.Row():
|
780 |
with gr.Column():
|
781 |
username = gr.Textbox(label="Username", placeholder="admin")
|
782 |
+
password = gr.Textbox(label="Password", type="password", placeholder="password123")
|
783 |
topic_input = gr.Textbox(label="Blog Topic", placeholder="e.g., Future of AI")
|
784 |
generate_btn = gr.Button("Generate & Edit", variant="primary")
|
785 |
|
|
|
788 |
blog_link = gr.Markdown("Blog link will appear here...")
|
789 |
blog_preview = gr.Markdown(label="Preview", value="Content preview will appear here...")
|
790 |
|
|
|
|
|
|
|
791 |
generate_btn.click(
|
792 |
fn=generate_and_edit,
|
793 |
inputs=[topic_input, username, password],
|
|
|
795 |
)
|
796 |
|
797 |
if __name__ == "__main__":
|
798 |
+
app.launch(share=True, debug=True)
|
799 |
+
|