Spaces:
Running
Running
| <script lang="ts"> | |
| import Dataframe from "@gradio/dataframe"; | |
| import { onMount } from 'svelte'; | |
| interface Message { | |
| text: string; | |
| sender: "user" | "bot"; | |
| } | |
| let value = { | |
| data: [ | |
| ["Alice", 25, "Yes", "2024-01-15", "Engineer", "San Francisco", 85000, "Active"], | |
| ["Bob", 30, "No", "2024-02-20", "Designer", "New York", 75000, "Inactive"], | |
| ["Charlie", 35, "Yes", "2024-03-10", "Manager", "Chicago", 95000, "Active"], | |
| ["Diana", 28, "No", "2024-04-05", "Developer", "Austin", 80000, "Inactive"], | |
| ["Eve", 32, "Yes", "2024-05-12", "Analyst", "Seattle", 70000, "Active"], | |
| ["Frank", 29, "Yes", "2024-06-18", "Engineer", "Boston", 82000, "Active"], | |
| ["Grace", 27, "No", "2024-07-22", "Designer", "Portland", 68000, "Inactive"], | |
| ["Henry", 33, "Yes", "2024-08-30", "Manager", "Denver", 88000, "Active"], | |
| ["Ivy", 26, "Yes", "2024-09-14", "Developer", "Miami", 72000, "Active"], | |
| ["Jack", 31, "No", "2024-10-08", "Analyst", "Atlanta", 65000, "Inactive"], | |
| ["Kate", 34, "Yes", "2024-11-25", "Engineer", "Phoenix", 90000, "Active"], | |
| ["Liam", 28, "Yes", "2024-12-01", "Designer", "Dallas", 78000, "Active"], | |
| ["Maya", 29, "No", "2025-01-15", "Manager", "Houston", 92000, "Inactive"], | |
| ["Noah", 36, "Yes", "2025-02-20", "Developer", "Philadelphia", 85000, "Active"], | |
| ["Olivia", 27, "Yes", "2025-03-10", "Analyst", "Detroit", 70000, "Active"] | |
| ], | |
| headers: ["Name", "Age", "Active", "Join Date", "Role", "Location", "Salary", "Status"], | |
| metadata: null | |
| }; | |
| let datatype = ["str", "number", "str", "date", "str", "str", "number", "str"]; | |
| let messages: Message[] = []; | |
| let newMessage = "What is Alice's salary?"; | |
| let isGenerating = false; | |
| let generator: any = null; | |
| let isModelReady = false; | |
| onMount(async () => { | |
| messages = [...messages, { text: "π€ Model initializing...", sender: "bot" }]; | |
| await initializeModel(); | |
| }); | |
| async function initializeModel() { | |
| try { | |
| console.log('Initializing Gemma model...'); | |
| // Load the library in chunks to prevent blocking | |
| const transformersModule = await import('https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.7.1'); | |
| // Update loading message | |
| messages = [...messages, { text: "π¦ Loading model components...", sender: "bot" }]; | |
| // Small delay to allow UI update | |
| await new Promise(resolve => setTimeout(resolve, 100)); | |
| const { pipeline } = transformersModule; | |
| // Update loading message | |
| messages = [...messages, { text: "π Starting model...", sender: "bot" }]; | |
| // Small delay to allow UI update | |
| await new Promise(resolve => setTimeout(resolve, 100)); | |
| generator = await pipeline( | |
| 'text-generation', | |
| 'onnx-community/gemma-3-270m-it-ONNX', | |
| { dtype: 'fp32' } | |
| ); | |
| console.log('Model loaded successfully'); | |
| isModelReady = true; | |
| messages = [...messages, { text: "β Model ready! Ask me anything about the employee data.", sender: "bot" }]; | |
| } catch (error) { | |
| console.error('Failed to initialize model:', error); | |
| messages = [...messages, { text: "β οΈ Failed to load AI model. Please refresh the page to try again.", sender: "bot" }]; | |
| } | |
| } | |
| async function sendMessage() { | |
| if (newMessage.trim() && !isGenerating) { | |
| const userMessage = newMessage.trim(); | |
| messages = [...messages, { text: userMessage, sender: "user" }]; | |
| if (!isModelReady) { | |
| messages = [...messages, { text: "β³ Model is still initializing. Please wait...", sender: "bot" }]; | |
| newMessage = ""; | |
| return; | |
| } | |
| isGenerating = true; | |
| newMessage = ""; | |
| // Add a placeholder response that will be updated | |
| const placeholderIndex = messages.length; | |
| messages = [...messages, { text: "π€ Thinking...", sender: "bot" }]; | |
| // Use requestAnimationFrame to ensure UI updates before heavy processing | |
| requestAnimationFrame(async () => { | |
| try { | |
| const systemPrompt = `You are a helpful AI assistant that answers questions about employee data. Here's the current data: | |
| ${value.headers.join(' | ')} | |
| ${value.data.map(row => row.join(' | ')).join('\n')} | |
| Keep your responses concise and helpful. Answer questions about the data above.`; | |
| // Break the processing into smaller chunks to prevent blocking | |
| const processInChunks = async () => { | |
| const response = await generator([ | |
| { role: "system", content: systemPrompt }, | |
| { role: "user", content: userMessage } | |
| ], { | |
| max_new_tokens: 256, | |
| do_sample: true, | |
| temperature: 0.7, | |
| top_p: 0.9 | |
| }); | |
| const generatedContent = response[0].generated_text.at(-1).content; | |
| // Update the placeholder message | |
| messages = messages.map((msg, index) => | |
| index === placeholderIndex ? { text: generatedContent, sender: "bot" } : msg | |
| ); | |
| }; | |
| // Use a small delay to allow UI to update | |
| await new Promise(resolve => setTimeout(resolve, 50)); | |
| await processInChunks(); | |
| } catch (error) { | |
| console.error('Generation error:', error); | |
| // Update the placeholder message with error | |
| messages = messages.map((msg, index) => | |
| index === placeholderIndex ? { text: 'Sorry, I encountered an error. Please try again.', sender: "bot" } : msg | |
| ); | |
| } finally { | |
| isGenerating = false; | |
| } | |
| }); | |
| } | |
| } | |
| function handleKeyPress(event: KeyboardEvent) { | |
| if (event.key === "Enter") { | |
| sendMessage(); | |
| } | |
| } | |
| </script> | |
| <header class="page-header"> | |
| <h1>Data Chat</h1> | |
| <p>Ask questions about your employee table in real time</p> | |
| </header> | |
| <div class="container"> | |
| <div class="dataframe-section"> | |
| <Dataframe | |
| bind:value | |
| show_search="none" | |
| show_row_numbers={false} | |
| show_copy_button={false} | |
| show_fullscreen_button={false} | |
| editable={true} | |
| show_label={false} | |
| /> | |
| <div class="credits"> | |
| β€οΈ Built with Gemma 3 270M Onnx & Gradio's Dataframe.js | |
| </div> | |
| </div> | |
| <div class="chatbot-section"> | |
| <h2>Chatbot</h2> | |
| <div class="chat-messages"> | |
| {#each messages as message} | |
| <div class="message {message.sender}"> | |
| <div class="message-content">{message.text}</div> | |
| </div> | |
| {/each} | |
| </div> | |
| </div> | |
| </div> | |
| <div class="chat-input-container"> | |
| <div class="chat-input"> | |
| <input | |
| type="text" | |
| bind:value={newMessage} | |
| placeholder="What is Alice's salary?" | |
| on:keypress={handleKeyPress} | |
| aria-label="Chat input" | |
| aria-busy={isGenerating} | |
| disabled={!isModelReady || isGenerating} | |
| /> | |
| <button class="send-button" on:click={sendMessage} disabled={!isModelReady || isGenerating}> | |
| {#if isGenerating} | |
| <span class="spinner" aria-hidden="true"></span> | |
| <span>Thinking...</span> | |
| {:else} | |
| <span>Send</span> | |
| {/if} | |
| </button> | |
| </div> | |
| </div> | |
| <style> | |
| .container { | |
| display: flex; | |
| height: 100vh; | |
| width: 100vw; | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; | |
| } | |
| .dataframe-section { | |
| width: 80%; | |
| padding: 20px; | |
| border-right: 2px solid #e0e0e0; | |
| overflow: hidden; | |
| padding-bottom: 100px; | |
| } | |
| .credits { | |
| text-align: center; | |
| margin-top: 20px; | |
| padding: 10px; | |
| color: #666; | |
| font-size: 12px; | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; | |
| border-top: 1px solid #e0e0e0; | |
| background-color: #f8f9fa; | |
| border-radius: 0 0 8px 8px; | |
| } | |
| .chatbot-section { | |
| width: 20%; | |
| padding: 20px; | |
| display: flex; | |
| flex-direction: column; | |
| background-color: #f8f9fa; | |
| } | |
| .page-header { | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| padding: 24px 24px 0 24px; | |
| } | |
| .page-header h1 { | |
| margin: 0; | |
| font-size: 28px; | |
| letter-spacing: -0.02em; | |
| color: #0f172a; | |
| } | |
| h2 { | |
| margin: 0 0 20px 0; | |
| color: #333; | |
| font-size: 20px; | |
| font-weight: 600; | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; | |
| } | |
| .chat-messages { | |
| flex: 1; | |
| overflow-y: auto; | |
| margin-bottom: 20px; | |
| padding: 15px; | |
| background: white; | |
| border-radius: 12px; | |
| border: 1px solid #e1e5e9; | |
| box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); | |
| } | |
| .message { | |
| margin-bottom: 12px; | |
| } | |
| .message.user { | |
| text-align: right; | |
| } | |
| .message.bot { | |
| text-align: left; | |
| } | |
| .message-content { | |
| display: inline-block; | |
| padding: 10px 16px; | |
| border-radius: 20px; | |
| max-width: 85%; | |
| word-wrap: break-word; | |
| font-size: 14px; | |
| line-height: 1.4; | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; | |
| } | |
| .message.user .message-content { | |
| background-color: #007acc; | |
| color: white; | |
| font-weight: 500; | |
| } | |
| .message.bot .message-content { | |
| background-color: #f1f3f4; | |
| color: #202124; | |
| font-weight: 400; | |
| } | |
| .chat-input { | |
| display: flex; | |
| gap: 10px; | |
| } | |
| .chat-input input { | |
| flex: 1; | |
| padding: 12px 16px; | |
| border: 2px solid #e1e5e9; | |
| border-radius: 8px; | |
| font-size: 14px; | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; | |
| transition: border-color 0.2s ease; | |
| } | |
| .chat-input input:focus { | |
| outline: none; | |
| border-color: #007acc; | |
| } | |
| .chat-input button { | |
| padding: 12px 20px; | |
| background-color: #007acc; | |
| color: white; | |
| border: none; | |
| border-radius: 8px; | |
| cursor: pointer; | |
| font-size: 14px; | |
| font-weight: 500; | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; | |
| transition: background-color 0.2s ease; | |
| } | |
| .chat-input button:hover { | |
| background-color: #005a9e; | |
| } | |
| .chat-input-container { | |
| position: fixed; | |
| bottom: 0; | |
| left: 0; | |
| right: 0; | |
| background: white; | |
| border-top: 1px solid #e1e5e9; | |
| padding: 20px; | |
| box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05); | |
| } | |
| .chat-input { | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| display: flex; | |
| gap: 10px; | |
| } | |
| </style> | |