VirtualOasis commited on
Commit
b914d47
·
verified ·
1 Parent(s): 179e56a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +136 -120
app.py CHANGED
@@ -414,158 +414,174 @@ def build_kg(url_or_text):
414
  url_or_text: URL to analyze or direct text input
415
 
416
  Returns:
417
- String: JSON containing entities, relationships, and simple diagram
418
  """
419
  try:
 
420
  if not url_or_text or len(url_or_text.strip()) == 0:
421
- return json.dumps({"error": "Please provide some text or a URL to analyze."}, ensure_ascii=True)
422
 
423
- # Step 1: Fetch content
424
- content = fetch_content(url_or_text)
425
 
426
- if content.startswith("Error"):
427
- return json.dumps({"error": content}, ensure_ascii=True)
428
-
429
- # Step 2: Extract entities
430
- entities_data = extract_entities(content)
431
-
432
- # Step 3: Create simple ASCII diagram (truncated for MCP)
433
- entities = entities_data.get("entities", [])
434
- relationships = entities_data.get("relationships", [])
435
-
436
- # Limit diagram size for MCP compatibility
437
- limited_entities = entities[:8] # Reduce from 10 to 8
438
- limited_relationships = relationships[:6] # Reduce from 8 to 6
439
- ascii_diagram = build_ascii_diagram(limited_entities, limited_relationships)
440
-
441
- # Truncate diagram if too long
442
- if len(ascii_diagram) > 1000:
443
- ascii_diagram = ascii_diagram[:950] + "\n... (truncated for MCP compatibility)"
444
-
445
- # Step 4: Create summary (more concise for MCP)
446
- num_entities = len(entities)
447
- num_relationships = len(relationships)
448
-
449
- summary = f"Knowledge Graph Analysis Complete!\n\nStats: {num_entities} entities, {num_relationships} relationships\n\nTop Entities:"
450
-
451
- for entity in entities[:3]: # Show only first 3 for MCP
452
- name = entity.get('name', 'Unknown')
453
- entity_type = entity.get('type', 'UNKNOWN')
454
- summary += f"\n• {name} ({entity_type})"
455
 
456
- if len(entities) > 3:
457
- summary += f"\n... and {len(entities) - 3} more"
458
 
459
- # Create MCP-friendly response with size limits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
460
  response = {
461
  "success": True,
462
- "entities": entities[:15], # Limit entities for MCP
463
- "relationships": relationships[:10], # Limit relationships for MCP
464
- "diagram": ascii_diagram,
465
- "summary": summary,
466
- "stats": {
467
- "entity_count": num_entities,
468
- "relationship_count": num_relationships,
469
- "content_length": min(len(content), 5000) # Cap reported length
470
- }
471
  }
472
 
473
- # Validate and sanitize response for MCP compatibility
474
- validated_response = validate_mcp_response(response)
475
-
476
- # Return compact JSON
477
- return json.dumps(validated_response, ensure_ascii=True, separators=(',', ':'))
478
 
479
  except Exception as e:
480
- error_msg = f"Analysis failed: {str(e)}"
481
- return json.dumps({"success": False, "error": error_msg}, ensure_ascii=True, separators=(',', ':'))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
482
 
483
  # Create Gradio interface with error handling
484
  try:
485
  demo = gr.Interface(
486
- fn=build_kg,
487
- inputs="text",
488
- outputs="text",
489
- title="🧠 KG Builder",
490
- description="Transform text or URLs into knowledge graphs for AI agents.",
491
- allow_flagging="never"
 
 
 
 
 
 
 
 
492
  )
493
 
494
  except Exception as e:
495
  print(f"Failed to create Gradio interface: {e}")
496
- # Create a simple fallback interface
497
- def simple_demo(text):
498
- return json.dumps({"error": f"Startup failed: {str(e)}"}), None, "Application failed to start properly."
499
-
500
- try:
501
- demo = gr.Interface(
502
- fn=simple_demo,
503
- inputs=[gr.Textbox(label="Input", placeholder="Enter text...")],
504
- outputs=[
505
- gr.JSON(label="Error Output"),
506
- gr.Image(label="No Image", type="filepath"),
507
- gr.Markdown(label="Error Message")
508
- ],
509
- title="⚠️ KG Builder - Startup Error",
510
- allow_flagging="never",
511
- cache_examples=False
512
- )
513
- except Exception as e2:
514
- # Ultimate fallback - minimal interface
515
- def minimal_demo(text):
516
- return {"error": str(e2)}
517
-
518
- demo = gr.Interface(
519
- fn=minimal_demo,
520
- inputs="text",
521
- outputs="json",
522
- title="KG Builder Error"
523
- )
524
 
525
  # Launch the demo
526
  if __name__ == "__main__":
 
 
527
  try:
528
- # Check if running in HuggingFace Spaces or other cloud environment
529
- is_hf_space = os.environ.get("SPACE_ID") is not None
530
-
531
- if is_hf_space:
532
- # HuggingFace Spaces configuration
533
- demo.launch(
534
- mcp_server=True,
535
- share=False,
536
- show_error=True,
537
- quiet=False,
538
- server_name="0.0.0.0", # Allow external connections
539
- server_port=7860 # Standard HF port
540
- )
541
- else:
542
- # Local development configuration
543
- demo.launch(
544
- mcp_server=True,
545
- share=False,
546
- show_error=True,
547
- quiet=False
548
- )
549
  except Exception as e:
550
- print(f"MCP Launch failed: {e}")
551
- # Try without MCP server as fallback
552
  try:
 
553
  demo.launch(
554
  mcp_server=False,
555
  share=False,
556
- show_error=True,
557
- quiet=True
558
  )
559
  except Exception as e2:
560
- print(f"Complete failure: {e2}")
561
- # Create minimal working demo
562
- def error_demo(text):
563
- return json.dumps({"error": f"Server startup failed: {str(e2)}"}, ensure_ascii=True)
 
 
564
 
565
- minimal_demo = gr.Interface(
566
- fn=error_demo,
567
- inputs="text",
568
  outputs="text",
569
- title="KG Builder - Error Mode"
570
  )
571
- minimal_demo.launch(share=False, quiet=True)
 
414
  url_or_text: URL to analyze or direct text input
415
 
416
  Returns:
417
+ String: Simple JSON response optimized for MCP streaming
418
  """
