Spaces:
Sleeping
Sleeping
Upload 4 files
Browse files
app.py
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
from utils import run_agent_sync
|
| 3 |
+
|
| 4 |
+
st.set_page_config(page_title="MCP POC", page_icon="π€", layout="wide")
|
| 5 |
+
|
| 6 |
+
st.title("Model Context Protocol(MCP) - Learning Path Generator")
|
| 7 |
+
|
| 8 |
+
# Initialize session state for progress
|
| 9 |
+
if 'current_step' not in st.session_state:
|
| 10 |
+
st.session_state.current_step = ""
|
| 11 |
+
if 'progress' not in st.session_state:
|
| 12 |
+
st.session_state.progress = 0
|
| 13 |
+
if 'last_section' not in st.session_state:
|
| 14 |
+
st.session_state.last_section = ""
|
| 15 |
+
if 'is_generating' not in st.session_state:
|
| 16 |
+
st.session_state.is_generating = False
|
| 17 |
+
|
| 18 |
+
# Sidebar for API and URL configuration
|
| 19 |
+
st.sidebar.header("Configuration")
|
| 20 |
+
|
| 21 |
+
# API Key input
|
| 22 |
+
google_api_key = st.sidebar.text_input("Google API Key", type="password")
|
| 23 |
+
|
| 24 |
+
# Pipedream URLs
|
| 25 |
+
st.sidebar.subheader("Pipedream URLs")
|
| 26 |
+
youtube_pipedream_url = st.sidebar.text_input("YouTube URL (Required)",
|
| 27 |
+
placeholder="Enter your Pipedream YouTube URL")
|
| 28 |
+
|
| 29 |
+
# Secondary tool selection
|
| 30 |
+
secondary_tool = st.sidebar.radio(
|
| 31 |
+
"Select Secondary Tool:",
|
| 32 |
+
["Drive", "Notion"]
|
| 33 |
+
)
|
| 34 |
+
|
| 35 |
+
# Secondary tool URL input
|
| 36 |
+
if secondary_tool == "Drive":
|
| 37 |
+
drive_pipedream_url = st.sidebar.text_input("Drive URL",
|
| 38 |
+
placeholder="Enter your Pipedream Drive URL")
|
| 39 |
+
notion_pipedream_url = None
|
| 40 |
+
else:
|
| 41 |
+
notion_pipedream_url = st.sidebar.text_input("Notion URL",
|
| 42 |
+
placeholder="Enter your Pipedream Notion URL")
|
| 43 |
+
drive_pipedream_url = None
|
| 44 |
+
|
| 45 |
+
# Quick guide before goal input
|
| 46 |
+
st.info("""
|
| 47 |
+
**Quick Guide:**
|
| 48 |
+
1. Enter your Google API key and YouTube URL (required)
|
| 49 |
+
2. Select and configure your secondary tool (Drive or Notion)
|
| 50 |
+
3. Enter a clear learning goal, for example:
|
| 51 |
+
- "I want to learn python basics in 3 days"
|
| 52 |
+
- "I want to learn data science basics in 10 days"
|
| 53 |
+
""")
|
| 54 |
+
|
| 55 |
+
# Main content area
|
| 56 |
+
st.header("Enter Your Goal")
|
| 57 |
+
user_goal = st.text_input("Enter your learning goal:",
|
| 58 |
+
help="Describe what you want to learn, and we'll generate a structured path using YouTube content and your selected tool.")
|
| 59 |
+
|
| 60 |
+
# Progress area
|
| 61 |
+
progress_container = st.container()
|
| 62 |
+
progress_bar = st.empty()
|
| 63 |
+
|
| 64 |
+
def update_progress(message: str):
|
| 65 |
+
"""Update progress in the Streamlit UI"""
|
| 66 |
+
st.session_state.current_step = message
|
| 67 |
+
|
| 68 |
+
# Determine section and update progress
|
| 69 |
+
if "Setting up agent with tools" in message:
|
| 70 |
+
section = "Setup"
|
| 71 |
+
st.session_state.progress = 0.1
|
| 72 |
+
elif "Added Google Drive integration" in message or "Added Notion integration" in message:
|
| 73 |
+
section = "Integration"
|
| 74 |
+
st.session_state.progress = 0.2
|
| 75 |
+
elif "Creating AI agent" in message:
|
| 76 |
+
section = "Setup"
|
| 77 |
+
st.session_state.progress = 0.3
|
| 78 |
+
elif "Generating your learning path" in message:
|
| 79 |
+
section = "Generation"
|
| 80 |
+
st.session_state.progress = 0.5
|
| 81 |
+
elif "Learning path generation complete" in message:
|
| 82 |
+
section = "Complete"
|
| 83 |
+
st.session_state.progress = 1.0
|
| 84 |
+
st.session_state.is_generating = False
|
| 85 |
+
else:
|
| 86 |
+
section = st.session_state.last_section or "Progress"
|
| 87 |
+
|
| 88 |
+
st.session_state.last_section = section
|
| 89 |
+
|
| 90 |
+
# Show progress bar
|
| 91 |
+
progress_bar.progress(st.session_state.progress)
|
| 92 |
+
|
| 93 |
+
# Update progress container with current status
|
| 94 |
+
with progress_container:
|
| 95 |
+
# Show section header if it changed
|
| 96 |
+
if section != st.session_state.last_section and section != "Complete":
|
| 97 |
+
st.write(f"**{section}**")
|
| 98 |
+
|
| 99 |
+
# Show message with tick for completed steps
|
| 100 |
+
if message == "Learning path generation complete!":
|
| 101 |
+
st.success("All steps completed! π")
|
| 102 |
+
else:
|
| 103 |
+
prefix = "β" if st.session_state.progress >= 0.5 else "β"
|
| 104 |
+
st.write(f"{prefix} {message}")
|
| 105 |
+
|
| 106 |
+
# Generate Learning Path button
|
| 107 |
+
if st.button("Generate Learning Path", type="primary", disabled=st.session_state.is_generating):
|
| 108 |
+
if not google_api_key:
|
| 109 |
+
st.error("Please enter your Google API key in the sidebar.")
|
| 110 |
+
elif not youtube_pipedream_url:
|
| 111 |
+
st.error("YouTube URL is required. Please enter your Pipedream YouTube URL in the sidebar.")
|
| 112 |
+
elif (secondary_tool == "Drive" and not drive_pipedream_url) or (secondary_tool == "Notion" and not notion_pipedream_url):
|
| 113 |
+
st.error(f"Please enter your Pipedream {secondary_tool} URL in the sidebar.")
|
| 114 |
+
elif not user_goal:
|
| 115 |
+
st.warning("Please enter your learning goal.")
|
| 116 |
+
else:
|
| 117 |
+
try:
|
| 118 |
+
# Set generating flag
|
| 119 |
+
st.session_state.is_generating = True
|
| 120 |
+
|
| 121 |
+
# Reset progress
|
| 122 |
+
st.session_state.current_step = ""
|
| 123 |
+
st.session_state.progress = 0
|
| 124 |
+
st.session_state.last_section = ""
|
| 125 |
+
|
| 126 |
+
result = run_agent_sync(
|
| 127 |
+
google_api_key=google_api_key,
|
| 128 |
+
youtube_pipedream_url=youtube_pipedream_url,
|
| 129 |
+
drive_pipedream_url=drive_pipedream_url,
|
| 130 |
+
notion_pipedream_url=notion_pipedream_url,
|
| 131 |
+
user_goal=user_goal,
|
| 132 |
+
progress_callback=update_progress
|
| 133 |
+
)
|
| 134 |
+
|
| 135 |
+
# Display results
|
| 136 |
+
st.header("Your Learning Path")
|
| 137 |
+
# print(result)
|
| 138 |
+
if result and "messages" in result:
|
| 139 |
+
for msg in result["messages"]:
|
| 140 |
+
st.markdown(f"π {msg.content}")
|
| 141 |
+
|
| 142 |
+
else:
|
| 143 |
+
st.error("No results were generated. Please try again.")
|
| 144 |
+
st.session_state.is_generating = False
|
| 145 |
+
except Exception as e:
|
| 146 |
+
st.error(f"An error occurred: {str(e)}")
|
| 147 |
+
st.error("Please check your API keys and URLs, and try again.")
|
| 148 |
+
st.session_state.is_generating = False
|
prompt.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
user_goal_prompt = """
|
| 2 |
+
Main Instruction: You are a day wise learning path generator. You will be given a goal. You have to generate a comprehensive day-wise learning path for the user goal in a Drive document/Notion page and create a corresponding YouTube playlist containing the core learning videos.
|
| 3 |
+
Step-by-Step Execution Flow:
|
| 4 |
+
You must follow these steps sequentially to fulfill the user's request:
|
| 5 |
+
1. Plan the Learning Path Structure: Internally, devise a day-wise structure of topics relevant to the user's learning goal. Determine the core topics needed to achieve the goal, aiming for a logical progression. Limit the number of days/topics to a manageable size for a foundational path.
|
| 6 |
+
2. Research Potential Video Resources: For each topic identified in the plan, search and identify multiple relevant YouTube video URLs that could serve as learning resources. This step aims for broad discovery.
|
| 7 |
+
3. Select Core Videos for the Learning Path: From the researched videos, select the single most suitable video for each day/topic in your planned structure (from Step 1). These selected videos should be foundational, provide excellent overviews, or be highly impactful for that specific topic and the overall learning goal. The total number of selected videos will match the number of days/topics planned. These selected videos will form the content of both the document and the playlist.
|
| 8 |
+
4. Format the Document Content: Create the content that will go into the document/Notion page. Using the structure from Step 1 and the core videos selected in Step 3, format the day-wise learning path according to the 'Learning path sample format'. Ensure the content includes a clear main title for the learning path and uses headers/titles for each day/section.
|
| 9 |
+
5. Create and Populate Drive Document/Notion Page:
|
| 10 |
+
a. Create a new document in Google Drive/Notion page(while creating get the document/notion page id for to read and write). choosing based on the tools available to you.
|
| 11 |
+
b. Paste the formatted day-wise learning path content from Step 4 into the document/Notion page. Ensure all YouTube links are properly formatted as clickable links within the document.
|
| 12 |
+
c. Save the document/Notion page ID for further use.
|
| 13 |
+
6. Create Public YouTube Playlist:
|
| 14 |
+
a. Create one public YouTube playlist with a relevant title for the overall learning path.
|
| 15 |
+
b. Save the YouTube playlist ID for further use(to add youtube video urls from the learning path).
|
| 16 |
+
c. Add only the core videos selected in Step 3 (the same videos listed in the document) to this playlist.
|
| 17 |
+
if you are unable to find a previously created playlist by you, try the step 6 again.
|
| 18 |
+
7. (Optional) Suggest Further Resources: (If deemed relevant for the topic based on your knowledge) Add a small section at the end of the document/Notion page suggesting "Top Channels or Institutes to Follow" for further learning on the main topic.
|
| 19 |
+
8. Provide Outputs: Ensure the final response to the user includes the links to the created Google Drive document/Notion page and the YouTube playlist. The final output should explicitly state: "Here is your learning path document link: [link]" and "Here is your YouTube playlist link: [link] (with relevant content)".
|
| 20 |
+
|
| 21 |
+
General Instructions & Guidelines:
|
| 22 |
+
1. Act like a team player, coordinating between tools.
|
| 23 |
+
2. Utilize the provided tool descriptions. Choose tools like Google Drive/Notion and YouTube API based on their availability and your capabilities.
|
| 24 |
+
3. You can use multiple tools simultaneously.
|
| 25 |
+
4. Do not ask for confirmation from the user; proceed with the best possible outcome.
|
| 26 |
+
5. If encountering errors (e.g., unable to edit), find alternatives (e.g., create a new document).
|
| 27 |
+
6. When searching for resources, use terms users would generally use.
|
| 28 |
+
7. Remember to track document/page and playlist IDs for potential future interactions.
|
| 29 |
+
|
| 30 |
+
Learning path sample format within a day/section (to be used with overall document titles and headers):
|
| 31 |
+
Day X:
|
| 32 |
+
Topic: Topic name X
|
| 33 |
+
YouTube Link: URL of the core video selected for Topic X
|
| 34 |
+
(Continue for subsequent days...)
|
| 35 |
+
"""
|
requirements.txt
CHANGED
|
@@ -1,3 +1,5 @@
|
|
| 1 |
-
|
| 2 |
-
|
|
|
|
|
|
|
| 3 |
streamlit
|
|
|
|
| 1 |
+
langchain
|
| 2 |
+
langgraph
|
| 3 |
+
langchain-mcp-adapters
|
| 4 |
+
langchain-google-genai
|
| 5 |
streamlit
|
utils.py
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from langchain_core.messages import HumanMessage
|
| 2 |
+
from langchain_core.runnables import RunnableConfig
|
| 3 |
+
from prompt import user_goal_prompt
|
| 4 |
+
from langgraph.prebuilt import create_react_agent
|
| 5 |
+
from langchain_mcp_adapters.client import MultiServerMCPClient
|
| 6 |
+
from langchain_google_genai import ChatGoogleGenerativeAI
|
| 7 |
+
from typing import Optional, Tuple, Any, Callable
|
| 8 |
+
import asyncio
|
| 9 |
+
|
| 10 |
+
cfg = RunnableConfig(recursion_limit=100)
|
| 11 |
+
|
| 12 |
+
def initialize_model(google_api_key: str) -> ChatGoogleGenerativeAI:
|
| 13 |
+
return ChatGoogleGenerativeAI(
|
| 14 |
+
model="gemini-2.5-flash",
|
| 15 |
+
google_api_key=google_api_key
|
| 16 |
+
)
|
| 17 |
+
|
| 18 |
+
async def setup_agent_with_tools(
|
| 19 |
+
google_api_key: str,
|
| 20 |
+
youtube_pipedream_url: str,
|
| 21 |
+
drive_pipedream_url: Optional[str] = None,
|
| 22 |
+
notion_pipedream_url: Optional[str] = None,
|
| 23 |
+
progress_callback: Optional[Callable[[str], None]] = None
|
| 24 |
+
) -> Any:
|
| 25 |
+
"""
|
| 26 |
+
Set up the agent with YouTube (mandatory) and optional Drive or Notion tools.
|
| 27 |
+
"""
|
| 28 |
+
try:
|
| 29 |
+
if progress_callback:
|
| 30 |
+
progress_callback("Setting up agent with tools... β
")
|
| 31 |
+
|
| 32 |
+
# Initialize tools configuration with mandatory YouTube
|
| 33 |
+
tools_config = {
|
| 34 |
+
"youtube": {
|
| 35 |
+
"url": youtube_pipedream_url,
|
| 36 |
+
"transport": "streamable_http"
|
| 37 |
+
}
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
# Add Drive if URL provided
|
| 41 |
+
if drive_pipedream_url:
|
| 42 |
+
tools_config["drive"] = {
|
| 43 |
+
"url": drive_pipedream_url,
|
| 44 |
+
"transport": "streamable_http"
|
| 45 |
+
}
|
| 46 |
+
if progress_callback:
|
| 47 |
+
progress_callback("Added Google Drive integration... β
")
|
| 48 |
+
|
| 49 |
+
# Add Notion if URL provided
|
| 50 |
+
if notion_pipedream_url:
|
| 51 |
+
tools_config["notion"] = {
|
| 52 |
+
"url": notion_pipedream_url,
|
| 53 |
+
"transport": "streamable_http"
|
| 54 |
+
}
|
| 55 |
+
if progress_callback:
|
| 56 |
+
progress_callback("Added Notion integration... β
")
|
| 57 |
+
|
| 58 |
+
if progress_callback:
|
| 59 |
+
progress_callback("Initializing MCP client... β
")
|
| 60 |
+
# Initialize MCP client with configured tools
|
| 61 |
+
mcp_client = MultiServerMCPClient(tools_config)
|
| 62 |
+
|
| 63 |
+
if progress_callback:
|
| 64 |
+
progress_callback("Getting available tools... β
")
|
| 65 |
+
# Get all tools
|
| 66 |
+
tools = await mcp_client.get_tools()
|
| 67 |
+
|
| 68 |
+
if progress_callback:
|
| 69 |
+
progress_callback("Creating AI agent... β
")
|
| 70 |
+
# Create agent with initialized model
|
| 71 |
+
mcp_orch_model = initialize_model(google_api_key)
|
| 72 |
+
agent = create_react_agent(mcp_orch_model, tools)
|
| 73 |
+
|
| 74 |
+
if progress_callback:
|
| 75 |
+
progress_callback("Setup complete! Starting to generate learning path... β
")
|
| 76 |
+
|
| 77 |
+
return agent
|
| 78 |
+
except Exception as e:
|
| 79 |
+
print(f"Error in setup_agent_with_tools: {str(e)}")
|
| 80 |
+
raise
|
| 81 |
+
|
| 82 |
+
def run_agent_sync(
|
| 83 |
+
google_api_key: str,
|
| 84 |
+
youtube_pipedream_url: str,
|
| 85 |
+
drive_pipedream_url: Optional[str] = None,
|
| 86 |
+
notion_pipedream_url: Optional[str] = None,
|
| 87 |
+
user_goal: str = "",
|
| 88 |
+
progress_callback: Optional[Callable[[str], None]] = None
|
| 89 |
+
) -> dict:
|
| 90 |
+
"""
|
| 91 |
+
Synchronous wrapper for running the agent.
|
| 92 |
+
"""
|
| 93 |
+
async def _run():
|
| 94 |
+
try:
|
| 95 |
+
agent = await setup_agent_with_tools(
|
| 96 |
+
google_api_key=google_api_key,
|
| 97 |
+
youtube_pipedream_url=youtube_pipedream_url,
|
| 98 |
+
drive_pipedream_url=drive_pipedream_url,
|
| 99 |
+
notion_pipedream_url=notion_pipedream_url,
|
| 100 |
+
progress_callback=progress_callback
|
| 101 |
+
)
|
| 102 |
+
|
| 103 |
+
# Combine user goal with prompt template
|
| 104 |
+
learning_path_prompt = "User Goal: " + user_goal + "\n" + user_goal_prompt
|
| 105 |
+
|
| 106 |
+
if progress_callback:
|
| 107 |
+
progress_callback("Generating your learning path...")
|
| 108 |
+
|
| 109 |
+
# Run the agent
|
| 110 |
+
result = await agent.ainvoke(
|
| 111 |
+
{"messages": [HumanMessage(content=learning_path_prompt)]},
|
| 112 |
+
config=cfg
|
| 113 |
+
)
|
| 114 |
+
|
| 115 |
+
if progress_callback:
|
| 116 |
+
progress_callback("Learning path generation complete!")
|
| 117 |
+
|
| 118 |
+
return result
|
| 119 |
+
except Exception as e:
|
| 120 |
+
print(f"Error in _run: {str(e)}")
|
| 121 |
+
raise
|
| 122 |
+
|
| 123 |
+
# Run in new event loop
|
| 124 |
+
loop = asyncio.new_event_loop()
|
| 125 |
+
asyncio.set_event_loop(loop)
|
| 126 |
+
try:
|
| 127 |
+
return loop.run_until_complete(_run())
|
| 128 |
+
finally:
|
| 129 |
+
loop.close()
|