From 54501c9a594cd53c511e6774524b5c600f9c1fb0 Mon Sep 17 00:00:00 2001 From: Cole Medin Date: Sun, 9 Mar 2025 08:25:30 -0500 Subject: [PATCH] Starting the split of streamlit_ui.py, better error handling --- README.md | 6 + graph_service.py | 1 + streamlit_pages/__init__.py | 1 + streamlit_pages/chat.py | 77 +++ .../future_enhancements.py | 0 streamlit_pages/intro.py | 140 +++++ streamlit_pages/mcp.py | 145 +++++ streamlit_pages/styles.py | 94 ++++ streamlit_ui.py | 516 +----------------- utils/utils.py | 22 + 10 files changed, 512 insertions(+), 490 deletions(-) create mode 100644 streamlit_pages/__init__.py create mode 100644 streamlit_pages/chat.py rename future_enhancements.py => streamlit_pages/future_enhancements.py (100%) create mode 100644 streamlit_pages/intro.py create mode 100644 streamlit_pages/mcp.py create mode 100644 streamlit_pages/styles.py diff --git a/README.md b/README.md index f8d009d7..eeade015 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,12 @@ After installation, follow the guided setup process in the Intro section of the The Streamlit interface will guide you through each step with clear instructions and interactive elements. There are a good amount of steps for the setup but it goes quick! +### Troubleshooting + +If you encounter any errors when using Archon, please first check the logs in the "Agent Service" tab. +Logs specifically for MCP are also logged to `workbench/logs.txt` (file is automatically created) so please +check there. The goal is for you to have a clear error message before creating a bug here in the GitHub repo + ## Project Evolution ### V1: Single-Agent Foundation diff --git a/graph_service.py b/graph_service.py index cb451245..05ba9868 100644 --- a/graph_service.py +++ b/graph_service.py @@ -61,6 +61,7 @@ async def invoke_agent(request: InvokeRequest): return {"response": response} except Exception as e: + print(f"Exception invoking Archon for thread {request.thread_id}: {str(e)}") write_to_log(f"Error processing message for thread {request.thread_id}: {str(e)}") raise HTTPException(status_code=500, detail=str(e)) diff --git a/streamlit_pages/__init__.py b/streamlit_pages/__init__.py new file mode 100644 index 00000000..2db4f6e4 --- /dev/null +++ b/streamlit_pages/__init__.py @@ -0,0 +1 @@ +# This file makes the streamlit_ui directory a Python package diff --git a/streamlit_pages/chat.py b/streamlit_pages/chat.py new file mode 100644 index 00000000..6800c8e3 --- /dev/null +++ b/streamlit_pages/chat.py @@ -0,0 +1,77 @@ +import streamlit as st +import uuid +import sys +import os + +# Add the current directory to Python path +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +from archon.archon_graph import agentic_flow + +@st.cache_resource +def get_thread_id(): + return str(uuid.uuid4()) + +thread_id = get_thread_id() + +async def run_agent_with_streaming(user_input: str): + """ + Run the agent with streaming text for the user_input prompt, + while maintaining the entire conversation in `st.session_state.messages`. + """ + config = { + "configurable": { + "thread_id": thread_id + } + } + + # First message from user + if len(st.session_state.messages) == 1: + async for msg in agentic_flow.astream( + {"latest_user_message": user_input}, config, stream_mode="custom" + ): + yield msg + # Continue the conversation + else: + async for msg in agentic_flow.astream( + Command(resume=user_input), config, stream_mode="custom" + ): + yield msg + +async def chat_tab(): + """Display the chat interface for talking to Archon""" + st.write("Describe to me an AI agent you want to build and I'll code it for you with Pydantic AI.") + st.write("Example: Build me an AI agent that can search the web with the Brave API.") + + # Initialize chat history in session state if not present + if "messages" not in st.session_state: + st.session_state.messages = [] + + # Display chat messages from history on app rerun + for message in st.session_state.messages: + message_type = message["type"] + if message_type in ["human", "ai", "system"]: + with st.chat_message(message_type): + st.markdown(message["content"]) + + # Chat input for the user + user_input = st.chat_input("What do you want to build today?") + + if user_input: + # We append a new request to the conversation explicitly + st.session_state.messages.append({"type": "human", "content": user_input}) + + # Display user prompt in the UI + with st.chat_message("user"): + st.markdown(user_input) + + # Display assistant response in chat message container + response_content = "" + with st.chat_message("assistant"): + message_placeholder = st.empty() # Placeholder for updating the message + # Run the async generator to fetch responses + async for chunk in run_agent_with_streaming(user_input): + response_content += chunk + # Update the placeholder with the current response content + message_placeholder.markdown(response_content) + + st.session_state.messages.append({"type": "ai", "content": response_content}) \ No newline at end of file diff --git a/future_enhancements.py b/streamlit_pages/future_enhancements.py similarity index 100% rename from future_enhancements.py rename to streamlit_pages/future_enhancements.py diff --git a/streamlit_pages/intro.py b/streamlit_pages/intro.py new file mode 100644 index 00000000..0fdfbb86 --- /dev/null +++ b/streamlit_pages/intro.py @@ -0,0 +1,140 @@ +import streamlit as st +import sys +import os + +# Add the parent directory to sys.path to allow importing from the parent directory +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +from utils.utils import create_new_tab_button + +def intro_tab(): + """Display the introduction and setup guide for Archon""" + # Display the banner image + st.image("public/Archon.png", use_container_width=True) + + # Welcome message + st.markdown(""" + # Welcome to Archon! + + Archon is an AI meta-agent designed to autonomously build, refine, and optimize other AI agents. + + It serves both as a practical tool for developers and as an educational framework demonstrating the evolution of agentic systems. + Archon is developed in iterations, starting with a simple Pydantic AI agent that can build other Pydantic AI agents, + all the way to a full agentic workflow using LangGraph that can build other AI agents with any framework. + + Through its iterative development, Archon showcases the power of planning, feedback loops, and domain-specific knowledge in creating robust AI agents. + """) + + # Setup guide with expandable sections + st.markdown("## Setup Guide") + st.markdown("Follow these concise steps to get Archon up and running (IMPORTANT: come back here after each step):") + + # Step 1: Environment Configuration + with st.expander("Step 1: Environment Configuration", expanded=True): + st.markdown(""" + ### Environment Configuration + + First, you need to set up your environment variables: + + 1. Go to the **Environment** tab + 2. Configure the following essential variables: + - `BASE_URL`: API endpoint (OpenAI, OpenRouter, or Ollama) + - `LLM_API_KEY`: Your API key for the LLM service + - `OPENAI_API_KEY`: Required for embeddings + - `SUPABASE_URL`: Your Supabase project URL + - `SUPABASE_SERVICE_KEY`: Your Supabase service key + - `PRIMARY_MODEL`: Main agent model (e.g., gpt-4o-mini) + - `REASONER_MODEL`: Planning model (e.g., o3-mini) + + These settings determine how Archon connects to external services and which models it uses. + """) + # Add a button to navigate to the Environment tab + create_new_tab_button("Go to Environment Section (New Tab)", "Environment", key="goto_env", use_container_width=True) + + # Step 2: Database Setup + with st.expander("Step 2: Database Setup", expanded=False): + st.markdown(""" + ### Database Setup + + Archon uses Supabase for vector storage and retrieval: + + 1. Go to the **Database** tab + 2. Select your embedding dimensions (1536 for OpenAI, 768 for nomic-embed-text) + 3. Follow the instructions to create the `site_pages` table + + This creates the necessary tables, indexes, and functions for vector similarity search. + """) + # Add a button to navigate to the Database tab + create_new_tab_button("Go to Database Section (New Tab)", "Database", key="goto_db", use_container_width=True) + + # Step 3: Documentation Crawling + with st.expander("Step 3: Documentation Crawling", expanded=False): + st.markdown(""" + ### Documentation Crawling + + Populate the database with framework documentation: + + 1. Go to the **Documentation** tab + 2. Click on "Crawl Pydantic AI Docs" + 3. Wait for the crawling process to complete + + This step downloads and processes documentation, creating embeddings for semantic search. + """) + # Add a button to navigate to the Documentation tab + create_new_tab_button("Go to the Documentation Section (New Tab)", "Documentation", key="goto_docs", use_container_width=True) + + # Step 4: Agent Service + with st.expander("Step 4: Agent Service Setup (for MCP)", expanded=False): + st.markdown(""" + ### MCP Agent Service Setup + + Start the graph service for agent generation: + + 1. Go to the **Agent Service** tab + 2. Click on "Start Agent Service" + 3. Verify the service is running + + The agent service powers the LangGraph workflow for agent creation. + """) + # Add a button to navigate to the Agent Service tab + create_new_tab_button("Go to Agent Service Section (New Tab)", "Agent Service", key="goto_service", use_container_width=True) + + # Step 5: MCP Configuration (Optional) + with st.expander("Step 5: MCP Configuration (Optional)", expanded=False): + st.markdown(""" + ### MCP Configuration + + For integration with AI IDEs: + + 1. Go to the **MCP** tab + 2. Select your IDE (Windsurf, Cursor, or Cline/Roo Code) + 3. Follow the instructions to configure your IDE + + This enables you to use Archon directly from your AI-powered IDE. + """) + # Add a button to navigate to the MCP tab + create_new_tab_button("Go to MCP Section (New Tab)", "MCP", key="goto_mcp", use_container_width=True) + + # Step 6: Using Archon + with st.expander("Step 6: Using Archon", expanded=False): + st.markdown(""" + ### Using Archon + + Once everything is set up: + + 1. Go to the **Chat** tab + 2. Describe the agent you want to build + 3. Archon will plan and generate the necessary code + + You can also use Archon directly from your AI IDE if you've configured MCP. + """) + # Add a button to navigate to the Chat tab + create_new_tab_button("Go to Chat Section (New Tab)", "Chat", key="goto_chat", use_container_width=True) + + # Resources + st.markdown(""" + ## Additional Resources + + - [GitHub Repository](https://github.com/coleam00/archon) + - [Archon Community Forum](https://thinktank.ottomator.ai/c/archon/30) + - [GitHub Kanban Board](https://github.com/users/coleam00/projects/1) + """) \ No newline at end of file diff --git a/streamlit_pages/mcp.py b/streamlit_pages/mcp.py new file mode 100644 index 00000000..be5ea178 --- /dev/null +++ b/streamlit_pages/mcp.py @@ -0,0 +1,145 @@ +import streamlit as st +import platform +import json +import os + +def generate_mcp_config(ide_type): + """ + Generate MCP configuration for the selected IDE type. + """ + # Get the absolute path to the current directory + base_path = os.path.abspath(os.path.dirname(__file__)) + + # Determine the correct python path based on the OS + if platform.system() == "Windows": + python_path = os.path.join(base_path, 'venv', 'Scripts', 'python.exe') + else: # macOS or Linux + python_path = os.path.join(base_path, 'venv', 'bin', 'python') + + server_script_path = os.path.join(base_path, 'mcp', 'mcp_server.py') + + # Create the config dictionary for Python + python_config = { + "mcpServers": { + "archon": { + "command": python_path, + "args": [server_script_path] + } + } + } + + # Create the config dictionary for Docker + docker_config = { + "mcpServers": { + "archon": { + "command": "docker", + "args": [ + "run", + "-i", + "--rm", + "-e", + "GRAPH_SERVICE_URL", + "archon-mcp:latest" + ], + "env": { + "GRAPH_SERVICE_URL": "http://host.docker.internal:8100" + } + } + } + } + + # Return appropriate configuration based on IDE type + if ide_type == "Windsurf": + return json.dumps(python_config, indent=2), json.dumps(docker_config, indent=2) + elif ide_type == "Cursor": + return f"{python_path} {server_script_path}", f"docker run -i --rm -e GRAPH_SERVICE_URL=http://host.docker.internal:8100 archon-mcp:latest" + elif ide_type == "Cline/Roo Code": + return json.dumps(python_config, indent=2), json.dumps(docker_config, indent=2) + else: + return "Unknown IDE type selected", "Unknown IDE type selected" + +def mcp_tab(): + """Display the MCP configuration interface""" + st.header("MCP Configuration") + st.write("Select your AI IDE to get the appropriate MCP configuration:") + + # IDE selection with side-by-side buttons + col1, col2, col3 = st.columns(3) + + with col1: + windsurf_button = st.button("Windsurf", use_container_width=True, key="windsurf_button") + with col2: + cursor_button = st.button("Cursor", use_container_width=True, key="cursor_button") + with col3: + cline_button = st.button("Cline/Roo Code", use_container_width=True, key="cline_button") + + # Initialize session state for selected IDE if not present + if "selected_ide" not in st.session_state: + st.session_state.selected_ide = None + + # Update selected IDE based on button clicks + if windsurf_button: + st.session_state.selected_ide = "Windsurf" + elif cursor_button: + st.session_state.selected_ide = "Cursor" + elif cline_button: + st.session_state.selected_ide = "Cline/Roo Code" + + # Display configuration if an IDE is selected + if st.session_state.selected_ide: + selected_ide = st.session_state.selected_ide + st.subheader(f"MCP Configuration for {selected_ide}") + python_config, docker_config = generate_mcp_config(selected_ide) + + # Configuration type tabs + config_tab1, config_tab2 = st.tabs(["Docker Configuration", "Python Configuration"]) + + with config_tab1: + st.markdown("### Docker Configuration") + st.code(docker_config, language="json" if selected_ide != "Cursor" else None) + + st.markdown("#### Requirements:") + st.markdown("- Docker installed") + st.markdown("- Run the setup script to build and start both containers:") + st.code("python run_docker.py", language="bash") + + with config_tab2: + st.markdown("### Python Configuration") + st.code(python_config, language="json" if selected_ide != "Cursor" else None) + + st.markdown("#### Requirements:") + st.markdown("- Python 3.11+ installed") + st.markdown("- Virtual environment created and activated") + st.markdown("- All dependencies installed via `pip install -r requirements.txt`") + st.markdown("- Must be running Archon not within a container") + + # Instructions based on IDE type + st.markdown("---") + st.markdown("### Setup Instructions") + + if selected_ide == "Windsurf": + st.markdown(""" + #### How to use in Windsurf: + 1. Click on the hammer icon above the chat input + 2. Click on "Configure" + 3. Paste the JSON from your preferred configuration tab above + 4. Click "Refresh" next to "Configure" + """) + elif selected_ide == "Cursor": + st.markdown(""" + #### How to use in Cursor: + 1. Go to Cursor Settings > Features > MCP + 2. Click on "+ Add New MCP Server" + 3. Name: Archon + 4. Type: command (equivalent to stdio) + 5. Command: Paste the command from your preferred configuration tab above + """) + elif selected_ide == "Cline/Roo Code": + st.markdown(""" + #### How to use in Cline or Roo Code: + 1. From the Cline/Roo Code extension, click the "MCP Server" tab + 2. Click the "Edit MCP Settings" button + 3. The MCP settings file should be displayed in a tab in VS Code + 4. Paste the JSON from your preferred configuration tab above + 5. Cline/Roo Code will automatically detect and start the MCP server + """) \ No newline at end of file diff --git a/streamlit_pages/styles.py b/streamlit_pages/styles.py new file mode 100644 index 00000000..62e06e91 --- /dev/null +++ b/streamlit_pages/styles.py @@ -0,0 +1,94 @@ +""" +This module contains the CSS styles for the Streamlit UI. +""" + +import streamlit as st + +def load_css(): + """ + Load the custom CSS styles for the Archon UI. + """ + st.markdown(""" + + """, unsafe_allow_html=True) diff --git a/streamlit_ui.py b/streamlit_ui.py index 37c4f92a..51ff9774 100644 --- a/streamlit_ui.py +++ b/streamlit_ui.py @@ -1,45 +1,36 @@ from __future__ import annotations +from supabase import Client, create_client from typing import Literal, TypedDict from langgraph.types import Command -import os - -import streamlit as st -import logfire -import asyncio -import time -import json -import uuid -import sys -import platform -import subprocess -import threading -import queue -import webbrowser -import importlib from urllib.parse import urlparse from openai import AsyncOpenAI -from supabase import Client, create_client from dotenv import load_dotenv -from utils.utils import get_env_var, save_env_var, write_to_log -from future_enhancements import future_enhancements_tab +import streamlit as st +import subprocess +import importlib +import threading +import platform +import logfire +import asyncio +import queue +import time +import json +import sys +import os -# Import all the message part classes -from pydantic_ai.messages import ( - ModelMessage, - ModelRequest, - ModelResponse, - SystemPromptPart, - UserPromptPart, - TextPart, - ToolCallPart, - ToolReturnPart, - RetryPromptPart, - ModelMessagesTypeAdapter +# Set page config - must be the first Streamlit command +st.set_page_config( + page_title="Archon - Agent Builder", + page_icon="🤖", + layout="wide", ) -# Add the current directory to Python path -sys.path.append(os.path.dirname(os.path.abspath(__file__))) -from archon.archon_graph import agentic_flow +from utils.utils import get_env_var, save_env_var, write_to_log, create_new_tab_button +from streamlit_pages.styles import load_css +from streamlit_pages.intro import intro_tab +from streamlit_pages.chat import chat_tab +from streamlit_pages.mcp import mcp_tab +from streamlit_pages.future_enhancements import future_enhancements_tab # Load environment variables from .env file load_dotenv() @@ -65,120 +56,8 @@ if get_env_var("SUPABASE_URL"): else: supabase = None -# Set page config - must be the first Streamlit command -st.set_page_config( - page_title="Archon - Agent Builder", - page_icon="🤖", - layout="wide", -) - -# Set custom theme colors to match Archon logo (green and pink) -# Primary color (green) and secondary color (pink) -st.markdown(""" - -""", unsafe_allow_html=True) - -# Helper function to create a button that opens a tab in a new window -def create_new_tab_button(label, tab_name, key=None, use_container_width=False): - """Create a button that opens a specified tab in a new browser window""" - # Create a unique key if none provided - if key is None: - key = f"new_tab_{tab_name.lower().replace(' ', '_')}" - - # Get the base URL - base_url = st.query_params.get("base_url", "") - if not base_url: - # If base_url is not in query params, use the default localhost URL - base_url = "http://localhost:8501" - - # Create the URL for the new tab - new_tab_url = f"{base_url}/?tab={tab_name}" - - # Create a button that will open the URL in a new tab when clicked - if st.button(label, key=key, use_container_width=use_container_width): - webbrowser.open_new_tab(new_tab_url) +# Load custom CSS styles +load_css() # Function to reload the archon_graph module def reload_archon_graph(): @@ -201,349 +80,6 @@ def reload_archon_graph(): # Configure logfire to suppress warnings (optional) logfire.configure(send_to_logfire='never') -@st.cache_resource -def get_thread_id(): - return str(uuid.uuid4()) - -thread_id = get_thread_id() - -async def run_agent_with_streaming(user_input: str): - """ - Run the agent with streaming text for the user_input prompt, - while maintaining the entire conversation in `st.session_state.messages`. - """ - config = { - "configurable": { - "thread_id": thread_id - } - } - - # First message from user - if len(st.session_state.messages) == 1: - async for msg in agentic_flow.astream( - {"latest_user_message": user_input}, config, stream_mode="custom" - ): - yield msg - # Continue the conversation - else: - async for msg in agentic_flow.astream( - Command(resume=user_input), config, stream_mode="custom" - ): - yield msg - -def generate_mcp_config(ide_type): - """ - Generate MCP configuration for the selected IDE type. - """ - # Get the absolute path to the current directory - base_path = os.path.abspath(os.path.dirname(__file__)) - - # Determine the correct python path based on the OS - if platform.system() == "Windows": - python_path = os.path.join(base_path, 'venv', 'Scripts', 'python.exe') - else: # macOS or Linux - python_path = os.path.join(base_path, 'venv', 'bin', 'python') - - server_script_path = os.path.join(base_path, 'mcp', 'mcp_server.py') - - # Create the config dictionary for Python - python_config = { - "mcpServers": { - "archon": { - "command": python_path, - "args": [server_script_path] - } - } - } - - # Create the config dictionary for Docker - docker_config = { - "mcpServers": { - "archon": { - "command": "docker", - "args": [ - "run", - "-i", - "--rm", - "-e", - "GRAPH_SERVICE_URL", - "archon-mcp:latest" - ], - "env": { - "GRAPH_SERVICE_URL": "http://host.docker.internal:8100" - } - } - } - } - - # Return appropriate configuration based on IDE type - if ide_type == "Windsurf": - return json.dumps(python_config, indent=2), json.dumps(docker_config, indent=2) - elif ide_type == "Cursor": - return f"{python_path} {server_script_path}", f"docker run -i --rm -e GRAPH_SERVICE_URL=http://host.docker.internal:8100 archon-mcp:latest" - elif ide_type == "Cline": - return json.dumps(python_config, indent=2), json.dumps(docker_config, indent=2) # Assuming Cline uses the same format as Windsurf - else: - return "Unknown IDE type selected", "Unknown IDE type selected" - -def mcp_tab(): - """Display the MCP configuration interface""" - st.header("MCP Configuration") - st.write("Select your AI IDE to get the appropriate MCP configuration:") - - # IDE selection with side-by-side buttons - col1, col2, col3 = st.columns(3) - - with col1: - windsurf_button = st.button("Windsurf", use_container_width=True, key="windsurf_button") - with col2: - cursor_button = st.button("Cursor", use_container_width=True, key="cursor_button") - with col3: - cline_button = st.button("Cline/Roo Code", use_container_width=True, key="cline_button") - - # Initialize session state for selected IDE if not present - if "selected_ide" not in st.session_state: - st.session_state.selected_ide = None - - # Update selected IDE based on button clicks - if windsurf_button: - st.session_state.selected_ide = "Windsurf" - elif cursor_button: - st.session_state.selected_ide = "Cursor" - elif cline_button: - st.session_state.selected_ide = "Cline/Roo Code" - - # Display configuration if an IDE is selected - if st.session_state.selected_ide: - selected_ide = st.session_state.selected_ide - st.subheader(f"MCP Configuration for {selected_ide}") - python_config, docker_config = generate_mcp_config(selected_ide) - - # Configuration type tabs - config_tab1, config_tab2 = st.tabs(["Docker Configuration", "Python Configuration"]) - - with config_tab1: - st.markdown("### Docker Configuration") - st.code(docker_config, language="json" if selected_ide != "Cursor" else None) - - st.markdown("#### Requirements:") - st.markdown("- Docker installed") - st.markdown("- Run the setup script to build and start both containers:") - st.code("python run_docker.py", language="bash") - - with config_tab2: - st.markdown("### Python Configuration") - st.code(python_config, language="json" if selected_ide != "Cursor" else None) - - st.markdown("#### Requirements:") - st.markdown("- Python 3.11+ installed") - st.markdown("- Virtual environment created and activated") - st.markdown("- All dependencies installed via `pip install -r requirements.txt`") - st.markdown("- Must be running Archon not within a container") - - # Instructions based on IDE type - st.markdown("---") - st.markdown("### Setup Instructions") - - if selected_ide == "Windsurf": - st.markdown(""" - #### How to use in Windsurf: - 1. Click on the hammer icon above the chat input - 2. Click on "Configure" - 3. Paste the JSON from your preferred configuration tab above - 4. Click "Refresh" next to "Configure" - """) - elif selected_ide == "Cursor": - st.markdown(""" - #### How to use in Cursor: - 1. Go to Cursor Settings > Features > MCP - 2. Click on "+ Add New MCP Server" - 3. Name: Archon - 4. Type: command (equivalent to stdio) - 5. Command: Paste the command from your preferred configuration tab above - """) - elif selected_ide == "Cline/Roo Code": - st.markdown(""" - #### How to use in Cline or Roo Code: - 1. From the Cline/Roo Code extension, click the "MCP Server" tab - 2. Click the "Edit MCP Settings" button - 3. The MCP settings file should be displayed in a tab in VS Code - 4. Paste the JSON from your preferred configuration tab above - 5. Cline/Roo Code will automatically detect and start the MCP server - """) - -async def chat_tab(): - """Display the chat interface for talking to Archon""" - st.write("Describe to me an AI agent you want to build and I'll code it for you with Pydantic AI.") - st.write("Example: Build me an AI agent that can search the web with the Brave API.") - - # Initialize chat history in session state if not present - if "messages" not in st.session_state: - st.session_state.messages = [] - - # Display chat messages from history on app rerun - for message in st.session_state.messages: - message_type = message["type"] - if message_type in ["human", "ai", "system"]: - with st.chat_message(message_type): - st.markdown(message["content"]) - - # Chat input for the user - user_input = st.chat_input("What do you want to build today?") - - if user_input: - # We append a new request to the conversation explicitly - st.session_state.messages.append({"type": "human", "content": user_input}) - - # Display user prompt in the UI - with st.chat_message("user"): - st.markdown(user_input) - - # Display assistant response in chat message container - response_content = "" - with st.chat_message("assistant"): - message_placeholder = st.empty() # Placeholder for updating the message - # Run the async generator to fetch responses - async for chunk in run_agent_with_streaming(user_input): - response_content += chunk - # Update the placeholder with the current response content - message_placeholder.markdown(response_content) - - st.session_state.messages.append({"type": "ai", "content": response_content}) - -def intro_tab(): - """Display the introduction and setup guide for Archon""" - # Display the banner image - st.image("public/Archon.png", use_container_width=True) - - # Welcome message - st.markdown(""" - # Welcome to Archon! - - Archon is an AI meta-agent designed to autonomously build, refine, and optimize other AI agents. - - It serves both as a practical tool for developers and as an educational framework demonstrating the evolution of agentic systems. - Archon is developed in iterations, starting with a simple Pydantic AI agent that can build other Pydantic AI agents, - all the way to a full agentic workflow using LangGraph that can build other AI agents with any framework. - - Through its iterative development, Archon showcases the power of planning, feedback loops, and domain-specific knowledge in creating robust AI agents. - """) - - # Setup guide with expandable sections - st.markdown("## Setup Guide") - st.markdown("Follow these concise steps to get Archon up and running (IMPORTANT: come back here after each step):") - - # Step 1: Environment Configuration - with st.expander("Step 1: Environment Configuration", expanded=True): - st.markdown(""" - ### Environment Configuration - - First, you need to set up your environment variables: - - 1. Go to the **Environment** tab - 2. Configure the following essential variables: - - `BASE_URL`: API endpoint (OpenAI, OpenRouter, or Ollama) - - `LLM_API_KEY`: Your API key for the LLM service - - `OPENAI_API_KEY`: Required for embeddings - - `SUPABASE_URL`: Your Supabase project URL - - `SUPABASE_SERVICE_KEY`: Your Supabase service key - - `PRIMARY_MODEL`: Main agent model (e.g., gpt-4o-mini) - - `REASONER_MODEL`: Planning model (e.g., o3-mini) - - These settings determine how Archon connects to external services and which models it uses. - """) - # Add a button to navigate to the Environment tab - create_new_tab_button("Go to Environment Section (New Tab)", "Environment", key="goto_env", use_container_width=True) - - # Step 2: Database Setup - with st.expander("Step 2: Database Setup", expanded=False): - st.markdown(""" - ### Database Setup - - Archon uses Supabase for vector storage and retrieval: - - 1. Go to the **Database** tab - 2. Select your embedding dimensions (1536 for OpenAI, 768 for nomic-embed-text) - 3. Follow the instructions to create the `site_pages` table - - This creates the necessary tables, indexes, and functions for vector similarity search. - """) - # Add a button to navigate to the Database tab - create_new_tab_button("Go to Database Section (New Tab)", "Database", key="goto_db", use_container_width=True) - - # Step 3: Documentation Crawling - with st.expander("Step 3: Documentation Crawling", expanded=False): - st.markdown(""" - ### Documentation Crawling - - Populate the database with framework documentation: - - 1. Go to the **Documentation** tab - 2. Click on "Crawl Pydantic AI Docs" - 3. Wait for the crawling process to complete - - This step downloads and processes documentation, creating embeddings for semantic search. - """) - # Add a button to navigate to the Documentation tab - create_new_tab_button("Go to the Documentation Section (New Tab)", "Documentation", key="goto_docs", use_container_width=True) - - # Step 4: Agent Service - with st.expander("Step 4: Agent Service Setup (for MCP)", expanded=False): - st.markdown(""" - ### MCP Agent Service Setup - - Start the graph service for agent generation: - - 1. Go to the **Agent Service** tab - 2. Click on "Start Agent Service" - 3. Verify the service is running - - The agent service powers the LangGraph workflow for agent creation. - """) - # Add a button to navigate to the Agent Service tab - create_new_tab_button("Go to Agent Service Section (New Tab)", "Agent Service", key="goto_service", use_container_width=True) - - # Step 5: MCP Configuration (Optional) - with st.expander("Step 5: MCP Configuration (Optional)", expanded=False): - st.markdown(""" - ### MCP Configuration - - For integration with AI IDEs: - - 1. Go to the **MCP** tab - 2. Select your IDE (Windsurf, Cursor, or Cline/Roo Code) - 3. Follow the instructions to configure your IDE - - This enables you to use Archon directly from your AI-powered IDE. - """) - # Add a button to navigate to the MCP tab - create_new_tab_button("Go to MCP Section (New Tab)", "MCP", key="goto_mcp", use_container_width=True) - - # Step 6: Using Archon - with st.expander("Step 6: Using Archon", expanded=False): - st.markdown(""" - ### Using Archon - - Once everything is set up: - - 1. Go to the **Chat** tab - 2. Describe the agent you want to build - 3. Archon will plan and generate the necessary code - - You can also use Archon directly from your AI IDE if you've configured MCP. - """) - # Add a button to navigate to the Chat tab - create_new_tab_button("Go to Chat Section (New Tab)", "Chat", key="goto_chat", use_container_width=True) - - # Resources - st.markdown(""" - ## Additional Resources - - - [GitHub Repository](https://github.com/coleam00/archon) - - [Archon Community Forum](https://thinktank.ottomator.ai/c/archon/30) - - [GitHub Kanban Board](https://github.com/users/coleam00/projects/1) - """) - def documentation_tab(): """Display the documentation interface""" st.header("Documentation") diff --git a/utils/utils.py b/utils/utils.py index ef7bf43f..f899b713 100644 --- a/utils/utils.py +++ b/utils/utils.py @@ -5,6 +5,8 @@ import inspect import json from typing import Optional from dotenv import load_dotenv +import streamlit as st +import webbrowser # Load environment variables from .env file load_dotenv() @@ -109,3 +111,23 @@ def log_node_execution(func): write_to_log(f"Error in node {func_name}: {str(e)}") raise return wrapper + +# Helper function to create a button that opens a tab in a new window +def create_new_tab_button(label, tab_name, key=None, use_container_width=False): + """Create a button that opens a specified tab in a new browser window""" + # Create a unique key if none provided + if key is None: + key = f"new_tab_{tab_name.lower().replace(' ', '_')}" + + # Get the base URL + base_url = st.query_params.get("base_url", "") + if not base_url: + # If base_url is not in query params, use the default localhost URL + base_url = "http://localhost:8501" + + # Create the URL for the new tab + new_tab_url = f"{base_url}/?tab={tab_name}" + + # Create a button that will open the URL in a new tab when clicked + if st.button(label, key=key, use_container_width=use_container_width): + webbrowser.open_new_tab(new_tab_url)