Spaces:
Sleeping
Sleeping
Update app.py
Browse files
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
|
418 |
"""
|
419 |
try:
|
|
|
420 |
if not url_or_text or len(url_or_text.strip()) == 0:
|
421 |
-
return
|
422 |
|
423 |
-
#
|
424 |
-
|
425 |
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
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 |
-
|
457 |
-
|
458 |
|
459 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
460 |
response = {
|
461 |
"success": True,
|
462 |
-
"
|
463 |
-
"
|
464 |
-
"
|
465 |
-
"
|
466 |
-
"
|
467 |
-
"entity_count": num_entities,
|
468 |
-
"relationship_count": num_relationships,
|
469 |
-
"content_length": min(len(content), 5000) # Cap reported length
|
470 |
-
}
|
471 |
}
|
472 |
|
473 |
-
#
|
474 |
-
|
475 |
-
|
476 |
-
# Return compact JSON
|
477 |
-
return json.dumps(validated_response, ensure_ascii=True, separators=(',', ':'))
|
478 |
|
479 |
except Exception as e:
|
480 |
-
|
481 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
482 |
|
483 |
# Create Gradio interface with error handling
|
484 |
try:
|
485 |
demo = gr.Interface(
|
486 |
-
fn=
|
487 |
-
inputs=
|
488 |
-
|
489 |
-
|
490 |
-
|
491 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
492 |
)
|
493 |
|
494 |
except Exception as e:
|
495 |
print(f"Failed to create Gradio interface: {e}")
|
496 |
-
# Create
|
497 |
-
def
|
498 |
-
return
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
|
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 |
-
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
#
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
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
|
551 |
-
|
552 |
try:
|
|
|
553 |
demo.launch(
|
554 |
mcp_server=False,
|
555 |
share=False,
|
556 |
-
|
557 |
-
|
558 |
)
|
559 |
except Exception as e2:
|
560 |
-
print(f"
|
561 |
-
|
562 |
-
|
563 |
-
|
|
|
|
|
564 |
|
565 |
-
|
566 |
-
fn=
|
567 |
-
inputs="text",
|
568 |
outputs="text",
|
569 |
-
title="KG Builder
|
570 |
)
|
571 |
-
|
|
|
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)
|