Team Ai
Apppublic

build-small-hackathon/Structured-Data-Rescuer

sourceHugging Faceapache-2.0updated 4mo agoView on Hugging Face
2likes
app.py342 linesDownload Raw Back to root
1import gradio as gr
2import json
3import os
4import csv
5import tempfile
6from huggingface_hub import InferenceClient
7
8# Replace this with your exact model repo ID
9MODEL_ID = "meta-llama/Llama-3.1-8B-Instruct" 
10
11# Securely load the Hugging Face token from Space secrets
12hf_token = os.environ.get("HF_TOKEN")
13
14# Initialize the HF inference client with the token
15client = InferenceClient(model=MODEL_ID, token=hf_token)
16
17# -------------------------
18# Custom CSS Styling
19# -------------------------
20custom_css = """
21.hero-container {
22    background: linear-gradient(135deg, #6366f1 0%, #14b8a6 100%);
23    padding: 2.5rem;
24    border-radius: 20px;
25    color: white;
26    margin-bottom: 2rem;
27    box-shadow: 0 10px 25px -5px rgba(99, 102, 241, 0.2);
28}
29.hero-container h1 {
30    color: white !important;
31    font-size: 2.5rem !important;
32    font-weight: 800 !important;
33    margin-bottom: 0.5rem;
34    text-shadow: 0 2px 4px rgba(0,0,0,0.1);
35}
36.hero-container p {
37    color: rgba(255, 255, 255, 0.9) !important;
38    font-size: 1.1rem !important;
39}
40.primary-btn {
41    background: linear-gradient(90deg, #6366f1 0%, #14b8a6 100%) !important;
42    border: none !important;
43    color: white !important;
44    font-weight: 600 !important;
45    border-radius: 10px !important;
46    transition: all 0.3s ease !important;
47    padding: 12px 24px !important;
48}
49.primary-btn:hover {
50    transform: translateY(-2px);
51    box-shadow: 0 8px 20px -5px rgba(99, 102, 241, 0.4);
52}
53.secondary-btn {
54    border-radius: 10px !important;
55    font-weight: 600 !important;
56}
57.feedback-card {
58    border-left: 4px solid #6366f1;
59    background-color: rgba(99, 102, 241, 0.05);
60}
61"""
62
63# -------------------------
64# Helper & Extraction Logic
65# -------------------------
66def generate_kpi_html(structured_data):
67    """Generates modern, responsive KPI metrics cards dynamically based on JSON data."""
68    if not structured_data or "error" in structured_data:
69        return """
70        <div style='display: flex; justify-content: center; align-items: center; height: 100px; border: 2px dashed var(--border-color-primary, #e5e7eb); border-radius: 12px; color: var(--text-color-subdued, #9ca3af);'>
71            Await extraction to generate KPI metrics...
72        </div>
73        """
74    
75    cards_html = ""
76    if isinstance(structured_data, dict):
77        # Pick the top 4 attributes to show as metrics
78        items = list(structured_data.items())[:4]
79        for key, val in items:
80            # Clean up the key label
81            display_key = str(key).replace("_", " ").replace("-", " ").title()
82            
83            # Format list value representation
84            if isinstance(val, list):
85                display_val = ", ".join(map(str, val))
86            else:
87                display_val = str(val)
88            
89            # Truncate if string is too long for the card layout
90            if len(display_val) > 40:
91                display_val = display_val[:37] + "..."
92                
93            # Dynamic highlight accents based on field types
94            accent_color = "#6366f1" # default Indigo
95            if any(x in display_key.lower() for x in ["price", "total", "amount", "cost", "revenue", "budget"]):
96                accent_color = "#10b981" # Emerald for cash/costs
97            elif any(x in display_key.lower() for x in ["date", "deadline", "due", "time"]):
98                accent_color = "#f59e0b" # Amber for dates/reminders
99            elif any(x in display_key.lower() for x in ["status", "priority", "importance"]):
100                accent_color = "#ef4444" # Crimson for status/alerts
101                
102            cards_html += f"""
103            <div style='background: var(--body-background-fill, #ffffff); padding: 1rem; border-radius: 12px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05); border: 1px solid var(--border-color-primary, #e5e7eb); border-left: 5px solid {accent_color}; min-width: 140px; flex: 1;'>
104                <div style='font-size: 0.7rem; color: var(--text-color-subdued, #6b7280); text-transform: uppercase; font-weight: 700; letter-spacing: 0.05em; margin-bottom: 0.25rem;'>{display_key}</div>
105                <div style='font-size: 1.05rem; color: var(--body-text-color, #111827); font-weight: 800; word-break: break-word;'>{display_val}</div>
106            </div>
107            """
108    elif isinstance(structured_data, list):
109        # Summary KPI for array data structures
110        cards_html = f"""
111        <div style='background: var(--body-background-fill, #ffffff); padding: 1rem; border-radius: 12px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05); border: 1px solid var(--border-color-primary, #e5e7eb); border-left: 5px solid #6366f1; min-width: 140px; flex: 1;'>
112            <div style='font-size: 0.7rem; color: var(--text-color-subdued, #6b7280); text-transform: uppercase; font-weight: 700; letter-spacing: 0.05em; margin-bottom: 0.25rem;'>Total Records Found</div>
113            <div style='font-size: 1.5rem; color: var(--body-text-color, #111827); font-weight: 800;'>{len(structured_data)}</div>
114        </div>
115        """
116        
117    return f"""
118    <div style='display: flex; flex-wrap: wrap; gap: 0.75rem; margin-bottom: 1rem; width: 100%;'>
119        {cards_html}
120    </div>
121    """
122
123def extract_data(raw_text, fields_to_extract):
124    if not hf_token:
125        err_state = {"error": "HF_TOKEN secret is missing. Please add your Hugging Face Access Token to the Space Secrets."}
126        return err_state, [["Error", "HF_TOKEN missing"]], generate_kpi_html(err_state)
127        
128    if not raw_text.strip() or not fields_to_extract.strip():
129        err_state = {"error": "Please provide both raw text and fields to extract."}
130        return err_state, [["Error", "Incomplete inputs"]], generate_kpi_html(err_state)
131
132    # Construct the system instruction
133    system_prompt = (
134        "You are an expert data extraction assistant. Your job is to extract specific "
135        "information from messy, unstructured text and output it as clean, valid JSON.\n"
136        "Rules:\n"
137        "1. Only extract the fields requested.\n"
138        "2. If a field is not found in the text, return 'null' for that field.\n"
139        "3. Output ONLY a raw JSON object. Do not include markdown formatting, backticks, or conversational text."
140    )
141
142    user_prompt = f"Fields to extract:\n{fields_to_extract}\n\nUnstructured Text:\n{raw_text}"
143
144    messages = [
145        {"role": "system", "content": system_prompt},
146        {"role": "user", "content": user_prompt}
147    ]
148
149    try:
150        # Call the model via the chat completion API
151        response = client.chat_completion(
152            messages=messages,
153            max_tokens=1024,
154            temperature=0.1, 
155        )
156        
157        output_text = response.choices[0].message.content.strip()
158
159        # Fallback: Safely strip markdown code blocks without regular expressions
160        cleaned_text = output_text
161        if cleaned_text.startswith("```"):
162            lines = cleaned_text.splitlines()
163            if len(lines) >= 2:
164                if lines[0].startswith("```"):
165                    lines = lines[1:]
166                if lines and lines[-1].strip() == "```":
167                    lines = lines[:-1]
168                cleaned_text = "\n".join(lines).strip()
169
170        # Parse the text into an actual JSON dictionary
171        structured_data = json.loads(cleaned_text)
172        
173        # Convert JSON structure to a displayable 2D list for the Table view
174        table_data = []
175        if isinstance(structured_data, dict):
176            for k, v in structured_data.items():
177                val_str = ", ".join(map(str, v)) if isinstance(v, list) else str(v)
178                table_data.append([k, val_str])
179        elif isinstance(structured_data, list):
180            for idx, item in enumerate(structured_data):
181                table_data.append([f"Item {idx + 1}", str(item)])
182                
183        return structured_data, table_data, generate_kpi_html(structured_data)
184
185    except json.JSONDecodeError:
186        error_dict = {
187            "error": "The model failed to return valid JSON. It returned this instead:",
188            "raw_output": output_text
189        }
190        return error_dict, [["Error", "Invalid JSON parsed"]], generate_kpi_html(error_dict)
191    except Exception as e:
192        error_msg = str(e)
193        if "model_not_found" in error_msg or "does not exist" in error_msg:
194            err_dict = {
195                "error": f"The model '{MODEL_ID}' was not found on Hugging Face.",
196                "troubleshooting": [
197                    "1. Check your Hugging Face repo for typos (case-sensitive).",
198                    "2. Verify HF_TOKEN secret read permissions.",
199                    "3. GGUF or LoRA adapter models are not directly supported by the Serverless API."
200                ]
201            }
202            return err_dict, [["Connection Error", "Model Not Found"]], generate_kpi_html(err_dict)
203        err_state = {"error": error_msg}
204        return err_state, [["Error", error_msg]], generate_kpi_html(err_state)
205
206def generate_csv(json_data):
207    """Converts the JSON output into a downloadable CSV file."""
208    if not json_data or "error" in json_data:
209        return None
210    
211    if isinstance(json_data, dict):
212        data_list = [json_data]
213    elif isinstance(json_data, list):
214        data_list = json_data
215    else:
216        return None
217
218    # Create a secure temporary file to hold the CSV
219    temp_dir = tempfile.mkdtemp()
220    csv_path = os.path.join(temp_dir, "extracted_data.csv")
221    
222    try:
223        with open(csv_path, 'w', newline='', encoding='utf-8') as f:
224            headers = set()
225            for item in data_list:
226                if isinstance(item, dict):
227                    headers.update(item.keys())
228            headers = list(headers)
229            
230            if not headers:
231                return None
232
233            writer = csv.DictWriter(f, fieldnames=headers)
234            writer.writeheader()
235            
236            for item in data_list:
237                if isinstance(item, dict):
238                    flat_item = {k: (str(v) if isinstance(v, (list, dict)) else v) for k, v in item.items()}
239                    writer.writerow(flat_item)
240        
241        return csv_path
242    except Exception as e:
243        return None
244
245# -------------------------
246# Build the Gradio UI
247# -------------------------
248with gr.Blocks(theme=gr.themes.Soft(), css=custom_css) as demo:
249    
250    # Styled Header Block
251    with gr.HTML(elem_classes="hero-container"):
252        gr.Markdown(
253            f"""
254            # ๐Ÿ›Ÿ The Data Rescuer
255            Turn messy logs, disorganized lists, automated transcripts, and raw OCR scripts into highly structured business-ready assets โ€” powered by `{MODEL_ID}`.
256            """
257        )
258    
259    with gr.Row():
260        # Left Column: Inputs
261        with gr.Column(scale=1):
262            raw_input = gr.Textbox(
263                label="1. Paste Unstructured Text",
264                placeholder="Paste your messy meeting notes, emails, or raw text here...",
265                lines=12
266            )
267            
268            schema_input = gr.Textbox(
269                label="2. What fields do you want to extract?",
270                placeholder="e.g., Company Name, Contact Person, Deadline, Action Items (list)",
271                lines=3
272            )
273            
274            extract_btn = gr.Button("๐Ÿš€ Extract Structured Data", variant="primary", elem_classes="primary-btn")
275            
276        # Right Column: Multi-view Output Panels
277        with gr.Column(scale=1):
278            # Dynamic HTML summary cards (Dashboard metrics style)
279            kpi_output = gr.HTML(
280                value="""
281                <div style='display: flex; justify-content: center; align-items: center; height: 100px; border: 2px dashed var(--border-color-primary, #e5e7eb); border-radius: 12px; color: var(--text-color-subdued, #9ca3af);'>
282                    Await extraction to generate KPI metrics...
283                </div>
284                """
285            )
286            
287            with gr.Tabs():
288                with gr.TabItem("๐Ÿ“Š Structured Table"):
289                    table_output = gr.Dataframe(
290                        headers=["Field Name", "Extracted Value"],
291                        datatype=["str", "str"],
292                        interactive=False,
293                        wrap=True
294                    )
295                with gr.TabItem("๐Ÿ” Raw JSON Tree"):
296                    json_output = gr.JSON(label="JSON Object")
297            
298            # Action controls below outputs
299            with gr.Row():
300                export_btn = gr.Button("๐Ÿ’พ Build Export File", variant="secondary", elem_classes="secondary-btn")
301                csv_output = gr.File(label="Ready for Download", interactive=False)
302
303    # -------------------------
304    # Examples Panel
305    # -------------------------
306    gr.Markdown("### Try it out with these examples:")
307    gr.Examples(
308        examples=[
309            [
310                "Hey guys, quick recap of today's sync. Sarah is going to handle the frontend React components by next Tuesday. John, you need to fix the database migration issue before Friday. Also, our client 'Acme Corp' wants the final delivery by October 15th.", 
311                "Task Owner, Task Description, Deadline, Client Name"
312            ],
313            [
314                "Invoice #99214. From: BlueTech Software. To: Jane Doe. Items: 1x Server Maintenance ($500), 2x Cloud Storage ($100 each). Total due: $700. Please pay by end of month.", 
315                "Invoice Number, Sender, Recipient, Items (list of names and prices), Total Amount"
316            ]
317        ],
318        inputs=[raw_input, schema_input],
319        label="Click an example to populate the inputs"
320    )
321
322    # -------------------------
323    # Event Connections
324    # -------------------------
325    # 1. Connect extraction button to the Table View, JSON Tree, and KPI output
326    extract_btn.click(
327        fn=extract_data,
328        inputs=[raw_input, schema_input],
329        outputs=[json_output, table_output, kpi_output]
330    )
331    
332    # 2. Connect CSV generation
333    export_btn.click(
334        fn=generate_csv,
335        inputs=[json_output],
336        outputs=[csv_output]
337    )
338
339# Launch the app
340if __name__ == "__main__":
341    demo.launch()
342