Commit
·
d874b77
1
Parent(s):
8942d03
add pagination to list nodes by type
Browse files- gradio_mcp_space.py +33 -10
gradio_mcp_space.py
CHANGED
|
@@ -982,7 +982,7 @@ def entity_relationships(node_id: str) -> str:
|
|
| 982 |
|
| 983 |
|
| 984 |
@observe(as_type="tool")
|
| 985 |
-
def search_by_type_and_name(node_type: str, name_query: str, limit: int = 10, fuzzy: bool = True) -> str:
|
| 986 |
"""
|
| 987 |
Search for nodes/entities by type and name substring with fuzzy matching support.
|
| 988 |
|
|
@@ -1005,19 +1005,27 @@ def search_by_type_and_name(node_type: str, name_query: str, limit: int = 10, fu
|
|
| 1005 |
return "Error: Knowledge graph not initialized"
|
| 1006 |
|
| 1007 |
try:
|
| 1008 |
-
# Convert limit to int if
|
| 1009 |
if isinstance(limit, str):
|
| 1010 |
try:
|
| 1011 |
limit = int(limit)
|
| 1012 |
except ValueError:
|
| 1013 |
return f"Error: 'limit' must be an integer, got '{limit}'"
|
| 1014 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1015 |
# Convert fuzzy to bool if it's a string
|
| 1016 |
if isinstance(fuzzy, str):
|
| 1017 |
fuzzy = fuzzy.lower() in ('true', '1', 'yes')
|
| 1018 |
-
|
| 1019 |
if limit <= 0:
|
| 1020 |
return "Error: limit must be a positive integer"
|
|
|
|
|
|
|
| 1021 |
|
| 1022 |
g = knowledge_graph.graph
|
| 1023 |
matches = []
|
|
@@ -1084,21 +1092,33 @@ def search_by_type_and_name(node_type: str, name_query: str, limit: int = 10, fu
|
|
| 1084 |
"score": score
|
| 1085 |
})
|
| 1086 |
|
| 1087 |
-
# Sort by match score (best matches first)
|
| 1088 |
matches.sort(key=lambda x: (x['score'], x['name'].lower()))
|
| 1089 |
-
matches = matches[:limit]
|
| 1090 |
|
| 1091 |
-
|
|
|
|
| 1092 |
return f"No matches for type '{node_type}' and name containing '{name_query}'."
|
| 1093 |
|
| 1094 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1095 |
result += "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n"
|
| 1096 |
|
| 1097 |
-
for i, match in enumerate(
|
| 1098 |
result += f"{i}. {match['name']}\n"
|
| 1099 |
result += f" ID: {match['id']}\n"
|
| 1100 |
result += f" Type: {match['type']}\n\n"
|
| 1101 |
|
|
|
|
|
|
|
|
|
|
| 1102 |
return result
|
| 1103 |
except Exception as e:
|
| 1104 |
return f"Error: {str(e)}"
|
|
@@ -1910,10 +1930,13 @@ def create_gradio_app():
|
|
| 1910 |
label="Node Type"
|
| 1911 |
)
|
| 1912 |
search_name = gr.Textbox(label="Name Contains", placeholder="Enter partial name...")
|
|
|
|
|
|
|
|
|
|
| 1913 |
search_type_btn = gr.Button("Search", variant="primary")
|
| 1914 |
with gr.Column():
|
| 1915 |
search_type_output = gr.Textbox(label="Results", lines=20, max_lines=30)
|
| 1916 |
-
search_type_btn.click(fn=search_by_type_and_name, inputs=[search_type, search_name], outputs=search_type_output)
|
| 1917 |
gr.Markdown(_tool_doc_md(search_by_type_and_name))
|
| 1918 |
|
| 1919 |
with gr.Tab("🔗 Relationships"):
|
|
|
|
| 982 |
|
| 983 |
|
| 984 |
@observe(as_type="tool")
|
| 985 |
+
def search_by_type_and_name(node_type: str, name_query: str, limit: int = 10, page: int = 1, fuzzy: bool = True) -> str:
|
| 986 |
"""
|
| 987 |
Search for nodes/entities by type and name substring with fuzzy matching support.
|
| 988 |
|
|
|
|
| 1005 |
return "Error: Knowledge graph not initialized"
|
| 1006 |
|
| 1007 |
try:
|
| 1008 |
+
# Convert limit/page to int if they're strings (MCP/Gradio may pass strings)
|
| 1009 |
if isinstance(limit, str):
|
| 1010 |
try:
|
| 1011 |
limit = int(limit)
|
| 1012 |
except ValueError:
|
| 1013 |
return f"Error: 'limit' must be an integer, got '{limit}'"
|
| 1014 |
+
|
| 1015 |
+
if isinstance(page, str):
|
| 1016 |
+
try:
|
| 1017 |
+
page = int(page)
|
| 1018 |
+
except ValueError:
|
| 1019 |
+
return f"Error: 'page' must be an integer, got '{page}'"
|
| 1020 |
+
|
| 1021 |
# Convert fuzzy to bool if it's a string
|
| 1022 |
if isinstance(fuzzy, str):
|
| 1023 |
fuzzy = fuzzy.lower() in ('true', '1', 'yes')
|
| 1024 |
+
|
| 1025 |
if limit <= 0:
|
| 1026 |
return "Error: limit must be a positive integer"
|
| 1027 |
+
if page < 1:
|
| 1028 |
+
return "Error: 'page' must be a positive integer (1 or greater)"
|
| 1029 |
|
| 1030 |
g = knowledge_graph.graph
|
| 1031 |
matches = []
|
|
|
|
| 1092 |
"score": score
|
| 1093 |
})
|
| 1094 |
|
| 1095 |
+
# Sort by match score (best matches first)
|
| 1096 |
matches.sort(key=lambda x: (x['score'], x['name'].lower()))
|
|
|
|
| 1097 |
|
| 1098 |
+
total = len(matches)
|
| 1099 |
+
if total == 0:
|
| 1100 |
return f"No matches for type '{node_type}' and name containing '{name_query}'."
|
| 1101 |
|
| 1102 |
+
# Pagination
|
| 1103 |
+
total_pages = (total + limit - 1) // limit
|
| 1104 |
+
if page > total_pages:
|
| 1105 |
+
return f"Error: Page {page} does not exist. Total pages: {total_pages} (with {total} results at {limit} per page)"
|
| 1106 |
+
|
| 1107 |
+
start_idx = (page - 1) * limit
|
| 1108 |
+
end_idx = start_idx + limit
|
| 1109 |
+
page_slice = matches[start_idx:end_idx]
|
| 1110 |
+
|
| 1111 |
+
result = f"Matches for type '{node_type}' and name '{name_query}' (Page {page}/{total_pages}, {total} total):\n"
|
| 1112 |
result += "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n"
|
| 1113 |
|
| 1114 |
+
for i, match in enumerate(page_slice, start=start_idx + 1):
|
| 1115 |
result += f"{i}. {match['name']}\n"
|
| 1116 |
result += f" ID: {match['id']}\n"
|
| 1117 |
result += f" Type: {match['type']}\n\n"
|
| 1118 |
|
| 1119 |
+
if page < total_pages:
|
| 1120 |
+
result += f"Use page={page + 1} to see the next page\n"
|
| 1121 |
+
|
| 1122 |
return result
|
| 1123 |
except Exception as e:
|
| 1124 |
return f"Error: {str(e)}"
|
|
|
|
| 1930 |
label="Node Type"
|
| 1931 |
)
|
| 1932 |
search_name = gr.Textbox(label="Name Contains", placeholder="Enter partial name...")
|
| 1933 |
+
search_limit = gr.Slider(1, 100, value=10, step=1, label="Max Results")
|
| 1934 |
+
search_page = gr.Slider(1, 100, value=1, step=1, label="Page")
|
| 1935 |
+
search_fuzzy = gr.Checkbox(label="Fuzzy Match", value=True)
|
| 1936 |
search_type_btn = gr.Button("Search", variant="primary")
|
| 1937 |
with gr.Column():
|
| 1938 |
search_type_output = gr.Textbox(label="Results", lines=20, max_lines=30)
|
| 1939 |
+
search_type_btn.click(fn=search_by_type_and_name, inputs=[search_type, search_name, search_limit, search_page, search_fuzzy], outputs=search_type_output)
|
| 1940 |
gr.Markdown(_tool_doc_md(search_by_type_and_name))
|
| 1941 |
|
| 1942 |
with gr.Tab("🔗 Relationships"):
|