Commit
·
5f8582c
1
Parent(s):
547246f
fix list entities by type bug
Browse files- gradio_mcp_space.py +52 -25
gradio_mcp_space.py
CHANGED
|
@@ -433,22 +433,24 @@ Edge Relations:
|
|
| 433 |
except Exception as e:
|
| 434 |
return f"Error: {str(e)}"
|
| 435 |
|
| 436 |
-
|
| 437 |
@observe(as_type="tool")
|
| 438 |
def list_nodes_by_type(node_type: str, limit: int = 20, page: int = 1) -> str:
|
| 439 |
"""
|
| 440 |
List nodes of a specific type in the knowledge graph.
|
| 441 |
-
|
|
|
|
|
|
|
|
|
|
| 442 |
Args:
|
| 443 |
node_type: The type of nodes to list (e.g., 'function', 'class', 'file')
|
| 444 |
limit: Maximum number of nodes to return (default: 20)
|
| 445 |
-
|
| 446 |
Returns:
|
| 447 |
str: A formatted string with matching nodes
|
| 448 |
"""
|
| 449 |
if knowledge_graph is None:
|
| 450 |
return "Error: Knowledge graph not initialized"
|
| 451 |
-
|
| 452 |
try:
|
| 453 |
# Convert limit/page to int if they're strings (MCP/Gradio may pass strings)
|
| 454 |
if isinstance(limit, str):
|
|
@@ -456,52 +458,77 @@ def list_nodes_by_type(node_type: str, limit: int = 20, page: int = 1) -> str:
|
|
| 456 |
limit = int(limit)
|
| 457 |
except ValueError:
|
| 458 |
return f"Error: 'limit' must be an integer, got '{limit}'"
|
| 459 |
-
|
| 460 |
if isinstance(page, str):
|
| 461 |
try:
|
| 462 |
page = int(page)
|
| 463 |
except ValueError:
|
| 464 |
return f"Error: 'page' must be an integer, got '{page}'"
|
| 465 |
-
|
| 466 |
if limit <= 0:
|
| 467 |
return "Error: limit must be a positive integer"
|
| 468 |
if page < 1:
|
| 469 |
return "Error: 'page' must be a positive integer (1 or greater)"
|
| 470 |
-
|
| 471 |
g = knowledge_graph.graph
|
| 472 |
-
matching_nodes = [
|
| 473 |
-
|
| 474 |
-
|
| 475 |
-
|
| 476 |
-
|
| 477 |
-
|
| 478 |
-
|
| 479 |
-
|
| 480 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 481 |
total = len(matching_nodes)
|
| 482 |
if total == 0:
|
| 483 |
return f"No nodes found of type '{node_type}'."
|
| 484 |
-
|
| 485 |
# Pagination
|
| 486 |
total_pages = (total + limit - 1) // limit
|
| 487 |
if page > total_pages:
|
| 488 |
return f"Error: Page {page} does not exist. Total pages: {total_pages} (with {total} nodes at {limit} per page)"
|
| 489 |
-
|
| 490 |
start_idx = (page - 1) * limit
|
| 491 |
end_idx = start_idx + limit
|
| 492 |
page_slice = matching_nodes[start_idx:end_idx]
|
| 493 |
-
|
| 494 |
result = f"Nodes of type '{node_type}' (Page {page}/{total_pages}, {total} total):\n"
|
| 495 |
result += "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n"
|
| 496 |
-
|
| 497 |
for i, node in enumerate(page_slice, start=start_idx + 1):
|
| 498 |
result += f"{i}. {node['name']}\n"
|
| 499 |
-
result += f" ID: {node['id']}\n
|
| 500 |
-
|
|
|
|
| 501 |
# Pagination hint
|
| 502 |
if page < total_pages:
|
| 503 |
result += f"Use page={page + 1} to see the next page\n"
|
| 504 |
-
|
| 505 |
return result
|
| 506 |
except Exception as e:
|
| 507 |
return f"Error: {str(e)}"
|
|
@@ -2043,7 +2070,7 @@ def create_gradio_app():
|
|
| 2043 |
with gr.Row():
|
| 2044 |
with gr.Column():
|
| 2045 |
node_type_input = gr.Dropdown(
|
| 2046 |
-
choices=["file", "directory", "chunk", "
|
| 2047 |
label="Node Type"
|
| 2048 |
)
|
| 2049 |
type_limit = gr.Slider(1, 100, value=20, step=1, label="Max Results")
|
|
@@ -2059,7 +2086,7 @@ def create_gradio_app():
|
|
| 2059 |
with gr.Row():
|
| 2060 |
with gr.Column():
|
| 2061 |
search_type = gr.Dropdown(
|
| 2062 |
-
choices=["file", "directory", "chunk", "
|
| 2063 |
label="Node Type"
|
| 2064 |
)
|
| 2065 |
search_name = gr.Textbox(label="Name Contains", placeholder="Enter partial name...")
|
|
|
|
| 433 |
except Exception as e:
|
| 434 |
return f"Error: {str(e)}"
|
| 435 |
|
|
|
|
| 436 |
@observe(as_type="tool")
|
| 437 |
def list_nodes_by_type(node_type: str, limit: int = 20, page: int = 1) -> str:
|
| 438 |
"""
|
| 439 |
List nodes of a specific type in the knowledge graph.
|
| 440 |
+
|
| 441 |
+
For entities, use entity_type (e.g., 'class', 'function', 'method').
|
| 442 |
+
For other nodes, use node_type (e.g., 'file', 'chunk', 'directory').
|
| 443 |
+
|
| 444 |
Args:
|
| 445 |
node_type: The type of nodes to list (e.g., 'function', 'class', 'file')
|
| 446 |
limit: Maximum number of nodes to return (default: 20)
|
| 447 |
+
page: Page number for pagination (default: 1)
|
| 448 |
Returns:
|
| 449 |
str: A formatted string with matching nodes
|
| 450 |
"""
|
| 451 |
if knowledge_graph is None:
|
| 452 |
return "Error: Knowledge graph not initialized"
|
| 453 |
+
|
| 454 |
try:
|
| 455 |
# Convert limit/page to int if they're strings (MCP/Gradio may pass strings)
|
| 456 |
if isinstance(limit, str):
|
|
|
|
| 458 |
limit = int(limit)
|
| 459 |
except ValueError:
|
| 460 |
return f"Error: 'limit' must be an integer, got '{limit}'"
|
| 461 |
+
|
| 462 |
if isinstance(page, str):
|
| 463 |
try:
|
| 464 |
page = int(page)
|
| 465 |
except ValueError:
|
| 466 |
return f"Error: 'page' must be an integer, got '{page}'"
|
| 467 |
+
|
| 468 |
if limit <= 0:
|
| 469 |
return "Error: limit must be a positive integer"
|
| 470 |
if page < 1:
|
| 471 |
return "Error: 'page' must be a positive integer (1 or greater)"
|
| 472 |
+
|
| 473 |
g = knowledge_graph.graph
|
| 474 |
+
matching_nodes = []
|
| 475 |
+
|
| 476 |
+
for node_id, data in g.nodes(data=True):
|
| 477 |
+
node = data['data']
|
| 478 |
+
current_node_type = getattr(node, 'node_type', None)
|
| 479 |
+
node_name = getattr(node, 'name', 'Unknown')
|
| 480 |
+
|
| 481 |
+
# For entity nodes, check entity_type instead of node_type
|
| 482 |
+
if current_node_type == 'entity':
|
| 483 |
+
entity_type = getattr(node, 'entity_type', '')
|
| 484 |
+
|
| 485 |
+
# Fallback: if entity_type is empty, check the entities dictionary
|
| 486 |
+
if not entity_type and node_id in knowledge_graph.entities:
|
| 487 |
+
entity_types = knowledge_graph.entities[node_id].get('type', [])
|
| 488 |
+
entity_type = entity_types[0] if entity_types else ''
|
| 489 |
+
|
| 490 |
+
if entity_type and entity_type.lower() == node_type.lower():
|
| 491 |
+
matching_nodes.append({
|
| 492 |
+
"id": node_id,
|
| 493 |
+
"name": node_name,
|
| 494 |
+
"type": f"entity ({entity_type})"
|
| 495 |
+
})
|
| 496 |
+
# For other nodes, check node_type directly
|
| 497 |
+
elif current_node_type == node_type:
|
| 498 |
+
matching_nodes.append({
|
| 499 |
+
"id": node_id,
|
| 500 |
+
"name": node_name,
|
| 501 |
+
"type": current_node_type
|
| 502 |
+
})
|
| 503 |
+
|
| 504 |
+
# Sort by name for consistent ordering
|
| 505 |
+
matching_nodes.sort(key=lambda x: x['name'].lower())
|
| 506 |
+
|
| 507 |
total = len(matching_nodes)
|
| 508 |
if total == 0:
|
| 509 |
return f"No nodes found of type '{node_type}'."
|
| 510 |
+
|
| 511 |
# Pagination
|
| 512 |
total_pages = (total + limit - 1) // limit
|
| 513 |
if page > total_pages:
|
| 514 |
return f"Error: Page {page} does not exist. Total pages: {total_pages} (with {total} nodes at {limit} per page)"
|
| 515 |
+
|
| 516 |
start_idx = (page - 1) * limit
|
| 517 |
end_idx = start_idx + limit
|
| 518 |
page_slice = matching_nodes[start_idx:end_idx]
|
| 519 |
+
|
| 520 |
result = f"Nodes of type '{node_type}' (Page {page}/{total_pages}, {total} total):\n"
|
| 521 |
result += "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n"
|
| 522 |
+
|
| 523 |
for i, node in enumerate(page_slice, start=start_idx + 1):
|
| 524 |
result += f"{i}. {node['name']}\n"
|
| 525 |
+
result += f" ID: {node['id']}\n"
|
| 526 |
+
result += f" Type: {node['type']}\n\n"
|
| 527 |
+
|
| 528 |
# Pagination hint
|
| 529 |
if page < total_pages:
|
| 530 |
result += f"Use page={page + 1} to see the next page\n"
|
| 531 |
+
|
| 532 |
return result
|
| 533 |
except Exception as e:
|
| 534 |
return f"Error: {str(e)}"
|
|
|
|
| 2070 |
with gr.Row():
|
| 2071 |
with gr.Column():
|
| 2072 |
node_type_input = gr.Dropdown(
|
| 2073 |
+
choices=["file", "directory", "chunk", "function", "class", "method"],
|
| 2074 |
label="Node Type"
|
| 2075 |
)
|
| 2076 |
type_limit = gr.Slider(1, 100, value=20, step=1, label="Max Results")
|
|
|
|
| 2086 |
with gr.Row():
|
| 2087 |
with gr.Column():
|
| 2088 |
search_type = gr.Dropdown(
|
| 2089 |
+
choices=["file", "directory", "chunk", "function", "class", "method"],
|
| 2090 |
label="Node Type"
|
| 2091 |
)
|
| 2092 |
search_name = gr.Textbox(label="Name Contains", placeholder="Enter partial name...")
|