419
  try:
420
+ # Quick validation
421
  if not url_or_text or len(url_or_text.strip()) == 0:
422
+ return '{"error":"Please provide text or URL to analyze"}'
423
 
424
+ # Limit input size immediately to prevent timeouts
425
+ input_text = url_or_text[:2000] if len(url_or_text) > 2000 else url_or_text
426
 
427
+ # Step 1: Fetch content (with timeout protection)
428
+ try:
429
+ content = fetch_content(input_text)
430
+ if content.startswith("Error"):
431
+ return f'{{"error":"{content}"}}'
432
+ except Exception:
433
+ content = input_text # Use input directly if fetch fails
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
434
 
435
+ # Limit content size for fast processing
436
+ content = content[:1500] if len(content) > 1500 else content
437
 
438
+ # Step 2: Quick entity extraction (simplified for speed)
439
+ try:
440
+ entities_data = simple_entity_extraction(content) # Always use simple extraction for MCP
441
+ except Exception:
442
+ entities_data = {"entities": [], "relationships": []}
443
+
444
+ # Step 3: Minimal response
445
+ entities = entities_data.get("entities", [])[:5] # Max 5 entities
446
+ relationships = entities_data.get("relationships", [])[:3] # Max 3 relationships
447
+
448
+ # Create minimal ASCII summary
449
+ diagram_parts = []
450
+ if entities:
451
+ diagram_parts.append("ENTITIES:")
452
+ for entity in entities:
453
+ name = str(entity.get("name", "Unknown"))[:20] # Truncate names
454
+ diagram_parts.append(f" - {name}")
455
+
456
+ if relationships:
457
+ diagram_parts.append("RELATIONSHIPS:")
458
+ for rel in relationships:
459
+ source = str(rel.get("source", ""))[:15]
460
+ target = str(rel.get("target", ""))[:15]
461
+ diagram_parts.append(f" {source} -> {target}")
462
+
463
+ diagram = "\n".join(diagram_parts) if diagram_parts else "No entities found"
464
+
465
+ # Ultra-minimal response
466
  response = {
467
  "success": True,
468
+ "entity_count": len(entities),
469
+ "relationship_count": len(relationships),
470
+ "entities": [{"name": e.get("name", "")[:20], "type": e.get("type", "UNKNOWN")} for e in entities],
471
+ "relationships": [{"source": r.get("source", "")[:15], "target": r.get("target", "")[:15]} for r in relationships],
472
+ "diagram": diagram[:500] # Strict limit
 
 
 
 
473
  }
