import gradio as gr from huggingface_hub import InferenceClient import os import requests from typing import List, Dict, Union import traceback from PIL import Image from io import BytesIO import asyncio from gradio_client import Client import time import threading HF_TOKEN = os.getenv("HF_TOKEN") hf_client = InferenceClient("CohereForAI/c4ai-command-r-plus-08-2024", token=HF_TOKEN) def get_headers(): if not HF_TOKEN: raise ValueError("Hugging Face token not found in environment variables") return {"Authorization": f"Bearer {HF_TOKEN}"} def get_most_liked_spaces(limit: int = 300) -> Union[List[Dict], str]: url = "https://huggingface.co/api/spaces" params = { "sort": "likes", "direction": -1, "limit": limit, "full": "true" } try: response = requests.get(url, params=params, headers=get_headers()) response.raise_for_status() return response.json() except requests.RequestException as e: return f"API request error: {str(e)}" except ValueError as e: return f"JSON decoding error: {str(e)}" def format_space(space: Dict) -> Dict: space_id = space.get('id', 'Unknown') space_name = space_id.split('/')[-1] if '/' in space_id else space_id space_author = space.get('author', 'Unknown') if isinstance(space_author, dict): space_author = space_author.get('user', space_author.get('name', 'Unknown')) space_likes = space.get('likes', 'N/A') space_url = f"https://huggingface.co/spaces/{space_id}" return { "id": space_id, "name": space_name, "author": space_author, "likes": space_likes, "url": space_url, } def format_spaces(spaces: Union[List[Dict], str]) -> List[Dict]: if isinstance(spaces, str): return [{"error": spaces}] return [format_space(space) for space in spaces if isinstance(space, dict)] def summarize_space(space: Dict) -> str: system_message = "당신은 Hugging Face Space의 내용을 요약하는 AI 조수입니다. 주어진 정보를 바탕으로 간결하고 명확한 요약을 제공해주세요." user_message = f"다음 Hugging Face Space를 요약해주세요: {space['name']} by {space['author']}. 좋아요 수: {space['likes']}. URL: {space['url']}" messages = [ {"role": "system", "content": system_message}, {"role": "user", "content": user_message} ] try: response = hf_client.chat_completion(messages, max_tokens=400, temperature=0.7) return response.choices[0].message.content except Exception as e: return f"요약 생성 중 오류 발생: {str(e)}" def get_app_py_content(space_id: str) -> str: app_py_url = f"https://huggingface.co/spaces/{space_id}/raw/main/app.py" try: response = requests.get(app_py_url, headers=get_headers()) if response.status_code == 200: content = response.text if len(content) > 500: # 내용을 500자로 제한합니다 content = content[:497] + "..." return content else: return f"app.py file not found or inaccessible for space: {space_id}" except requests.RequestException: return f"Error fetching app.py content for space: {space_id}" def on_select(space): try: print(f"Selected space: {space['name']}") summary = summarize_space(space) app_content = get_app_py_content(space['id']) info = f"선택된 Space: {space['name']} (ID: {space['id']})\n" info += f"Author: {space['author']}\n" info += f"Likes: {space['likes']}\n" info += f"URL: {space['url']}\n\n" info += f"요약:\n{summary}" print(f"Returning URL: {space['url']}") return info, app_content, space['url'] except Exception as e: print(f"Error in on_select: {str(e)}") print(traceback.format_exc()) return f"오류가 발생했습니다: {str(e)}", "", "" def update_screenshot(url, last_url, force_update=False): print(f"Updating screenshot. Current URL: {url}, Last URL: {last_url}, Force update: {force_update}") if url and (url != last_url or force_update): screenshot = take_screenshot(url) print("Screenshot updated") return screenshot, url print("No update needed") return gr.update(), last_url def refresh_screenshot(url, last_url): print(f"Refresh button clicked. URL: {url}, Last URL: {last_url}") # 항상 강제로 업데이트 return update_screenshot(url, last_url, force_update=True) def take_screenshot(url): try: print(f"Taking screenshot of URL: {url}") client = Client("ginipick/selenium-screenshot-gradio") result = client.predict(url=url, api_name="/predict") print(f"Screenshot result: {result}") if isinstance(result, str) and os.path.exists(result): return Image.open(result) else: print(f"Invalid result from API: {result}") return Image.new('RGB', (600, 360), color='lightgray') except Exception as e: print(f"Screenshot error: {str(e)}") return Image.new('RGB', (600, 360), color='lightgray') def generate_usage_guide(app_content): system_message = "당신은 Python 코드를 분석하여, 화면 보듯이 이용 방법을 설명하는 AI 조수입니다. app.py 코드를 바탕으로 코드에 대한 언급은 제외하고, 이용자 관점에서 1) 기존 유사 기술 방식괴 비교해 특징, 장점에 대해 친절하고 자세하게 상세한 사용 방법을 제공해주세요." user_message = f"다음 Python 코드를 기반으로 화면 UI/UX적 측면으로 특징과 사용 방법을 설명해주세요:\n\n{app_content}" messages = [ {"role": "system", "content": system_message}, {"role": "user", "content": user_message} ] try: response = hf_client.chat_completion(messages, max_tokens=4000, temperature=0.7) return response.choices[0].message.content except Exception as e: return f"사용 방법 생성 중 오류 발생: {str(e)}" def create_ui(): try: spaces_list = get_most_liked_spaces() formatted_spaces = format_spaces(spaces_list) print(f"Total spaces loaded: {len(formatted_spaces)}") css = """ footer {visibility: hidden;} .minimal-button {min-width: 30px !important; height: 25px !important; line-height: 1 !important; font-size: 12px !important; padding: 2px 5px !important;} .space-row {margin-bottom: 5px !important;} #refresh-button, #manual-button { width: 100% !important; margin-top: 5px !important; } #info-output, #usage-guide { height: 400px; /* 높이를 400px로 증가 */ overflow-y: auto; padding-right: 10px; } #app-py-content { height: auto !important; max-height: none !important; overflow-y: visible !important; } .output-group { border: 1px solid #ddd; border-radius: 5px; padding: 10px; margin-bottom: 20px; } /* 스크롤바 스타일 수정 */ ::-webkit-scrollbar { width: 10px; } ::-webkit-scrollbar-track { background: #f1f1f1; } ::-webkit-scrollbar-thumb { background: #888; border-radius: 5px; } ::-webkit-scrollbar-thumb:hover { background: #555; } """ with gr.Blocks(css=css, theme="Nymbo/Nymbo_Theme") as demo: gr.Markdown("# 300: HuggingFace Most Liked Spaces") with gr.Row(): with gr.Column(scale=1): space_rows = [] for space in formatted_spaces: with gr.Row(elem_classes="space-row") as space_row: with gr.Column(): gr.Markdown(f"{space['name']} by {space['author']} (Likes: {space['likes']})", elem_classes="space-info") button = gr.Button("클릭", elem_classes="minimal-button") space_rows.append((space_row, button, space)) with gr.Column(scale=1): with gr.Group(elem_classes="output-group"): info_output = gr.Textbox(label="Space 정보 및 요약", elem_id="info-output", lines=20, max_lines=30) url_state = gr.State("") last_url_state = gr.State("") screenshot_output = gr.Image(type="pil", label="Live 화면", height=360, width=600) refresh_button = gr.Button("🔄 서비스 화면", elem_id="refresh-button") manual_button = gr.Button("선택 서비스 특징 및 사용법", elem_id="manual-button") with gr.Group(elem_classes="output-group"): usage_guide = gr.Textbox(label="선택 서비스 특징 및 사용법", elem_id="usage-guide", visible=False, lines=20, max_lines=30) with gr.Group(elem_classes="output-group"): app_py_content = gr.Code(language="python", label="메인 소스코드", elem_id="app-py-content", lines=None, max_lines=None) update_trigger = gr.Button("Update Screenshot", visible=False) for _, button, space in space_rows: button.click( lambda s=space: on_select(s), inputs=[], outputs=[info_output, app_py_content, url_state] ).then( update_screenshot, inputs=[url_state, last_url_state], outputs=[screenshot_output, last_url_state] ) def refresh_screenshot(url, last_url): print(f"Refresh button clicked. URL: {url}, Last URL: {last_url}") return update_screenshot(url, last_url) refresh_button.click( refresh_screenshot, inputs=[url_state, last_url_state], outputs=[screenshot_output, last_url_state] ) def show_usage_guide(app_content): usage_text = generate_usage_guide(app_content) return gr.update(value=usage_text, visible=True) manual_button.click( show_usage_guide, inputs=[app_py_content], outputs=[usage_guide] ) update_trigger.click( update_screenshot, inputs=[url_state, last_url_state], outputs=[screenshot_output, last_url_state] ) # Start a background thread to trigger updates def trigger_updates(): while True: time.sleep(5) update_trigger.click() threading.Thread(target=trigger_updates, daemon=True).start() return demo except Exception as e: print(f"Error in create_ui: {str(e)}") print(traceback.format_exc()) raise if __name__ == "__main__": try: demo = create_ui() demo.launch() except Exception as e: print(f"Error in main: {str(e)}") print(traceback.format_exc())