Spaces:
Running
Running
Commit
Β·
8455de3
1
Parent(s):
062db00
updated doc-mcp app
Browse files- app.py +103 -34
- rag/query.py +0 -2
app.py
CHANGED
|
@@ -791,74 +791,125 @@ with gr.Blocks(title="Doc-MCP") as demo:
|
|
| 791 |
|
| 792 |
with gr.Row():
|
| 793 |
with gr.Column(scale=2):
|
| 794 |
-
# Repository selection
|
| 795 |
-
|
| 796 |
-
|
| 797 |
-
|
| 798 |
-
|
| 799 |
-
|
| 800 |
-
|
| 801 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 802 |
refresh_repos_btn = gr.Button(
|
| 803 |
-
"π Refresh
|
| 804 |
)
|
| 805 |
|
| 806 |
# Query mode selection
|
| 807 |
query_mode = gr.Radio(
|
| 808 |
choices=["default", "text_search", "hybrid"],
|
| 809 |
-
label="
|
| 810 |
value="default",
|
| 811 |
-
info="default:
|
| 812 |
)
|
| 813 |
|
| 814 |
# Query input
|
| 815 |
query_input = gr.Textbox(
|
| 816 |
-
label="Your
|
| 817 |
-
placeholder="
|
| 818 |
lines=3,
|
|
|
|
| 819 |
)
|
| 820 |
|
| 821 |
-
query_btn = gr.Button("
|
| 822 |
|
| 823 |
# Response display as text area
|
| 824 |
response_output = gr.Textbox(
|
| 825 |
-
label="Response",
|
| 826 |
-
value="Your
|
| 827 |
lines=10,
|
| 828 |
interactive=False,
|
|
|
|
| 829 |
)
|
| 830 |
|
| 831 |
with gr.Column(scale=2):
|
| 832 |
-
gr.Markdown("### Source
|
|
|
|
| 833 |
|
| 834 |
# Source nodes display as JSON
|
| 835 |
sources_output = gr.JSON(
|
| 836 |
-
label="Source
|
| 837 |
value={
|
| 838 |
-
"message": "Source
|
|
|
|
| 839 |
},
|
| 840 |
)
|
| 841 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 842 |
def get_available_docs_repo():
|
| 843 |
"""
|
| 844 |
-
List the available docs of repositories
|
| 845 |
|
| 846 |
Returns:
|
| 847 |
-
|
| 848 |
"""
|
| 849 |
try:
|
| 850 |
repos = get_available_repositories()
|
| 851 |
-
|
|
|
|
|
|
|
| 852 |
except Exception as e:
|
| 853 |
print(f"Error refreshing repository list: {e}")
|
| 854 |
-
return gr.Dropdown(choices=[], value=None)
|
| 855 |
|
| 856 |
# Simple query handler
|
| 857 |
def handle_query(repo: str, mode: str, query: str):
|
| 858 |
"""
|
| 859 |
Handle query request - returns raw data from retriever
|
| 860 |
Args:
|
| 861 |
-
repo: Selected repository
|
| 862 |
mode: Query mode (default, text_search, hybrid)
|
| 863 |
query: User's query
|
| 864 |
Returns:
|
|
@@ -867,8 +918,8 @@ with gr.Blocks(title="Doc-MCP") as demo:
|
|
| 867 |
if not query.strip():
|
| 868 |
return {"error": "Please enter a query."}
|
| 869 |
|
| 870 |
-
if not repo:
|
| 871 |
-
return {"error": "Please select a repository."}
|
| 872 |
|
| 873 |
try:
|
| 874 |
# Import QueryRetriever here to avoid circular imports
|
|
@@ -895,8 +946,8 @@ with gr.Blocks(title="Doc-MCP") as demo:
|
|
| 895 |
This function is designed to support Retrieval-Augmented Generation (RAG) by extracting
|
| 896 |
the most relevant context chunks from indexed documentation sources.
|
| 897 |
Args:
|
| 898 |
-
repo: Selected repository
|
| 899 |
-
mode: Query mode
|
| 900 |
query: User's query
|
| 901 |
Returns:
|
| 902 |
Tuple of (response_text, source_nodes_json)
|
|
@@ -914,24 +965,42 @@ with gr.Blocks(title="Doc-MCP") as demo:
|
|
| 914 |
|
| 915 |
return response_text, source_nodes
|
| 916 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 917 |
refresh_repos_btn.click(
|
| 918 |
fn=get_available_docs_repo,
|
| 919 |
outputs=[repo_dropdown],
|
| 920 |
-
api_name="
|
| 921 |
)
|
| 922 |
|
| 923 |
-
#
|
| 924 |
query_btn.click(
|
| 925 |
fn=make_query,
|
| 926 |
-
inputs=[
|
| 927 |
outputs=[response_output, sources_output],
|
| 928 |
-
api_name="
|
| 929 |
)
|
| 930 |
|
| 931 |
# Also allow Enter key to trigger query
|
| 932 |
query_input.submit(
|
| 933 |
fn=make_query,
|
| 934 |
-
inputs=[
|
| 935 |
outputs=[response_output, sources_output],
|
| 936 |
show_api=False,
|
| 937 |
)
|
|
@@ -1149,4 +1218,4 @@ with gr.Blocks(title="Doc-MCP") as demo:
|
|
| 1149 |
|
| 1150 |
|
| 1151 |
if __name__ == "__main__":
|
| 1152 |
-
demo.launch(mcp_server=True
|
|
|
|
| 791 |
|
| 792 |
with gr.Row():
|
| 793 |
with gr.Column(scale=2):
|
| 794 |
+
# Repository selection - Dropdown that becomes textbox when selected
|
| 795 |
+
with gr.Row():
|
| 796 |
+
repo_dropdown = gr.Dropdown(
|
| 797 |
+
choices=get_available_repositories() or ["No repositories available"],
|
| 798 |
+
label="π Select Documentation Repository",
|
| 799 |
+
value=None,
|
| 800 |
+
interactive=True,
|
| 801 |
+
allow_custom_value=True,
|
| 802 |
+
info="Choose from available repositories"
|
| 803 |
+
)
|
| 804 |
+
|
| 805 |
+
# Hidden textbox that will become visible when repo is selected
|
| 806 |
+
selected_repo_textbox = gr.Textbox(
|
| 807 |
+
label="π― Selected Repository",
|
| 808 |
+
value="",
|
| 809 |
+
interactive=False,
|
| 810 |
+
visible=False,
|
| 811 |
+
info="Currently selected repository for querying"
|
| 812 |
+
)
|
| 813 |
+
|
| 814 |
refresh_repos_btn = gr.Button(
|
| 815 |
+
"π Refresh Repository List", variant="secondary", size="sm"
|
| 816 |
)
|
| 817 |
|
| 818 |
# Query mode selection
|
| 819 |
query_mode = gr.Radio(
|
| 820 |
choices=["default", "text_search", "hybrid"],
|
| 821 |
+
label="π Search Strategy",
|
| 822 |
value="default",
|
| 823 |
+
info="β’ default: Semantic similarity (AI understanding)\nβ’ text_search: Keyword matching\nβ’ hybrid: Combined approach for best results",
|
| 824 |
)
|
| 825 |
|
| 826 |
# Query input
|
| 827 |
query_input = gr.Textbox(
|
| 828 |
+
label="π Ask About Your Documentation",
|
| 829 |
+
placeholder="How do I implement a custom component? What are the available API endpoints? How to configure the system?",
|
| 830 |
lines=3,
|
| 831 |
+
info="Ask natural language questions about your documentation"
|
| 832 |
)
|
| 833 |
|
| 834 |
+
query_btn = gr.Button("π Search Documentation", variant="primary", size="lg")
|
| 835 |
|
| 836 |
# Response display as text area
|
| 837 |
response_output = gr.Textbox(
|
| 838 |
+
label="π€ AI Assistant Response",
|
| 839 |
+
value="Your AI-powered documentation response will appear here with contextual information and source citations...",
|
| 840 |
lines=10,
|
| 841 |
interactive=False,
|
| 842 |
+
info="Generated using Nebius LLM with retrieved documentation context"
|
| 843 |
)
|
| 844 |
|
| 845 |
with gr.Column(scale=2):
|
| 846 |
+
gr.Markdown("### π Source References")
|
| 847 |
+
gr.Markdown("View the exact documentation sources used to generate the response, with relevance scores and GitHub links.")
|
| 848 |
|
| 849 |
# Source nodes display as JSON
|
| 850 |
sources_output = gr.JSON(
|
| 851 |
+
label="π Source Citations & Metadata",
|
| 852 |
value={
|
| 853 |
+
"message": "Source documentation excerpts with relevance scores will appear here after your query...",
|
| 854 |
+
"info": "Each source includes file path, relevance score, and content snippet"
|
| 855 |
},
|
| 856 |
)
|
| 857 |
|
| 858 |
+
# Event handlers
|
| 859 |
+
def handle_repo_selection(selected_repo):
|
| 860 |
+
"""Handle repository selection from dropdown"""
|
| 861 |
+
if not selected_repo or selected_repo in ["No repositories available", ""]:
|
| 862 |
+
return (
|
| 863 |
+
gr.Dropdown(visible=True), # Keep dropdown visible
|
| 864 |
+
gr.Textbox(visible=False, value=""), # Hide textbox
|
| 865 |
+
gr.Button(interactive=False) # Disable query button
|
| 866 |
+
)
|
| 867 |
+
else:
|
| 868 |
+
return (
|
| 869 |
+
gr.Dropdown(visible=False), # Hide dropdown
|
| 870 |
+
gr.Textbox(visible=True, value=selected_repo), # Show textbox with selected repo
|
| 871 |
+
gr.Button(interactive=True) # Enable query button
|
| 872 |
+
)
|
| 873 |
+
|
| 874 |
+
def reset_repo_selection():
|
| 875 |
+
"""Reset to show dropdown again"""
|
| 876 |
+
try:
|
| 877 |
+
repos = get_available_repositories() or ["No repositories available"]
|
| 878 |
+
return (
|
| 879 |
+
gr.Dropdown(choices=repos, value=None, visible=True), # Show dropdown with refreshed choices
|
| 880 |
+
gr.Textbox(visible=False, value=""), # Hide textbox
|
| 881 |
+
gr.Button(interactive=False) # Disable query button
|
| 882 |
+
)
|
| 883 |
+
except Exception as e:
|
| 884 |
+
print(f"Error refreshing repository list: {e}")
|
| 885 |
+
return (
|
| 886 |
+
gr.Dropdown(choices=["Error loading repositories"], value=None, visible=True),
|
| 887 |
+
gr.Textbox(visible=False, value=""),
|
| 888 |
+
gr.Button(interactive=False)
|
| 889 |
+
)
|
| 890 |
+
|
| 891 |
def get_available_docs_repo():
|
| 892 |
"""
|
| 893 |
+
List the available docs of repositories - should be called first to list out all the available repo docs to chat with
|
| 894 |
|
| 895 |
Returns:
|
| 896 |
+
Updated dropdown with available repositories
|
| 897 |
"""
|
| 898 |
try:
|
| 899 |
repos = get_available_repositories()
|
| 900 |
+
if not repos:
|
| 901 |
+
repos = ["No repositories available - Please ingest documentation first"]
|
| 902 |
+
return gr.Dropdown(choices=repos, value=None)
|
| 903 |
except Exception as e:
|
| 904 |
print(f"Error refreshing repository list: {e}")
|
| 905 |
+
return gr.Dropdown(choices=["Error loading repositories"], value=None)
|
| 906 |
|
| 907 |
# Simple query handler
|
| 908 |
def handle_query(repo: str, mode: str, query: str):
|
| 909 |
"""
|
| 910 |
Handle query request - returns raw data from retriever
|
| 911 |
Args:
|
| 912 |
+
repo: Selected repository from textbox
|
| 913 |
mode: Query mode (default, text_search, hybrid)
|
| 914 |
query: User's query
|
| 915 |
Returns:
|
|
|
|
| 918 |
if not query.strip():
|
| 919 |
return {"error": "Please enter a query."}
|
| 920 |
|
| 921 |
+
if not repo or repo in ["No repositories available", "Error loading repositories", ""]:
|
| 922 |
+
return {"error": "Please select a valid repository."}
|
| 923 |
|
| 924 |
try:
|
| 925 |
# Import QueryRetriever here to avoid circular imports
|
|
|
|
| 946 |
This function is designed to support Retrieval-Augmented Generation (RAG) by extracting
|
| 947 |
the most relevant context chunks from indexed documentation sources.
|
| 948 |
Args:
|
| 949 |
+
repo: Selected repository from the textbox input
|
| 950 |
+
mode: Query mode (default, text_search, hybrid)
|
| 951 |
query: User's query
|
| 952 |
Returns:
|
| 953 |
Tuple of (response_text, source_nodes_json)
|
|
|
|
| 965 |
|
| 966 |
return response_text, source_nodes
|
| 967 |
|
| 968 |
+
# Wire up events
|
| 969 |
+
|
| 970 |
+
# Handle repository selection from dropdown
|
| 971 |
+
repo_dropdown.change(
|
| 972 |
+
fn=handle_repo_selection,
|
| 973 |
+
inputs=[repo_dropdown],
|
| 974 |
+
outputs=[repo_dropdown, selected_repo_textbox, query_btn],
|
| 975 |
+
show_api=False
|
| 976 |
+
)
|
| 977 |
+
|
| 978 |
+
# Handle refresh button - resets to dropdown view
|
| 979 |
+
refresh_repos_btn.click(
|
| 980 |
+
fn=reset_repo_selection,
|
| 981 |
+
outputs=[repo_dropdown, selected_repo_textbox, query_btn],
|
| 982 |
+
show_api=False
|
| 983 |
+
)
|
| 984 |
+
|
| 985 |
+
# Also provide API endpoint for listing repositories
|
| 986 |
refresh_repos_btn.click(
|
| 987 |
fn=get_available_docs_repo,
|
| 988 |
outputs=[repo_dropdown],
|
| 989 |
+
api_name="list_available_docs",
|
| 990 |
)
|
| 991 |
|
| 992 |
+
# Query button uses the textbox value (not dropdown)
|
| 993 |
query_btn.click(
|
| 994 |
fn=make_query,
|
| 995 |
+
inputs=[selected_repo_textbox, query_mode, query_input], # Use textbox, not dropdown
|
| 996 |
outputs=[response_output, sources_output],
|
| 997 |
+
api_name="query_documentation",
|
| 998 |
)
|
| 999 |
|
| 1000 |
# Also allow Enter key to trigger query
|
| 1001 |
query_input.submit(
|
| 1002 |
fn=make_query,
|
| 1003 |
+
inputs=[selected_repo_textbox, query_mode, query_input], # Use textbox, not dropdown
|
| 1004 |
outputs=[response_output, sources_output],
|
| 1005 |
show_api=False,
|
| 1006 |
)
|
|
|
|
| 1218 |
|
| 1219 |
|
| 1220 |
if __name__ == "__main__":
|
| 1221 |
+
demo.launch(mcp_server=True)
|
rag/query.py
CHANGED
|
@@ -60,8 +60,6 @@ class QueryRetriever:
|
|
| 60 |
response_mode="refine",
|
| 61 |
)
|
| 62 |
|
| 63 |
-
|
| 64 |
-
|
| 65 |
response = query_engine.query(query)
|
| 66 |
nodes = []
|
| 67 |
for node in response.source_nodes:
|
|
|
|
| 60 |
response_mode="refine",
|
| 61 |
)
|
| 62 |
|
|
|
|
|
|
|
| 63 |
response = query_engine.query(query)
|
| 64 |
nodes = []
|
| 65 |
for node in response.source_nodes:
|