474
 
475
+ # Return ultra-compact JSON
476
+ return json.dumps(response, separators=(',', ':'))[:2000] # Hard size limit
 
 
 
477
 
478
  except Exception as e:
479
+ # Ultra-simple error response
480
+ error_msg = str(e)[:100] # Truncate error message
481
+ return f'{{"success":false,"error":"{error_msg}"}}'
482
+
483
+ # Wrapper function with timeout protection for MCP
484
+ def mcp_safe_build_kg(url_or_text):
485
+ """MCP-safe wrapper with timeout protection"""
486
+ try:
487
+ import signal
488
+ import functools
489
+
490
+ def timeout_handler(signum, frame):
491
+ raise TimeoutError("Function timed out")
492
+
493
+ # Set timeout for 10 seconds
494
+ signal.signal(signal.SIGALRM, timeout_handler)
495
+ signal.alarm(10)
496
+
497
+ try:
498
+ result = build_kg(url_or_text)
499
+ signal.alarm(0) # Cancel timeout
500
+ return result
501
+ except TimeoutError:
502
+ return '{"success":false,"error":"Request timed out"}'
503
+ except Exception as e:
504
+ signal.alarm(0) # Cancel timeout
505
+ return f'{{"success":false,"error":"Function error: {str(e)[:50]}"}}'
506
+
507
+ except Exception:
508
+ # Fallback if signal not available (Windows, etc.)
509
+ try:
510
+ return build_kg(url_or_text)
511
+ except Exception as e:
512
+ return f'{{"success":false,"error":"Fallback error: {str(e)[:50]}"}}'
513
 
514
  # Create Gradio interface with error handling
515
  try:
516
  demo = gr.Interface(
517
+ fn=mcp_safe_build_kg, # Use the timeout-protected version
518
+ inputs=gr.Textbox(
519
+ label="Input Text or URL",
520
+ placeholder="Enter text to analyze or paste a URL...",
521
+ max_lines=5
522
+ ),
523
+ outputs=gr.Textbox(
524
+ label="Knowledge Graph JSON",
525
+ show_copy_button=True
526
+ ),
527
+ title="KG Builder - MCP Edition",
528
+ description="Lightweight knowledge graph builder optimized for MCP servers.",
529
+ allow_flagging="never",
530
+ cache_examples=False
531
  )
532
 
533
  except Exception as e:
534
  print(f"Failed to create Gradio interface: {e}")
535
+ # Create minimal fallback
536
+ def error_demo(text):
537
+ return f'{{"error":"Interface creation failed: {str(e)[:100]}"}}'
538
+
539
+ demo = gr.Interface(
540
+ fn=error_demo,
541
+ inputs="text",
542
+ outputs="text",
543
+ title="KG Builder - Error Mode",
544
+ allow_flagging="never"
545
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
546
 
547
  # Launch the demo
548
  if __name__ == "__main__":
549
+ print("Starting KG Builder MCP Server...")
550
+
551
  try:
552
+ demo.launch(
553
+ mcp_server=True,
554
+ share=False,
555
+ show_error=False, # Reduce error verbosity for MCP
556
+ quiet=True, # Reduce logging to prevent SSE issues
557
+ server_name="0.0.0.0",
558
+ server_port=7860,
559
+ max_threads=1, # Limit concurrency to prevent resource issues
560
+ show_api=False # Disable API docs to reduce overhead
561
+ )
 
 
 
 
 
 
 
 
 
 
 
562
  except Exception as e:
563
+ print(f"MCP server launch failed: {e}")
564
+ print("Trying fallback mode...")
565
  try:
566
+ # Fallback without MCP
567
  demo.launch(
568
  mcp_server=False,
569
  share=False,
570
+ quiet=True,
571
+ show_error=False
572
  )
573
  except Exception as e2:
574
+ print(f"All launch attempts failed: {e2}")
575
+ print("Creating emergency fallback...")
576
+
577
+ # Create absolute minimal demo
578
+ def emergency_demo(text):
579
+ return '{"error":"Server in emergency mode"}'
580
 
581
+ emergency = gr.Interface(
582
+ fn=emergency_demo,
583
+ inputs="text",
584
  outputs="text",
585
+ title="KG Builder Emergency Mode"
586
  )
587
+ emergency.launch(quiet=True, share=False)