diff --git a/.env.example b/.env.example index 41a49840..08fea9bf 100644 --- a/.env.example +++ b/.env.example @@ -4,16 +4,17 @@ # OpenRouter: https://openrouter.ai/api/v1 BASE_URL= -# Get your Open AI API Key by following these instructions - -# https://help.openai.com/en/articles/4936850-where-do-i-find-my-openai-api-key -# Even if using OpenRouter/Ollama, you still need to set this for the embedding model. -# Future versions of Archon will be more flexible with this. -OPENAI_API_KEY= - # For OpenAI: https://help.openai.com/en/articles/4936850-where-do-i-find-my-openai-api-key # For OpenRouter: https://openrouter.ai/keys +# For Ollama, no need to set this unless you specifically configured an API key LLM_API_KEY= +# Get your Open AI API Key by following these instructions - +# https://help.openai.com/en/articles/4936850-where-do-i-find-my-openai-api-key +# Even if using OpenRouter, you still need to set this for the embedding model. +# No need to set this if using Ollama. +OPENAI_API_KEY= + # For the Supabase version (sample_supabase_agent.py), set your Supabase URL and Service Key. # Get your SUPABASE_URL from the API section of your Supabase project settings - # https://supabase.com/dashboard/project//settings/api @@ -32,4 +33,9 @@ REASONER_MODEL= # The LLM you want to use for the primary agent/coder. # Example: gpt-4o-mini # Example: qwen2.5:14b-instruct-8k -PRIMARY_MODEL= \ No newline at end of file +PRIMARY_MODEL= + +# Embedding model you want to use +# Example for Ollama: nomic-embed-text +# Example for OpenAI: text-embedding-3-small +EMBEDDING_MODEL= \ No newline at end of file diff --git a/README.md b/README.md index fd2d9389..98b95183 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,6 @@ Archon demonstrates three key principles in modern AI development: - LangSmith - Other frameworks besides Pydantic AI - Other vector databases besides Supabase -- Alternative embedding models besides OpenAI ## Getting Started with V3 (current version) @@ -152,6 +151,7 @@ This will: 1. Set up the database: - Execute `utils/site_pages.sql` in your Supabase SQL Editor - This creates tables and enables vector similarity search + - See the Database Setup section for more details 2. Crawl documentation: ```bash @@ -202,8 +202,12 @@ The interface will be available at `http://localhost:8501` - `utils/`: Utility functions and database setup - `utils.py`: Shared utility functions - `site_pages.sql`: Database setup commands + - `site_pages_ollama.sql`: Database setup commands with vector dimensions updated for nomic-embed-text + +### Database Setup + +The Supabase database uses the following schema: -### Database Schema ```sql CREATE TABLE site_pages ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), @@ -213,10 +217,19 @@ CREATE TABLE site_pages ( summary TEXT, content TEXT, metadata JSONB, - embedding VECTOR(1536) + embedding VECTOR(1536) -- Adjust dimensions as necessary (i.e. 768 for nomic-embed-text) ); ``` +Execute the SQL commands in `utils/site_pages.sql` to: +1. Create the necessary tables +2. Enable vector similarity search +3. Set up Row Level Security policies + +In Supabase, do this by going to the "SQL Editor" tab and pasting in the SQL into the editor there. Then click "Run". + +If using Ollama with the nomic-embed-text embedding model or another with 786 dimensions, either update site_pages.sql so that the dimensions are 768 instead of 1536 or use `utils/ollama_site_pages.sql` + ## Contributing We welcome contributions! Whether you're fixing bugs, adding features, or improving documentation, please feel free to submit a Pull Request. diff --git a/archon/archon_graph.py b/archon/archon_graph.py index 850e65b3..982e5b9e 100644 --- a/archon/archon_graph.py +++ b/archon/archon_graph.py @@ -48,7 +48,13 @@ end_conversation_agent = Agent( system_prompt='Your job is to end a conversation for creating an AI agent by giving instructions for how to execute the agent and they saying a nice goodbye to the user.', ) -openai_client = AsyncOpenAI(api_key=os.getenv("OPENAI_API_KEY")) +openai_client=None + +if is_ollama: + openai_client = AsyncOpenAI(base_url=base_url,api_key=api_key) +else: + openai_client = AsyncOpenAI(api_key=os.getenv("OPENAI_API_KEY")) + supabase: Client = Client( os.getenv("SUPABASE_URL"), os.getenv("SUPABASE_SERVICE_KEY") diff --git a/archon/crawl_pydantic_ai_docs.py b/archon/crawl_pydantic_ai_docs.py index 81e897fb..995f1b1a 100644 --- a/archon/crawl_pydantic_ai_docs.py +++ b/archon/crawl_pydantic_ai_docs.py @@ -17,7 +17,20 @@ from supabase import create_client, Client load_dotenv() # Initialize OpenAI and Supabase clients -openai_client = AsyncOpenAI(api_key=os.getenv("OPENAI_API_KEY")) + +base_url = os.getenv('BASE_URL', 'https://api.openai.com/v1') +api_key = os.getenv('LLM_API_KEY', 'no-llm-api-key-provided') +is_ollama = "localhost" in base_url.lower() + +embedding_model = os.getenv('EMBEDDING_MODEL', 'text-embedding-3-small') + +openai_client=None + +if is_ollama: + openai_client = AsyncOpenAI(base_url=base_url,api_key=api_key) +else: + openai_client = AsyncOpenAI(api_key=os.getenv("OPENAI_API_KEY")) + supabase: Client = create_client( os.getenv("SUPABASE_URL"), os.getenv("SUPABASE_SERVICE_KEY") @@ -88,7 +101,7 @@ async def get_title_and_summary(chunk: str, url: str) -> Dict[str, str]: try: response = await openai_client.chat.completions.create( - model=os.getenv("LLM_MODEL", "gpt-4o-mini"), + model=os.getenv("PRIMARY_MODEL", "gpt-4o-mini"), messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": f"URL: {url}\n\nContent:\n{chunk[:1000]}..."} # Send first 1000 chars for context @@ -104,7 +117,7 @@ async def get_embedding(text: str) -> List[float]: """Get embedding vector from OpenAI.""" try: response = await openai_client.embeddings.create( - model="text-embedding-3-small", + model= embedding_model, input=text ) return response.data[0].embedding @@ -231,7 +244,20 @@ def get_pydantic_ai_docs_urls() -> List[str]: print(f"Error fetching sitemap: {e}") return [] +async def clear_existing_records(): + """Clear all existing records with source='pydantic_ai_docs' from the site_pages table.""" + try: + result = supabase.table("site_pages").delete().eq("metadata->>source", "pydantic_ai_docs").execute() + print("Cleared existing pydantic_ai_docs records from site_pages") + return result + except Exception as e: + print(f"Error clearing existing records: {e}") + return None + async def main(): + # Clear existing records first + await clear_existing_records() + # Get URLs from Pydantic AI docs urls = get_pydantic_ai_docs_urls() if not urls: diff --git a/archon/pydantic_ai_coder.py b/archon/pydantic_ai_coder.py index 80f6a942..206cfa99 100644 --- a/archon/pydantic_ai_coder.py +++ b/archon/pydantic_ai_coder.py @@ -19,8 +19,11 @@ llm = os.getenv('PRIMARY_MODEL', 'gpt-4o-mini') base_url = os.getenv('BASE_URL', 'https://api.openai.com/v1') api_key = os.getenv('LLM_API_KEY', 'no-llm-api-key-provided') model = OpenAIModel(llm, base_url=base_url, api_key=api_key) +embedding_model = os.getenv('EMBEDDING_MODEL', 'text-embedding-3-small') -# logfire.configure(send_to_logfire='if-token-present') +logfire.configure(send_to_logfire='if-token-present') + +is_ollama = "localhost" in base_url.lower() @dataclass class PydanticAIDeps: @@ -88,7 +91,7 @@ async def get_embedding(text: str, openai_client: AsyncOpenAI) -> List[float]: """Get embedding vector from OpenAI.""" try: response = await openai_client.embeddings.create( - model="text-embedding-3-small", + model=embedding_model, input=text ) return response.data[0].embedding diff --git a/iterations/v3-mcp-support/README.md b/iterations/v3-mcp-support/README.md index 82bab8e1..ffa9ac89 100644 --- a/iterations/v3-mcp-support/README.md +++ b/iterations/v3-mcp-support/README.md @@ -187,6 +187,7 @@ CREATE TABLE site_pages ( - `utils/`: Utility functions and database setup - `utils.py`: Shared utility functions - `site_pages.sql`: Database setup commands + - `site_pages_ollama.sql`: Database setup commands with vector dimensions updated for nomic-embed-text ### Runtime - `workbench/`: Runtime files and logs diff --git a/streamlit_ui.py b/streamlit_ui.py index 8d20ddf7..c2dd78ac 100644 --- a/streamlit_ui.py +++ b/streamlit_ui.py @@ -33,7 +33,17 @@ from archon.archon_graph import agentic_flow from dotenv import load_dotenv load_dotenv() -openai_client = AsyncOpenAI(api_key=os.getenv("OPENAI_API_KEY")) + +openai_client=None +base_url = os.getenv('BASE_URL', 'https://api.openai.com/v1') +api_key = os.getenv('LLM_API_KEY', 'no-llm-api-key-provided') +is_ollama = "localhost" in base_url.lower() + +if is_ollama: + openai_client = AsyncOpenAI(base_url=base_url,api_key=api_key) +else: + openai_client = AsyncOpenAI(api_key=os.getenv("OPENAI_API_KEY")) + supabase: Client = Client( os.getenv("SUPABASE_URL"), os.getenv("SUPABASE_SERVICE_KEY")