Spaces:
Runtime error
Runtime error
| import gradio as gr | |
| import time | |
| import io | |
| import base64 | |
| from PIL import Image, ImageDraw | |
| # --- 1. 가상 백엔드 (Virtual Backend) --- | |
| class AgentSystem: | |
| def __init__(self): | |
| self.file_system = { | |
| "research_data.csv": {"type": "csv", "content": "Month,Sales,Growth\nJan,100,10%\nFeb,120,15%\nMar,150,20%\nApr,170,18%"}, | |
| "agent_architecture.png": {"type": "image", "content": "architecture_diagram"}, | |
| "meeting_notes.txt": {"type": "text", "content": "Meeting Notes:\n- Discussed multi-agent protocols.\n- Need to optimize latency.\n- Next sprint starts Monday."}, | |
| "demo_video.mp4": {"type": "video", "content": "dummy_video_path"} | |
| } | |
| def read_file(self, filename): | |
| if filename not in self.file_system: | |
| return {"type": "none", "content": ""}, "File not found." | |
| return self.file_system[filename], f"File '{filename}' opened." | |
| def generate_plot(self): | |
| """Matplotlib을 사용하여 실제 그래프 이미지를 생성""" | |
| try: | |
| import matplotlib.pyplot as plt | |
| plt.switch_backend('Agg') | |
| fig, ax = plt.subplots(figsize=(5, 3)) | |
| months = ['Jan', 'Feb', 'Mar', 'Apr'] | |
| sales = [100, 120, 150, 170] | |
| ax.bar(months, sales, color='#007aff', alpha=0.7) | |
| ax.set_title("Q1 Research Growth") | |
| ax.grid(True, alpha=0.3) | |
| buf = io.BytesIO() | |
| plt.savefig(buf, format='png', dpi=100, bbox_inches='tight') | |
| buf.seek(0) | |
| plt.close(fig) | |
| return Image.open(buf) | |
| except ImportError: | |
| img = Image.new('RGB', (400, 300), color='white') | |
| d = ImageDraw.Draw(img) | |
| d.text((10,10), "Matplotlib not found", fill='black') | |
| return img | |
| def process_query(self, query, current_file): | |
| query = query.lower() | |
| logs = [] | |
| result_img = None | |
| result_text = "" | |
| logs.append(f"🧠 **Thought:** User wants to '{query}'...") | |
| time.sleep(0.5) | |
| if "plot" in query and current_file == "research_data.csv": | |
| logs.append(f"🛠️ **Tool:** Executing `Python.matplotlib` on {current_file}...") | |
| time.sleep(0.8) | |
| result_img = self.generate_plot() | |
| result_text = "I've generated a bar chart based on the CSV data." | |
| elif "summarize" in query and current_file: | |
| logs.append(f"🛠️ **Tool:** Reading content of {current_file}...") | |
| time.sleep(0.5) | |
| content = self.file_system[current_file]['content'] | |
| result_text = f"📝 **Summary:** The file contains {len(content)} characters. Key topic appears to be related to research/sales." | |
| elif "open" in query: | |
| result_text = "Please use the Finder to open files for now." | |
| else: | |
| result_text = "I am an Agent OS. I can **plot data**, **summarize text**, or help you navigate." | |
| return logs, result_text, result_img | |
| os_system = AgentSystem() | |
| # --- 2. CSS (No Inline Styles) --- | |
| CSS = """ | |
| @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); | |
| :root { --bg-desktop: url('https://images.unsplash.com/photo-1621574539436-4b7044e3756c?q=80&w=2832&auto=format&fit=crop'); } | |
| body { margin: 0; padding: 0; overflow: hidden !important; background: #000; } | |
| .gradio-container { width: 100vw !important; height: 100vh !important; padding: 0 !important; margin: 0 !important; background: var(--bg-desktop) center/cover no-repeat fixed !important; display: block !important; } | |
| footer { display: none !important; } | |
| #menubar { position: absolute; top: 0; left: 0; width: 100%; height: 28px; background: rgba(255,255,255,0.8); backdrop-filter: blur(10px); display: flex; align-items: center; padding: 0 15px; z-index: 9000; font-family: 'Inter'; font-size: 12px; font-weight: 600; } | |
| #desktop { position: absolute; top: 28px; bottom: 0; left: 0; right: 0; pointer-events: none; } | |
| .os-window { position: absolute !important; top: 50% !important; left: 50% !important; transform: translate(-50%, -50%) !important; width: 650px !important; height: 450px !important; background: rgba(255,255,255,0.95); border-radius: 10px; box-shadow: 0 25px 50px rgba(0,0,0,0.2); border: 1px solid rgba(255,255,255,0.4); display: flex; flex-direction: column; overflow: hidden; pointer-events: auto; z-index: 100; } | |
| .window-header { height: 28px; background: #f0f0f0; border-bottom: 1px solid #ddd; display: flex; align-items: center; padding: 0 10px; gap: 6px; } | |
| .btn-dot { width: 11px; height: 11px; border-radius: 50%; } | |
| .red { background: #ff5f57; border: 1px solid #e0443e; } | |
| .yellow { background: #ffbd2e; border: 1px solid #dea123; } | |
| .green { background: #28c940; border: 1px solid #1aab29; } | |
| .win-title { flex:1; text-align:center; font-size:12px; color:#666; font-weight:500; margin-right:50px;} | |
| .window-content { flex: 1; padding: 0; overflow: hidden; display: flex; flex-direction: column; } | |
| /* Specific styling for preview content area */ | |
| .preview-container { background: white; justify-content: center; align-items: center; } | |
| .finder-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 15px; padding: 20px; } | |
| .finder-item { display: flex; flex-direction: column; align-items: center; cursor: pointer; padding: 10px; border-radius: 5px; transition: background 0.2s; } | |
| .finder-item:hover { background: rgba(0,0,0,0.05); } | |
| #copilot { position: absolute; bottom: 100px; right: 30px; width: 340px; height: 500px; background: rgba(255,255,255,0.85); backdrop-filter: blur(20px); border-radius: 12px; border: 1px solid rgba(255,255,255,0.5); box-shadow: 0 10px 40px rgba(0,0,0,0.15); display: flex; flex-direction: column; z-index: 5000; pointer-events: auto; } | |
| .copilot-bg { background: transparent !important; } | |
| .copilot-header-row { background: #f9f9f9; } | |
| .copilot-input-row { padding: 10px; border-top: 1px solid #eee; background: white; border-radius: 0 0 12px 12px; } | |
| #dock-wrap { position: absolute; bottom: 20px; width: 100%; display: flex; justify-content: center; pointer-events: none; z-index: 6000; } | |
| #dock { background: rgba(255,255,255,0.3); backdrop-filter: blur(15px); padding: 8px 15px; border-radius: 16px; display: flex; gap: 12px; border: 1px solid rgba(255,255,255,0.2); pointer-events: auto; box-shadow: 0 5px 15px rgba(0,0,0,0.1); } | |
| .dock-icon { font-size: 24px; width: 44px; height: 44px; background: rgba(255,255,255,0.8); border-radius: 10px; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: transform 0.2s; border: none; } | |
| .dock-icon:hover { transform: scale(1.1) translateY(-5px); } | |
| """ | |
| # --- 3. Logic Functions --- | |
| def handle_file_click(file_name): | |
| data, msg = os_system.read_file(file_name) | |
| vis_finder = gr.update(visible=False) | |
| vis_preview = gr.update(visible=True) | |
| if data['type'] == 'image': | |
| return vis_finder, vis_preview, gr.update(value=None, visible=False), gr.update(value=f"### {file_name}\n(Image Content Placeholder)", visible=True), file_name | |
| elif data['type'] == 'csv': | |
| return vis_finder, vis_preview, gr.update(value=None, visible=False), gr.update(value=f"### {file_name}\n```csv\n{data['content']}\n```", visible=True), file_name | |
| else: | |
| return vis_finder, vis_preview, gr.update(value=None, visible=False), gr.update(value=f"### {file_name}\n{data['content']}", visible=True), file_name | |
| def agent_response(message, history, current_file): | |
| if not message: return "", history | |
| history.append({"role": "user", "content": message}) | |
| yield "", history | |
| logs, result_text, result_img = os_system.process_query(message, current_file) | |
| for log in logs: | |
| history.append({"role": "assistant", "content": log}) | |
| yield "", history | |
| time.sleep(0.2) | |
| history.append({"role": "assistant", "content": result_text}) | |
| yield "", history | |
| def agent_update_preview(message, current_file): | |
| if "plot" in message.lower() and current_file == "research_data.csv": | |
| img = os_system.generate_plot() | |
| return gr.update(visible=True, value=img), gr.update(visible=False) | |
| return gr.update(), gr.update() | |
| def show_finder(): return gr.update(visible=True), gr.update(visible=False), "" | |
| def close_all(): return gr.update(visible=False), gr.update(visible=False), "" | |
| # --- 4. UI Builder (Cleaned & Ordered) --- | |
| with gr.Blocks(css=CSS, theme=gr.themes.Base()) as demo: | |
| current_file_state = gr.State("") | |
| # Top Bar | |
| with gr.Column(elem_id="menubar"): | |
| gr.HTML(" <b>AgentOS</b> File Edit View Window Help") | |
| # Desktop Area | |
| with gr.Group(elem_id="desktop"): | |
| # 1. Finder Window | |
| with gr.Group(visible=False, elem_classes=["os-window"]) as win_finder: | |
| with gr.Row(elem_classes=["window-header"]): | |
| gr.HTML('<div class="btn-dot red"></div><div class="btn-dot yellow"></div><div class="btn-dot green"></div><div class="win-title">Finder</div>') | |
| with gr.Column(elem_classes=["window-content"]): | |
| gr.Markdown("### 📍 Desktop") | |
| with gr.Row(elem_classes=["finder-grid"]): | |
| finder_buttons = [] | |
| for fname, fmeta in os_system.file_system.items(): | |
| icon = "📊" if fmeta['type']=='csv' else "🖼️" if fmeta['type']=='image' else "📝" | |
| btn = gr.Button(f"{icon}\n{fname}", elem_classes=["finder-item"]) | |
| finder_buttons.append((btn, fname)) | |
| # 2. Preview Window (Fixed: Moved styles to CSS class 'preview-container') | |
| with gr.Group(visible=False, elem_classes=["os-window"]) as win_preview: | |
| with gr.Row(elem_classes=["window-header"]): | |
| gr.HTML('<div class="btn-dot red"></div><div class="btn-dot yellow"></div><div class="btn-dot green"></div><div class="win-title">Preview</div>') | |
| with gr.Column(elem_classes=["window-content", "preview-container"]): | |
| win_preview_img = gr.Image(visible=False, height=350, width=500, show_label=False, container=False) | |
| win_preview_text = gr.Markdown(visible=True, value="### No Content") | |
| # Copilot Chatbot | |
| with gr.Group(elem_id="copilot"): | |
| # Fixed: Moved styles to CSS class 'copilot-header-row' | |
| with gr.Row(elem_classes=["window-header", "copilot-header-row"]): | |
| gr.HTML('<div class="win-title" style="margin:0; text-align:left; padding-left:10px;">🤖 Agent Copilot</div>') | |
| chatbot = gr.Chatbot(elem_id="chatbot", elem_classes=["copilot-bg"], height=400, type="messages") | |
| # Fixed: Moved styles to CSS class 'copilot-input-row' | |
| with gr.Row(elem_classes=["copilot-input-row"]): | |
| msg_input = gr.Textbox(show_label=False, placeholder="Type 'plot graph'...", container=False, scale=4) | |
| send_btn = gr.Button("⬆", scale=1, size="sm", variant="primary") | |
| # Dock | |
| with gr.Group(elem_id="dock-wrap"): | |
| with gr.Row(elem_id="dock"): | |
| btn_finder = gr.Button("📂", elem_classes=["dock-icon"]) | |
| btn_reset = gr.Button("🏠", elem_classes=["dock-icon"]) | |
| # --- 5. Wiring Events --- | |
| # Finder Buttons | |
| for btn, fname in finder_buttons: | |
| btn.click( | |
| fn=handle_file_click, | |
| inputs=[gr.State(fname)], | |
| outputs=[win_finder, win_preview, win_preview_img, win_preview_text, current_file_state] | |
| ) | |
| # Chatbot | |
| msg_input.submit( | |
| agent_response, [msg_input, chatbot, current_file_state], [msg_input, chatbot] | |
| ).then( | |
| agent_update_preview, [msg_input, current_file_state], [win_preview_img, win_preview_text] | |
| ) | |
| send_btn.click( | |
| agent_response, [msg_input, chatbot, current_file_state], [msg_input, chatbot] | |
| ).then( | |
| agent_update_preview, [msg_input, current_file_state], [win_preview_img, win_preview_text] | |
| ) | |
| # Dock | |
| btn_finder.click(show_finder, outputs=[win_finder, win_preview, current_file_state]) | |
| btn_reset.click(close_all, outputs=[win_finder, win_preview, current_file_state]) | |
| if __name__ == "__main__": | |
| demo.launch() |