├── config.py ├── auth ├── __init__.py ├── handlers.py └── forms.py ├── tests ├── __init__.py └── test_main.py ├── utils ├── __init__.py └── supabase_client.py ├── src └── pages │ ├── __init__.py │ ├── home.py │ ├── profile.py │ ├── settings.py │ └── chat.py ├── index ├── graph_store.json ├── image__vector_store.json ├── index_store.json ├── docstore.json └── default__vector_store.json ├── fastapi.zip ├── fastapi ├── requirements.txt ├── app │ ├── db │ │ └── supabase.py │ ├── models │ │ └── user.py │ ├── api │ │ ├── profile.py │ │ └── auth.py │ ├── sql │ │ └── init.sql │ └── services │ │ └── llama_index.py ├── api.py ├── main.py ├── supabase_setup.sql ├── readme.md ├── api copy.py └── install.sh ├── requirements.txt ├── setup.py ├── pyproject.toml ├── LICENSE ├── sql └── init.sql ├── install.sh ├── data └── data.md ├── main.py ├── .gitignore └── README.md /config.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /auth/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_main.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /index/graph_store.json: -------------------------------------------------------------------------------- 1 | {"graph_dict": {}} -------------------------------------------------------------------------------- /fastapi.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruvnet/supabase-authentication/HEAD/fastapi.zip -------------------------------------------------------------------------------- /index/image__vector_store.json: -------------------------------------------------------------------------------- 1 | {"embedding_dict": {}, "text_id_to_ref_doc_id": {}, "metadata_dict": {}} -------------------------------------------------------------------------------- /fastapi/requirements.txt: -------------------------------------------------------------------------------- 1 | fastapi 2 | uvicorn 3 | supabase 4 | llama-index 5 | llama-index-vector-stores-supabase -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | streamlit>=1.0 2 | supabase>=0.4.3 3 | httpx>=0.18 4 | pandas>=1.1 5 | fastapi 6 | python-multipart 7 | uvicorn 8 | llama-index 9 | llama-index-vector-stores-supabase -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | name="streamlit-supabase-app", 5 | version="0.1.0", 6 | packages=find_packages(), 7 | include_package_data=True, 8 | install_requires=[ 9 | "streamlit>=1.12.0", 10 | "supabase>=1.0.3", 11 | ], 12 | ) 13 | -------------------------------------------------------------------------------- /index/index_store.json: -------------------------------------------------------------------------------- 1 | {"index_store/data": {"c9a08082-fb99-4b9b-9166-83ebe76aa2d9": {"__type__": "vector_store", "__data__": "{\"index_id\": \"c9a08082-fb99-4b9b-9166-83ebe76aa2d9\", \"summary\": null, \"nodes_dict\": {\"36815fa0-0599-4a15-82ad-eb4a9ba6a7cf\": \"36815fa0-0599-4a15-82ad-eb4a9ba6a7cf\"}, \"doc_id_dict\": {}, \"embeddings_dict\": {}}"}}} -------------------------------------------------------------------------------- /fastapi/app/db/supabase.py: -------------------------------------------------------------------------------- 1 | import os 2 | from supabase import create_client, Client 3 | 4 | SUPABASE_URL = os.getenv("SUPABASE_URL") 5 | SUPABASE_KEY = os.getenv("SUPABASE_KEY") 6 | 7 | if not SUPABASE_URL or not SUPABASE_KEY: 8 | raise ValueError("SUPABASE_URL and SUPABASE_KEY must be set in environment variables") 9 | 10 | supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY) -------------------------------------------------------------------------------- /src/pages/home.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | def show_home(user): 4 | if user: 5 | st.subheader("Welcome to Your Dashboard") 6 | st.write("Here's some personalized content for logged-in users.") 7 | # Add more personalized content or features here 8 | else: 9 | st.subheader("Welcome to the Home Page") 10 | st.write("Please login or register to access more features.") 11 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "streamlit-supabase-app" 3 | version = "0.1.0" 4 | description = "A Streamlit app with Supabase authentication" 5 | authors = ["Your Name "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.8" 9 | streamlit = "^1.12.0" 10 | supabase = "^1.0.3" 11 | 12 | [tool.poetry.dev-dependencies] 13 | pytest = "^7.1.2" 14 | 15 | [build-system] 16 | requires = ["poetry-core>=1.0.0"] 17 | build-backend = "poetry.core.masonry.api" 18 | -------------------------------------------------------------------------------- /fastapi/api.py: -------------------------------------------------------------------------------- 1 | import uvicorn 2 | import logging 3 | from fastapi import FastAPI 4 | from fastapi.responses import RedirectResponse 5 | from app.api import auth, profile 6 | 7 | app = FastAPI() 8 | 9 | @app.get("/") 10 | async def redirect_to_docs(): 11 | return RedirectResponse(url="/docs") 12 | 13 | app.include_router(auth.router) 14 | app.include_router(profile.router) 15 | 16 | if __name__ == "__main__": 17 | # logging.basicConfig(level=logging.DEBUG) 18 | # uvicorn.run(app, host="0.0.0.0", port=8000, log_level="debug") 19 | uvicorn.run(app, host="0.0.0.0", port=8000) -------------------------------------------------------------------------------- /fastapi/main.py: -------------------------------------------------------------------------------- 1 | import uvicorn 2 | import logging 3 | from fastapi import FastAPI 4 | from fastapi.responses import RedirectResponse 5 | from app.api import auth, profile 6 | 7 | app = FastAPI() 8 | 9 | @app.get("/") 10 | async def redirect_to_docs(): 11 | return RedirectResponse(url="/docs") 12 | 13 | app.include_router(auth.router) 14 | app.include_router(profile.router) 15 | 16 | if __name__ == "__main__": 17 | # logging.basicConfig(level=logging.DEBUG) 18 | # uvicorn.run(app, host="0.0.0.0", port=8000, log_level="debug") 19 | uvicorn.run(app, host="0.0.0.0", port=8000) -------------------------------------------------------------------------------- /auth/handlers.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | from utils.supabase_client import supabase 3 | 4 | def handle_confirmation(): 5 | token = st.query_params.get("confirmation_token") 6 | 7 | if token: 8 | try: 9 | res = supabase.auth.verify_otp({"token": token[0], "type": "signup"}) 10 | st.success("Email confirmed successfully! You can now log in.") 11 | except Exception as e: 12 | st.error(f"Confirmation failed: {str(e)}") 13 | else: 14 | st.warning("No confirmation token found in the URL.") 15 | 16 | def logout(): 17 | supabase.auth.sign_out() 18 | st.session_state.clear() 19 | st.session_state.logged_out = True 20 | st.rerun() -------------------------------------------------------------------------------- /fastapi/app/models/user.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel 2 | from typing import Optional 3 | 4 | class User(BaseModel): 5 | id: str 6 | email: str 7 | full_name: Optional[str] = None 8 | bio: Optional[str] = None 9 | age: Optional[int] = None 10 | theme: Optional[str] = "light" 11 | notifications: Optional[bool] = True 12 | language: Optional[str] = "en" 13 | 14 | class Settings(BaseModel): 15 | full_name: Optional[str] = None 16 | bio: Optional[str] = None 17 | age: Optional[int] = None 18 | theme: Optional[str] = "light" 19 | notifications: Optional[bool] = True 20 | language: Optional[str] = "en" 21 | 22 | class UserCreate(BaseModel): 23 | email: str 24 | password: str 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 rUv 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /fastapi/supabase_setup.sql: -------------------------------------------------------------------------------- 1 | -- Enable the pgvector extension 2 | CREATE EXTENSION IF NOT EXISTS vector; 3 | 4 | -- Create the profiles table 5 | CREATE TABLE IF NOT EXISTS profiles ( 6 | id UUID PRIMARY KEY REFERENCES auth.users(id), 7 | full_name TEXT, 8 | bio TEXT, 9 | age INT, 10 | theme TEXT DEFAULT 'light', 11 | notifications BOOLEAN DEFAULT true, 12 | language TEXT DEFAULT 'en' 13 | ); 14 | 15 | -- Create a function to handle new user signups 16 | CREATE OR REPLACE FUNCTION public.handle_new_user() 17 | RETURNS TRIGGER AS $$ 18 | BEGIN 19 | INSERT INTO public.profiles (id) 20 | VALUES (NEW.id); 21 | RETURN NEW; 22 | END; 23 | $$ LANGUAGE plpgsql SECURITY DEFINER; 24 | 25 | -- Create a trigger to call the function on new user signups 26 | CREATE TRIGGER on_auth_user_created 27 | AFTER INSERT ON auth.users 28 | FOR EACH ROW EXECUTE FUNCTION public.handle_new_user(); 29 | 30 | -- Create a table for storing user-specific vector indexes 31 | CREATE TABLE IF NOT EXISTS user_indexes ( 32 | id UUID PRIMARY KEY REFERENCES auth.users(id), 33 | index_name TEXT NOT NULL, 34 | embedding VECTOR(1536) 35 | ); 36 | 37 | -- Create an index on the embedding column for faster similarity searches 38 | CREATE INDEX ON user_indexes USING ivfflat (embedding vector_cosine_ops); 39 | -------------------------------------------------------------------------------- /sql/init.sql: -------------------------------------------------------------------------------- 1 | -- Create the profiles table 2 | CREATE TABLE profiles ( 3 | id uuid PRIMARY KEY DEFAULT gen_random_uuid(), 4 | user_id uuid REFERENCES auth.users(id) ON DELETE CASCADE, 5 | full_name text, 6 | email text, 7 | bio text, 8 | age integer, 9 | theme text DEFAULT 'light', 10 | notifications boolean DEFAULT true, 11 | language text DEFAULT 'en', 12 | created_at timestamp with time zone DEFAULT timezone('utc'::text, now()) NOT NULL, 13 | updated_at timestamp with time zone DEFAULT timezone('utc'::text, now()) NOT NULL 14 | ); 15 | 16 | -- Create a function to automatically update the 'updated_at' column 17 | CREATE OR REPLACE FUNCTION update_modified_column() 18 | RETURNS TRIGGER AS $$ 19 | BEGIN 20 | NEW.updated_at = now(); 21 | RETURN NEW; 22 | END; 23 | $$ 24 | language 'plpgsql'; 25 | 26 | -- Create a trigger to call this function whenever a row is updated 27 | CREATE TRIGGER update_profiles_modtime 28 | BEFORE UPDATE ON profiles 29 | FOR EACH ROW 30 | EXECUTE FUNCTION update_modified_column(); 31 | 32 | -- Create an index on user_id for faster lookups 33 | CREATE INDEX idx_profiles_user_id ON profiles(user_id); 34 | 35 | -- Add a unique constraint on user_id to ensure one profile per user 36 | ALTER TABLE profiles ADD CONSTRAINT unique_user_profile UNIQUE (user_id); -------------------------------------------------------------------------------- /utils/supabase_client.py: -------------------------------------------------------------------------------- 1 | import os 2 | import streamlit as st 3 | from supabase import create_client, Client 4 | 5 | @st.cache_resource 6 | def init_supabase(): 7 | url = os.environ.get("SUPABASE_URL") or st.secrets.get("SUPABASE_URL") 8 | key = os.environ.get("SUPABASE_KEY") or st.secrets.get("SUPABASE_KEY") 9 | 10 | if not url or not key: 11 | st.error("Supabase URL and API key must be set in environment variables or Streamlit secrets.") 12 | st.stop() 13 | 14 | return create_client(url, key) 15 | 16 | supabase: Client = init_supabase() 17 | 18 | # Function to check if the profile exists for a user 19 | def check_profile_exists(user_id): 20 | try: 21 | response = supabase.from_("profiles").select("*").eq("user_id", user_id).execute() 22 | return len(response.data) > 0 23 | except Exception as e: 24 | st.error(f"Failed to check profile existence: {str(e)}") 25 | return False 26 | 27 | # Function to insert initial profile data 28 | def insert_initial_profile(user_id, full_name="New User", bio="This is your bio."): 29 | try: 30 | supabase.from_("profiles").insert({ 31 | "user_id": user_id, 32 | "full_name": full_name, 33 | "bio": bio 34 | }).execute() 35 | st.success("Initial profile created successfully!") 36 | except Exception as e: 37 | st.error(f"Failed to insert initial profile data: {str(e)}") 38 | 39 | # Main function to check and create profile if necessary 40 | def ensure_profile_exists(user): 41 | if not check_profile_exists(user.id): 42 | insert_initial_profile(user.id) -------------------------------------------------------------------------------- /auth/forms.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | from utils.supabase_client import supabase 3 | 4 | def registration_form(): 5 | st.subheader("Register") 6 | email = st.text_input("Email") 7 | password = st.text_input("Password", type="password") 8 | confirm_password = st.text_input("Confirm Password", type="password") 9 | 10 | if st.button("Register"): 11 | if password == confirm_password: 12 | try: 13 | res = supabase.auth.sign_up({"email": email, "password": password}) 14 | st.success("Registration successful! Please check your email to verify your account.") 15 | except Exception as e: 16 | st.error(f"Registration failed: {str(e)}") 17 | else: 18 | st.error("Passwords do not match") 19 | 20 | def login_form(): 21 | st.subheader("Login") 22 | email = st.text_input("Email") 23 | password = st.text_input("Password", type="password") 24 | 25 | if st.button("Login"): 26 | try: 27 | res = supabase.auth.sign_in_with_password({"email": email, "password": password}) 28 | st.session_state.user = res.user 29 | st.success("Login successful!") 30 | st.rerun() 31 | except Exception as e: 32 | st.error(f"Login failed: {str(e)}") 33 | 34 | def password_reset_form(): 35 | st.subheader("Reset Password") 36 | email = st.text_input("Email") 37 | 38 | if st.button("Send Reset Link"): 39 | try: 40 | res = supabase.auth.reset_password_email(email) 41 | st.success("Password reset link sent to your email!") 42 | except Exception as e: 43 | st.error(f"Failed to send reset link: {str(e)}") 44 | -------------------------------------------------------------------------------- /index/docstore.json: -------------------------------------------------------------------------------- 1 | {"docstore/metadata": {"210c05e0-d41d-480f-ab17-20e3a4a1d9cc": {"doc_hash": "f6f0ea57d2e12ddd6fb0171cf57b473373b8024097cb25e582535db859b5b62d"}, "36815fa0-0599-4a15-82ad-eb4a9ba6a7cf": {"doc_hash": "f6f0ea57d2e12ddd6fb0171cf57b473373b8024097cb25e582535db859b5b62d", "ref_doc_id": "210c05e0-d41d-480f-ab17-20e3a4a1d9cc"}}, "docstore/data": {"36815fa0-0599-4a15-82ad-eb4a9ba6a7cf": {"__data__": {"id_": "36815fa0-0599-4a15-82ad-eb4a9ba6a7cf", "embedding": null, "metadata": {"file_path": "/workspaces/supabase-authentication/data/data.md", "file_name": "data.md", "file_type": "text/markdown", "file_size": 0, "creation_date": "2024-08-30", "last_modified_date": "2024-08-30"}, "excluded_embed_metadata_keys": ["file_name", "file_type", "file_size", "creation_date", "last_modified_date", "last_accessed_date"], "excluded_llm_metadata_keys": ["file_name", "file_type", "file_size", "creation_date", "last_modified_date", "last_accessed_date"], "relationships": {"1": {"node_id": "210c05e0-d41d-480f-ab17-20e3a4a1d9cc", "node_type": "4", "metadata": {"file_path": "/workspaces/supabase-authentication/data/data.md", "file_name": "data.md", "file_type": "text/markdown", "file_size": 0, "creation_date": "2024-08-30", "last_modified_date": "2024-08-30"}, "hash": "f6f0ea57d2e12ddd6fb0171cf57b473373b8024097cb25e582535db859b5b62d", "class_name": "RelatedNodeInfo"}}, "text": "", "mimetype": "text/plain", "start_char_idx": 0, "end_char_idx": 0, "text_template": "{metadata_str}\n\n{content}", "metadata_template": "{key}: {value}", "metadata_seperator": "\n", "class_name": "TextNode"}, "__type__": "1"}}, "docstore/ref_doc_info": {"210c05e0-d41d-480f-ab17-20e3a4a1d9cc": {"node_ids": ["36815fa0-0599-4a15-82ad-eb4a9ba6a7cf"], "metadata": {"file_path": "/workspaces/supabase-authentication/data/data.md", "file_name": "data.md", "file_type": "text/markdown", "file_size": 0, "creation_date": "2024-08-30", "last_modified_date": "2024-08-30"}}}} -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Create the main files 4 | touch main.py config.py setup.py 5 | 6 | # Create pyproject.toml for Poetry 7 | cat << EOF > pyproject.toml 8 | [tool.poetry] 9 | name = "streamlit-supabase-app" 10 | version = "0.1.0" 11 | description = "A Streamlit app with Supabase authentication" 12 | authors = ["Your Name "] 13 | 14 | [tool.poetry.dependencies] 15 | python = "^3.8" 16 | streamlit = "^1.12.0" 17 | supabase = "^1.0.3" 18 | 19 | [tool.poetry.dev-dependencies] 20 | pytest = "^7.1.2" 21 | 22 | [build-system] 23 | requires = ["poetry-core>=1.0.0"] 24 | build-backend = "poetry.core.masonry.api" 25 | EOF 26 | 27 | # Create setup.py 28 | cat << EOF > setup.py 29 | from setuptools import setup, find_packages 30 | 31 | setup( 32 | name="streamlit-supabase-app", 33 | version="0.1.0", 34 | packages=find_packages(), 35 | include_package_data=True, 36 | install_requires=[ 37 | "streamlit>=1.12.0", 38 | "supabase>=1.0.3", 39 | ], 40 | ) 41 | EOF 42 | 43 | # Create utils directory and its files 44 | mkdir -p utils 45 | touch utils/__init__.py utils/supabase_client.py 46 | 47 | # Create auth directory and its files 48 | mkdir -p auth 49 | touch auth/__init__.py auth/forms.py auth/handlers.py 50 | 51 | # Create pages directory and its files 52 | mkdir -p pages 53 | touch pages/__init__.py pages/home.py pages/profile.py pages/settings.py 54 | 55 | # Create tests directory 56 | mkdir -p tests 57 | touch tests/__init__.py tests/test_main.py 58 | 59 | echo "Project structure created successfully!" 60 | 61 | # Print the directory structure 62 | echo "Directory structure:" 63 | if command -v tree &> /dev/null; then 64 | tree 65 | else 66 | find . -print | sed -e 's;[^/]*/;|____;g;s;____|; |;g' 67 | fi 68 | 69 | echo "You can now copy the content into each file." 70 | echo "Remember to update the pyproject.toml and setup.py files with your specific project details." -------------------------------------------------------------------------------- /data/data.md: -------------------------------------------------------------------------------- 1 | # AI and Machine Learning Overview 2 | 3 | ## Introduction to AI 4 | Artificial Intelligence (AI) is the simulation of human intelligence processes by machines, especially computer systems. These processes include learning, reasoning, and self-correction. 5 | 6 | ## author 7 | Reuven Cohen, Ai Consultant 8 | 9 | ## Machine Learning 10 | Machine Learning is a subset of AI that focuses on the development of algorithms that can access data and use it to learn for themselves. 11 | 12 | ### Types of Machine Learning 13 | 1. Supervised Learning 14 | 2. Unsupervised Learning 15 | 3. Reinforcement Learning 16 | 17 | ## Deep Learning 18 | Deep Learning is a subset of Machine Learning that uses artificial neural networks with multiple layers (deep neural networks) to analyze various factors of data. 19 | 20 | ## Applications of AI and ML 21 | - Healthcare: Disease diagnosis and drug discovery 22 | - Finance: Fraud detection and algorithmic trading 23 | - Automotive: Self-driving cars 24 | - Natural Language Processing: Chatbots and language translation 25 | 26 | ## Ethical Considerations 27 | As AI becomes more prevalent, it's crucial to consider ethical implications such as: 28 | - Privacy concerns 29 | - Bias in AI systems 30 | - Job displacement 31 | - AI safety and control 32 | 33 | ## Future of AI 34 | The future of AI is expected to bring significant advancements in: 35 | - Quantum AI 36 | - Explainable AI (XAI) 37 | - AI in edge computing 38 | - Human-AI collaboration 39 | 40 | ## Getting Started with AI 41 | To start learning AI, consider the following steps: 42 | 1. Learn Python programming 43 | 2. Study mathematics, especially linear algebra and calculus 44 | 3. Understand basic statistics and probability 45 | 4. Start with machine learning libraries like scikit-learn 46 | 5. Move on to deep learning frameworks like TensorFlow or PyTorch 47 | 48 | Remember, the field of AI is vast and constantly evolving. Continuous learning is key to staying up-to-date with the latest developments. -------------------------------------------------------------------------------- /fastapi/app/api/profile.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | from fastapi import APIRouter, Depends, HTTPException 4 | from app.models.user import User, Settings 5 | from app.api.auth import get_current_user 6 | from app.db.supabase import supabase 7 | from app.services.llama_index import get_user_index, query_user_index 8 | 9 | router = APIRouter() 10 | 11 | @router.get("/profile", response_model=User) 12 | async def read_profile(current_user: dict = Depends(get_current_user)): 13 | try: 14 | response = supabase.table("profiles").select("*").eq("user_id", current_user.id).execute() 15 | if not response.data: 16 | raise HTTPException(status_code=404, detail="Profile not found") 17 | profile = response.data[0] 18 | return User(**profile) 19 | except Exception as e: 20 | raise HTTPException(status_code=400, detail=f"Failed to fetch profile: {str(e)}") 21 | 22 | @router.put("/settings", response_model=User) 23 | async def update_settings(settings: Settings, current_user: dict = Depends(get_current_user)): 24 | try: 25 | response = supabase.table("profiles").update(settings.dict(exclude_unset=True)).eq("user_id", current_user.id).execute() 26 | if not response.data: 27 | raise HTTPException(status_code=404, detail="Profile not found") 28 | updated_profile = response.data[0] 29 | return User(**updated_profile) 30 | except Exception as e: 31 | raise HTTPException(status_code=400, detail=f"Failed to update settings: {str(e)}") 32 | 33 | 34 | logging.basicConfig(level=logging.DEBUG) 35 | logger = logging.getLogger(__name__) 36 | 37 | @router.post("/index") 38 | async def create_user_index(current_user: User = Depends(get_current_user)): 39 | try: 40 | logger.info(f"Attempting to create index for user: {current_user.id}") 41 | logger.debug(f"SUPABASE_URL set: {'SUPABASE_URL' in os.environ}") 42 | logger.debug(f"OPENAI_API_KEY set: {'OPENAI_API_KEY' in os.environ}") 43 | 44 | index = get_user_index(current_user.id) 45 | logger.info(f"Index created successfully for user: {current_user.id}") 46 | return {"message": "User index created successfully", "user_id": current_user.id} 47 | except Exception as e: 48 | logger.exception(f"Failed to create user index for user {current_user.id}") 49 | raise HTTPException(status_code=500, detail=f"Failed to create user index: {str(e)}") -------------------------------------------------------------------------------- /fastapi/readme.md: -------------------------------------------------------------------------------- 1 | # FastAPI Supabase Authentication App 2 | 3 | This FastAPI application demonstrates user authentication using Supabase as the backend. 4 | 5 | ## Prerequisites 6 | 7 | - Python 3.7+ 8 | - FastAPI 9 | - Supabase account and project 10 | 11 | ## Setup 12 | 13 | 1. Clone the repository to your local machine. 14 | 15 | 2. Install the required dependencies: 16 | ``` 17 | pip install fastapi uvicorn supabase python-multipart 18 | ``` 19 | 20 | 3. Set up your Supabase project: 21 | - Create a new project in Supabase 22 | - Enable Email Auth in Authentication > Providers 23 | 24 | 4. Create a `.env` file in the root directory with your Supabase credentials: 25 | ``` 26 | SUPABASE_URL=your_supabase_project_url 27 | SUPABASE_KEY=your_supabase_anon_key 28 | ``` 29 | 30 | ## Running the App 31 | 32 | To run the FastAPI app, use the following command in your terminal: 33 | 34 | ``` 35 | uvicorn main:app --reload 36 | ``` 37 | 38 | The app should now be running on `http://localhost:8000`. 39 | 40 | ## API Documentation 41 | 42 | Once the app is running, you can view the automatic API documentation at: 43 | 44 | ``` 45 | http://localhost:8000/docs 46 | ``` 47 | 48 | This will show you all available endpoints and allow you to test them directly from the browser. 49 | 50 | ## Authentication 51 | 52 | The app uses OAuth2 password flow for authentication. To log in: 53 | 54 | 1. Navigate to the `/login` endpoint in the Swagger UI. 55 | 2. Use the following parameters in the request body: 56 | - `grant_type`: Set to "password" 57 | - `username`: Your email address 58 | - `password`: Your password 59 | - `scope`: Can be left empty 60 | - `client_id`: Can be left empty 61 | - `client_secret`: Can be left empty 62 | 63 | 3. Execute the request to receive an access token. 64 | 65 | Use this access token in the "Authorize" button at the top of the Swagger UI to authenticate other endpoints. 66 | 67 | ## Available Endpoints 68 | 69 | - `/login`: Authenticate and receive an access token 70 | - `/register`: Create a new user account 71 | - `/profile`: Get the current user's profile 72 | - `/settings`: Update user settings 73 | - `/reset-password`: Request a password reset email 74 | - `/confirm`: Confirm email address after registration 75 | 76 | ## Security Note 77 | 78 | This app is for demonstration purposes. In a production environment, ensure you follow best practices for security, including proper error handling and input validation. 79 | -------------------------------------------------------------------------------- /src/pages/profile.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | from supabase import create_client 3 | from utils.supabase_client import supabase 4 | 5 | def fetch_user_profile(user_id): 6 | try: 7 | response = supabase.from_("profiles").select("*").eq("user_id", user_id).execute() 8 | if response.data: 9 | return response.data[0] 10 | return None 11 | except Exception as e: 12 | st.error(f"Failed to load profile data: {str(e)}") 13 | return None 14 | 15 | def update_user_profile(user_id, full_name, bio): 16 | try: 17 | supabase.from_("profiles").update({ 18 | "full_name": full_name, 19 | "bio": bio 20 | }).eq("user_id", user_id).execute() 21 | st.success("Profile updated successfully!") 22 | except Exception as e: 23 | st.error(f"Failed to update profile data: {str(e)}") 24 | 25 | def insert_initial_profile(user_id, full_name="New User", bio="This is your bio."): 26 | try: 27 | supabase.from_("profiles").insert({ 28 | "user_id": user_id, 29 | "full_name": full_name, 30 | "bio": bio 31 | }).execute() 32 | st.success("Profile created successfully!") 33 | return {"full_name": full_name, "bio": bio} 34 | except Exception as e: 35 | st.error(f"Failed to insert initial profile data: {str(e)}") 36 | return None 37 | 38 | def show_profile(user): 39 | st.subheader("Your Profile") 40 | st.write(f"Email: {user.email}") 41 | 42 | profile_data = fetch_user_profile(user.id) 43 | 44 | if not profile_data: 45 | st.warning("No profile found. Let's create one!") 46 | full_name = st.text_input("Full Name", value="New User") 47 | bio = st.text_area("Bio", value="This is your bio.") 48 | if st.button("Create Profile"): 49 | profile_data = insert_initial_profile(user.id, full_name, bio) 50 | 51 | if profile_data: 52 | full_name = st.text_input("Full Name", value=profile_data.get("full_name", "")) 53 | bio = st.text_area("Bio", value=profile_data.get("bio", "")) 54 | 55 | if st.button("Update Profile"): 56 | update_user_profile(user.id, full_name, bio) 57 | 58 | # This function should be called from your main.py or wherever you handle the user's session 59 | def handle_profile(user): 60 | if user: 61 | show_profile(user) 62 | else: 63 | st.warning("Please log in to view and edit your profile.") -------------------------------------------------------------------------------- /fastapi/app/sql/init.sql: -------------------------------------------------------------------------------- 1 | -- Enable the pgvector extension 2 | CREATE EXTENSION IF NOT EXISTS vector; 3 | 4 | -- Create a table to store documents 5 | CREATE TABLE IF NOT EXISTS documents ( 6 | id BIGSERIAL PRIMARY KEY, 7 | title TEXT, 8 | body TEXT, 9 | embedding VECTOR(384) 10 | ); 11 | 12 | -- Create an index on the embedding column for faster similarity searches 13 | CREATE INDEX ON documents USING ivfflat (embedding vector_cosine_ops); 14 | 15 | -- Create a function to match documents 16 | CREATE OR REPLACE FUNCTION match_documents ( 17 | query_embedding VECTOR(384), 18 | match_threshold FLOAT, 19 | match_count INT 20 | ) 21 | RETURNS TABLE ( 22 | id BIGINT, 23 | title TEXT, 24 | body TEXT, 25 | similarity FLOAT 26 | ) 27 | LANGUAGE SQL STABLE 28 | AS $$ 29 | SELECT 30 | documents.id, 31 | documents.title, 32 | documents.body, 33 | 1 - (documents.embedding <=> query_embedding) AS similarity 34 | FROM documents 35 | WHERE 1 - (documents.embedding <=> query_embedding) > match_threshold 36 | ORDER BY documents.embedding <=> query_embedding 37 | LIMIT match_count; 38 | $$ 39 | ; 40 | 41 | -- Create a table to store user-specific indexes 42 | CREATE TABLE IF NOT EXISTS user_indexes ( 43 | id UUID PRIMARY KEY REFERENCES auth.users(id), 44 | index_name TEXT NOT NULL, 45 | embedding VECTOR(384) 46 | ); 47 | 48 | -- Create an index on the embedding column for user-specific indexes 49 | CREATE INDEX ON user_indexes USING ivfflat (embedding vector_cosine_ops); 50 | 51 | -- Create a function to handle new user signups 52 | CREATE OR REPLACE FUNCTION public.handle_new_user() 53 | RETURNS TRIGGER AS $$ 54 | BEGIN 55 | INSERT INTO public.user_indexes (id, index_name) 56 | VALUES (NEW.id, 'default_index'); 57 | RETURN NEW; 58 | END; 59 | $$ 60 | LANGUAGE plpgsql SECURITY DEFINER; 61 | 62 | -- Create a trigger to call the function on new user signups 63 | CREATE TRIGGER on_auth_user_created 64 | AFTER INSERT ON auth.users 65 | FOR EACH ROW EXECUTE FUNCTION public.handle_new_user(); 66 | 67 | -- Enable Row Level Security (RLS) on the documents table 68 | ALTER TABLE documents ENABLE ROW LEVEL SECURITY; 69 | 70 | -- Create a policy that allows users to see only their own documents 71 | CREATE POLICY "Users can only access their own documents" ON documents 72 | FOR ALL USING (auth.uid() = user_id); 73 | 74 | -- Add a user_id column to the documents table to associate documents with users 75 | ALTER TABLE documents ADD COLUMN user_id UUID REFERENCES auth.users(id); -------------------------------------------------------------------------------- /fastapi/app/api/auth.py: -------------------------------------------------------------------------------- 1 | from fastapi import APIRouter, Depends, HTTPException, Form 2 | from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm 3 | from app.db.supabase import supabase 4 | from app.models.user import UserCreate, User 5 | 6 | router = APIRouter() 7 | 8 | oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/login") 9 | 10 | async def get_current_user(token: str = Depends(oauth2_scheme)): 11 | try: 12 | user = supabase.auth.get_user(token) 13 | return user.user 14 | except Exception as e: 15 | raise HTTPException(status_code=401, detail="Invalid authentication credentials") 16 | 17 | @router.post("/login") 18 | async def login(form_data: OAuth2PasswordRequestForm = Depends()): 19 | try: 20 | res = supabase.auth.sign_in_with_password({"email": form_data.username, "password": form_data.password}) 21 | return {"access_token": res.session.access_token, "token_type": "bearer"} 22 | except Exception as e: 23 | raise HTTPException(status_code=400, detail=f"Login failed: {str(e)}") 24 | 25 | @router.post("/register") 26 | async def register(user: UserCreate): 27 | try: 28 | res = supabase.auth.sign_up({"email": user.email, "password": user.password}) 29 | return {"message": "Registration successful! Please check your email to verify your account."} 30 | except Exception as e: 31 | raise HTTPException(status_code=400, detail=f"Registration failed: {str(e)}") 32 | 33 | @router.post("/reset-password") 34 | async def reset_password(email: str = Form(...)): 35 | try: 36 | res = supabase.auth.reset_password_email(email) 37 | return {"message": "Password reset link sent to your email!"} 38 | except Exception as e: 39 | raise HTTPException(status_code=400, detail=f"Failed to send reset link: {str(e)}") 40 | 41 | @router.get("/confirm") 42 | async def confirm(token: str): 43 | try: 44 | res = supabase.auth.verify_otp({"token": token, "type": "email"}) 45 | return {"message": "Email confirmed successfully! You can now log in."} 46 | except Exception as e: 47 | raise HTTPException(status_code=400, detail=f"Confirmation failed: {str(e)}") 48 | 49 | @router.post("/logout") 50 | async def logout(token: str = Depends(oauth2_scheme)): 51 | try: 52 | supabase.auth.sign_out(token) 53 | return {"message": "Logged out successfully!"} 54 | except Exception as e: 55 | raise HTTPException(status_code=400, detail=f"Logout failed: {str(e)}") 56 | -------------------------------------------------------------------------------- /src/pages/settings.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | from supabase import create_client 3 | from utils.supabase_client import supabase 4 | 5 | def show_settings(user): 6 | st.subheader("Settings") 7 | 8 | # Fetch current user settings 9 | try: 10 | response = supabase.from_("profiles").select("*").eq("user_id", user.id).execute() 11 | if response.data: 12 | current_settings = response.data[0] 13 | else: 14 | st.warning("No profile found. Please create a profile first.") 15 | return 16 | except Exception as e: 17 | st.error(f"Failed to load user settings: {str(e)}") 18 | return 19 | 20 | # Settings form 21 | with st.form("user_settings"): 22 | full_name = st.text_input("Full Name", value=current_settings.get("full_name", "")) 23 | email = st.text_input("Email", value=current_settings.get("email", user.email)) 24 | bio = st.text_area("Bio", value=current_settings.get("bio", "")) 25 | age = st.number_input("Age", value=current_settings.get("age", 0), min_value=0, max_value=120) 26 | theme = st.selectbox("Theme", ["light", "dark"], index=0 if current_settings.get("theme", "light") == "light" else 1) 27 | notifications = st.checkbox("Enable Notifications", value=current_settings.get("notifications", True)) 28 | language = st.selectbox("Language", ["en", "es", "fr", "de"], index=["en", "es", "fr", "de"].index(current_settings.get("language", "en"))) 29 | 30 | submit_button = st.form_submit_button("Update Settings") 31 | 32 | if submit_button: 33 | try: 34 | # Update user settings in Supabase 35 | supabase.table("profiles").update({ 36 | "full_name": full_name, 37 | "email": email, 38 | "bio": bio, 39 | "age": age, 40 | "theme": theme, 41 | "notifications": notifications, 42 | "language": language 43 | }).eq("user_id", user.id).execute() 44 | 45 | st.success("Settings updated successfully!") 46 | except Exception as e: 47 | st.error(f"Failed to update settings: {str(e)}") 48 | 49 | # Display current settings 50 | st.subheader("Current Settings") 51 | st.json(current_settings) 52 | 53 | # This function should be called from your main.py or wherever you handle the user's session 54 | def handle_settings(user): 55 | if user: 56 | show_settings(user) 57 | else: 58 | st.warning("Please log in to view and edit your settings.") -------------------------------------------------------------------------------- /fastapi/app/services/llama_index.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from llama_index.core import VectorStoreIndex, StorageContext 3 | from llama_index.vector_stores.supabase import SupabaseVectorStore 4 | from app.db.supabase import SUPABASE_URL, SUPABASE_KEY 5 | import os 6 | from urllib.parse import urlparse 7 | import asyncio 8 | from asyncio import TimeoutError 9 | logger = logging.getLogger(__name__) 10 | 11 | async def get_user_index_with_timeout(user_id: str, timeout: int = 30): 12 | try: 13 | return await asyncio.wait_for(get_user_index(user_id), timeout=timeout) 14 | except TimeoutError: 15 | logger.error(f"Timeout occurred while creating index for user {user_id}") 16 | raise HTTPException(status_code=504, detail="Index creation timed out") 17 | 18 | def get_user_index(user_id: str): 19 | logger.info(f"Starting get_user_index for user: {user_id}") 20 | 21 | if 'OPENAI_API_KEY' not in os.environ: 22 | logger.error("OPENAI_API_KEY is not set in the environment variables") 23 | raise ValueError("OPENAI_API_KEY is not set in the environment variables") 24 | 25 | try: 26 | parsed_url = urlparse(SUPABASE_URL) 27 | DB_CONNECTION = f"postgresql://{parsed_url.username}:{parsed_url.password}@{parsed_url.hostname}:{parsed_url.port or 5432}/{parsed_url.path.lstrip('/')}" 28 | logger.debug(f"Constructed DB_CONNECTION (sensitive info redacted): postgresql://{parsed_url.username}:****@{parsed_url.hostname}:{parsed_url.port or 5432}/{parsed_url.path.lstrip('/')}") 29 | 30 | logger.info("Initializing SupabaseVectorStore") 31 | vector_store = SupabaseVectorStore( 32 | postgres_connection_string=DB_CONNECTION, 33 | collection_name=f'user_collection_{user_id}' 34 | ) 35 | 36 | logger.info("Creating StorageContext") 37 | storage_context = StorageContext.from_defaults(vector_store=vector_store) 38 | 39 | logger.info("Creating VectorStoreIndex") 40 | index = VectorStoreIndex.from_documents([], storage_context=storage_context) 41 | 42 | logger.info(f"Successfully created index for user: {user_id}") 43 | return index 44 | except Exception as e: 45 | logger.exception(f"Error in get_user_index for user {user_id}") 46 | raise 47 | 48 | def query_user_index(user_id: str, query: str): 49 | logger.info(f"Starting query_user_index for user: {user_id}") 50 | try: 51 | index = get_user_index(user_id) 52 | query_engine = index.as_query_engine() 53 | response = query_engine.query(query) 54 | logger.info(f"Successfully queried index for user: {user_id}") 55 | return str(response) 56 | except Exception as e: 57 | logger.exception(f"Error in query_user_index for user {user_id}") 58 | raise -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import streamlit as st 4 | 5 | # Add the src directory to the Python path 6 | sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), 'src'))) 7 | 8 | # Set page config at the very top of the main script 9 | st.set_page_config( 10 | page_title="Supabase Authentication", 11 | page_icon="🔐", 12 | layout="centered", 13 | initial_sidebar_state="auto", 14 | menu_items=None 15 | ) 16 | 17 | from auth.handlers import handle_confirmation, logout 18 | from auth.forms import login_form, registration_form, password_reset_form 19 | from pages.home import show_home 20 | from pages.profile import show_profile 21 | from pages.settings import show_settings 22 | from pages.chat import chat # Changed from show_chat to chat 23 | from utils.supabase_client import supabase, ensure_profile_exists 24 | 25 | # Hide the Streamlit style and About menu 26 | hide_streamlit_style = """ 27 | 33 | """ 34 | st.markdown(hide_streamlit_style, unsafe_allow_html=True) 35 | 36 | def check_user_session(): 37 | session = supabase.auth.get_session() 38 | if session: 39 | st.session_state.user = session.user 40 | return True 41 | return False 42 | 43 | def main(): 44 | st.title("Supabase Authentication") 45 | 46 | # Handle logout 47 | if st.session_state.get('logged_out', False): 48 | st.session_state.clear() 49 | st.rerun() 50 | 51 | # Check for existing session on app load 52 | if 'user' not in st.session_state: 53 | if check_user_session(): 54 | st.success("Welcome back!") 55 | else: 56 | st.session_state.user = None 57 | 58 | user = st.session_state.get('user') 59 | 60 | # Ensure profile exists for the user 61 | if user: 62 | ensure_profile_exists(user) 63 | 64 | # Check if we're on the confirmation page 65 | query_params = st.query_params 66 | if "confirmation" in query_params: 67 | handle_confirmation() 68 | else: 69 | with st.sidebar: 70 | if user: 71 | # Logged-in user menu 72 | st.write(f"Welcome, {user.email}") 73 | choice = st.selectbox("Menu", ["Home", "Profile", "Settings", "Chat"], key="menu_selectbox") 74 | 75 | # Handle logout 76 | if st.button("Logout", key="logout_button"): 77 | logout() 78 | else: 79 | # Restricted menu for non-logged-in users 80 | choice = st.selectbox("Menu", ["Home", "Login", "Register", "Reset Password"], key="menu_selectbox_guest") 81 | 82 | # Load the selected page based on menu choice and authentication status 83 | if not user: 84 | if choice == "Home": 85 | show_home(user) 86 | elif choice == "Login": 87 | login_form() 88 | elif choice == "Register": 89 | registration_form() 90 | elif choice == "Reset Password": 91 | password_reset_form() 92 | else: 93 | if choice == "Home": 94 | show_home(user) 95 | elif choice == "Profile": 96 | show_profile(user) 97 | elif choice == "Settings": 98 | show_settings(user) 99 | elif choice == "Chat": 100 | chat() # Changed from show_chat(user) to chat() 101 | 102 | if __name__ == "__main__": 103 | main() -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control 110 | .pdm.toml 111 | .pdm-python 112 | .pdm-build/ 113 | 114 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 115 | __pypackages__/ 116 | 117 | # Celery stuff 118 | celerybeat-schedule 119 | celerybeat.pid 120 | 121 | # SageMath parsed files 122 | *.sage.py 123 | 124 | # Environments 125 | .env 126 | .venv 127 | env/ 128 | venv/ 129 | ENV/ 130 | env.bak/ 131 | venv.bak/ 132 | 133 | # Spyder project settings 134 | .spyderproject 135 | .spyproject 136 | 137 | # Rope project settings 138 | .ropeproject 139 | 140 | # mkdocs documentation 141 | /site 142 | 143 | # mypy 144 | .mypy_cache/ 145 | .dmypy.json 146 | dmypy.json 147 | 148 | # Pyre type checker 149 | .pyre/ 150 | 151 | # pytype static type analyzer 152 | .pytype/ 153 | 154 | # Cython debug symbols 155 | cython_debug/ 156 | 157 | # PyCharm 158 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 159 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 160 | # and can be added to the global gitignore or merged into this file. For a more nuclear 161 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 162 | #.idea/ 163 | -------------------------------------------------------------------------------- /fastapi/api copy.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI, Depends, HTTPException, Form 2 | from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm 3 | from fastapi.responses import RedirectResponse 4 | from pydantic import BaseModel 5 | import os 6 | import requests 7 | from typing import Optional 8 | from supabase import create_client, Client 9 | 10 | app = FastAPI() 11 | 12 | # Supabase initialization 13 | SUPABASE_URL = os.getenv("SUPABASE_URL") 14 | SUPABASE_KEY = os.getenv("SUPABASE_KEY") 15 | supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY) 16 | 17 | # OAuth2 Scheme 18 | oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/login") 19 | 20 | @app.get("/") 21 | async def redirect_to_docs(): 22 | return RedirectResponse(url="/docs") 23 | 24 | # Pydantic models 25 | class User(BaseModel): 26 | id: str 27 | email: str 28 | full_name: Optional[str] = None 29 | bio: Optional[str] = None 30 | age: Optional[int] = None 31 | theme: Optional[str] = "light" 32 | notifications: Optional[bool] = True 33 | language: Optional[str] = "en" 34 | 35 | class Settings(BaseModel): 36 | full_name: Optional[str] = None 37 | bio: Optional[str] = None 38 | age: Optional[int] = None 39 | theme: Optional[str] = "light" 40 | notifications: Optional[bool] = True 41 | language: Optional[str] = "en" 42 | 43 | class UserCreate(BaseModel): 44 | email: str 45 | password: str 46 | 47 | # Helper function to get user from token 48 | async def get_current_user(token: str = Depends(oauth2_scheme)): 49 | try: 50 | user = supabase.auth.get_user(token) 51 | return user.user 52 | except Exception as e: 53 | raise HTTPException(status_code=401, detail="Invalid authentication credentials") 54 | 55 | @app.post("/login") 56 | async def login(form_data: OAuth2PasswordRequestForm = Depends()): 57 | try: 58 | res = supabase.auth.sign_in_with_password({"email": form_data.username, "password": form_data.password}) 59 | return {"access_token": res.session.access_token, "token_type": "bearer"} 60 | except Exception as e: 61 | raise HTTPException(status_code=400, detail=f"Login failed: {str(e)}") 62 | 63 | @app.post("/register") 64 | async def register(user: UserCreate): 65 | try: 66 | res = supabase.auth.sign_up({"email": user.email, "password": user.password}) 67 | return {"message": "Registration successful! Please check your email to verify your account."} 68 | except Exception as e: 69 | raise HTTPException(status_code=400, detail=f"Registration failed: {str(e)}") 70 | 71 | @app.post("/reset-password") 72 | async def reset_password(email: str = Form(...)): 73 | try: 74 | res = supabase.auth.reset_password_email(email) 75 | return {"message": "Password reset link sent to your email!"} 76 | except Exception as e: 77 | raise HTTPException(status_code=400, detail=f"Failed to send reset link: {str(e)}") 78 | 79 | @app.get("/confirm") 80 | async def confirm(token: str): 81 | try: 82 | # Use the Supabase client to verify the token 83 | res = supabase.auth.verify_otp({"token": token, "type": "email"}) 84 | return {"message": "Email confirmed successfully! You can now log in."} 85 | except Exception as e: 86 | raise HTTPException(status_code=400, detail=f"Confirmation failed: {str(e)}") 87 | 88 | @app.post("/logout") 89 | async def logout(token: str = Depends(oauth2_scheme)): 90 | try: 91 | supabase.auth.sign_out(token) 92 | return {"message": "Logged out successfully!"} 93 | except Exception as e: 94 | raise HTTPException(status_code=400, detail=f"Logout failed: {str(e)}") 95 | 96 | @app.get("/profile", response_model=User) 97 | async def read_profile(current_user: dict = Depends(get_current_user)): 98 | try: 99 | response = supabase.table("profiles").select("*").eq("user_id", current_user.id).execute() 100 | if not response.data: 101 | raise HTTPException(status_code=404, detail="Profile not found") 102 | profile = response.data[0] 103 | return User(**profile) 104 | except Exception as e: 105 | raise HTTPException(status_code=400, detail=f"Failed to fetch profile: {str(e)}") 106 | 107 | @app.put("/settings", response_model=User) 108 | async def update_settings(settings: Settings, current_user: dict = Depends(get_current_user)): 109 | try: 110 | response = supabase.table("profiles").update(settings.dict(exclude_unset=True)).eq("user_id", current_user.id).execute() 111 | if not response.data: 112 | raise HTTPException(status_code=404, detail="Profile not found") 113 | updated_profile = response.data[0] 114 | return User(**updated_profile) 115 | except Exception as e: 116 | raise HTTPException(status_code=400, detail=f"Failed to update settings: {str(e)}") 117 | 118 | if __name__ == "__main__": 119 | import uvicorn 120 | uvicorn.run(app, host="0.0.0.0", port=8000) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Streamlit and FastAPI Authentication with Supabase 2 | 3 | This project showcases a comprehensive authentication system using Supabase as the backend, implemented with both Streamlit and FastAPI frontends. It demonstrates how to build secure, user-friendly authentication flows in two popular Python web frameworks. 4 | 5 | ### Streamlit App 6 | The Streamlit application provides a user-friendly, interactive interface for user registration, login, profile management, and settings customization. It leverages Streamlit's simplicity to create a responsive web app with minimal code. 7 | 8 | ### FastAPI Backend 9 | The FastAPI backend offers a robust API for handling authentication and user data management. It implements OAuth2 password flow for secure token-based authentication, as shown in the image. The API includes endpoints for user registration, login, profile retrieval, and settings updates. 10 | 11 | This dual-frontend approach allows developers to explore different implementation styles while sharing a common Supabase backend. Whether you prefer the rapid prototyping capabilities of Streamlit or the high-performance, type-safe API development with FastAPI, this project provides a solid foundation for building authenticated web applications. 12 | 13 | # Streamlit Supabase Authentication App 14 | 15 | This Streamlit application demonstrates user authentication and profile management using Supabase as the backend. 16 | 17 | ## Prerequisites 18 | 19 | - Python 3.7+ 20 | - Streamlit 21 | - Supabase account and project 22 | 23 | ## Setup 24 | 25 | 1. Clone the repository to your local machine. 26 | 27 | 2. Install the required dependencies: 28 | ``` 29 | pip install -r requirements.txt 30 | ``` 31 | 32 | 3. Set up your Supabase project: 33 | - Create a new project in Supabase 34 | - Enable Email Auth in Authentication > Providers 35 | - Create a `profiles` table in your Supabase database with the following SQL: 36 | 37 | ```sql 38 | CREATE TABLE profiles ( 39 | id uuid PRIMARY KEY DEFAULT gen_random_uuid(), 40 | user_id uuid REFERENCES auth.users(id) ON DELETE CASCADE, 41 | full_name text, 42 | email text, 43 | bio text, 44 | age integer, 45 | theme text DEFAULT 'light', 46 | notifications boolean DEFAULT true, 47 | language text DEFAULT 'en', 48 | created_at timestamp with time zone DEFAULT timezone('utc'::text, now()) NOT NULL, 49 | updated_at timestamp with time zone DEFAULT timezone('utc'::text, now()) NOT NULL 50 | ); 51 | 52 | CREATE OR REPLACE FUNCTION update_modified_column() 53 | RETURNS TRIGGER AS $$ 54 | BEGIN 55 | NEW.updated_at = now(); 56 | RETURN NEW; 57 | END; 58 | $$ language 'plpgsql'; 59 | 60 | CREATE TRIGGER update_profiles_modtime 61 | BEFORE UPDATE ON profiles 62 | FOR EACH ROW 63 | EXECUTE FUNCTION update_modified_column(); 64 | 65 | CREATE INDEX idx_profiles_user_id ON profiles(user_id); 66 | 67 | ALTER TABLE profiles ADD CONSTRAINT unique_user_profile UNIQUE (user_id); 68 | ``` 69 | 70 | 4. Create a `.env` file in the root directory with your Supabase credentials: 71 | ``` 72 | SUPABASE_URL=your_supabase_project_url 73 | SUPABASE_KEY=your_supabase_anon_key 74 | ``` 75 | 76 | ## Running the App 77 | 78 | To run the Streamlit app, use the following command in your terminal: 79 | 80 | ``` 81 | streamlit run main.py 82 | ``` 83 | 84 | The app should now be running on `http://localhost:8501`. 85 | 86 | ## Features 87 | 88 | - User Registration 89 | - User Login 90 | - Password Reset 91 | - Profile Management 92 | - User Settings (theme, notifications, language) 93 | 94 | ## File Structure 95 | 96 | - `main.py`: The main Streamlit app 97 | - `utils/supabase_client.py`: Supabase client initialization 98 | - `src/auth/`: Authentication-related functions 99 | - `src/pages/`: Different pages of the app (home, profile, settings) 100 | 101 | ## Customization 102 | 103 | You can customize the app by modifying the following: 104 | 105 | - `src/pages/`: Add or modify pages 106 | - `src/auth/`: Adjust authentication logic 107 | - `utils/supabase_client.py`: Add more Supabase-related functions 108 | 109 | ## Troubleshooting 110 | 111 | If you encounter any issues: 112 | 113 | 1. Ensure your Supabase credentials are correct in the `.env` file 114 | 2. Check that you've created the `profiles` table in your Supabase database 115 | 3. Verify that you've installed all required dependencies 116 | 117 | For more help, refer to the Streamlit and Supabase documentation. 118 | 119 | ## Security Note 120 | 121 | This app is for demonstration purposes. In a production environment, ensure you follow best practices for security, including proper error handling and input validation. 122 | 123 | # FastAPI Supabase Authentication App 124 | 125 | This project demonstrates user authentication and profile management using Supabase as the backend, with both Streamlit and FastAPI frontends. 126 | 127 | ## Prerequisites 128 | 129 | - Python 3.7+ 130 | - Streamlit 131 | - FastAPI 132 | - Uvicorn 133 | - Supabase account and project 134 | 135 | ## Setup 136 | 137 | 1. Clone the repository to your local machine. 138 | 139 | 2. Install the required dependencies: 140 | ``` 141 | pip install -r requirements.txt 142 | ``` 143 | 144 | 3. Set up your Supabase project as described in the previous instructions. 145 | 146 | 4. Create a `.env` file in the root directory with your Supabase credentials: 147 | ``` 148 | SUPABASE_URL=your_supabase_project_url 149 | SUPABASE_KEY=your_supabase_anon_key 150 | ``` 151 | 152 | ## Running the FastAPI App 153 | 154 | To run the FastAPI app, use the following command: 155 | 156 | ``` 157 | uvicorn fastapi.api:app --reload 158 | ``` 159 | 160 | The FastAPI app should now be running on `http://localhost:8000`. 161 | 162 | ## FastAPI Details 163 | 164 | The FastAPI application is defined in `./fastapi/api.py`. It provides the following endpoints: 165 | 166 | - `/login` (POST): Authenticate a user and return an access token 167 | - `/register` (POST): Register a new user 168 | - `/profile` (GET): Get the current user's profile 169 | - `/settings` (PUT): Update user settings 170 | - `/reset-password` (POST): Request a password reset email 171 | - `/confirm` (GET): Confirm email address after registration 172 | 173 | ### Authentication 174 | 175 | The FastAPI app uses OAuth2 password flow for authentication. To log in: 176 | 177 | 1. Send a POST request to `/login` with the following form data: 178 | - `grant_type`: "password" 179 | - `username`: Your email address 180 | - `password`: Your password 181 | - `scope`: (Optional) 182 | - `client_id`: (Optional) 183 | - `client_secret`: (Optional) 184 | 185 | 2. Use the returned access token in the `Authorization` header for authenticated requests. 186 | 187 | ## API Documentation 188 | 189 | FastAPI provides automatic API documentation. Once the FastAPI app is running, you can access: 190 | 191 | - Swagger UI: `http://localhost:8000/docs` 192 | - ReDoc: `http://localhost:8000/redoc` 193 | 194 | ## File Structure 195 | 196 | - `main.py`: The main Streamlit app 197 | - `fastapi/api.py`: The FastAPI application 198 | - `utils/supabase_client.py`: Supabase client initialization 199 | - `src/auth/`: Authentication-related functions 200 | - `src/pages/`: Different pages of the Streamlit app 201 | 202 | ## Customization 203 | 204 | You can customize both the Streamlit and FastAPI apps by modifying their respective files and adding new endpoints or pages as needed. 205 | 206 | ## Security Note 207 | 208 | This project is for demonstration purposes. In a production environment, ensure you follow best practices for security, including proper error handling, input validation, and secure token management. 209 | 210 | -------------------------------------------------------------------------------- /fastapi/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Create project structure 4 | mkdir -p app/api app/models app/db app/services 5 | 6 | # Create main.py 7 | cat << EOF > app/main.py 8 | from fastapi import FastAPI 9 | from fastapi.responses import RedirectResponse 10 | from app.api import auth, profile 11 | 12 | app = FastAPI() 13 | 14 | @app.get("/") 15 | async def redirect_to_docs(): 16 | return RedirectResponse(url="/docs") 17 | 18 | app.include_router(auth.router) 19 | app.include_router(profile.router) 20 | 21 | if __name__ == "__main__": 22 | import uvicorn 23 | uvicorn.run(app, host="0.0.0.0", port=8000) 24 | EOF 25 | 26 | # Create api/auth.py 27 | cat << EOF > app/api/auth.py 28 | from fastapi import APIRouter, Depends, HTTPException, Form 29 | from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm 30 | from app.db.supabase import supabase 31 | from app.models.user import UserCreate, User 32 | 33 | router = APIRouter() 34 | 35 | oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/login") 36 | 37 | async def get_current_user(token: str = Depends(oauth2_scheme)): 38 | try: 39 | user = supabase.auth.get_user(token) 40 | return user.user 41 | except Exception as e: 42 | raise HTTPException(status_code=401, detail="Invalid authentication credentials") 43 | 44 | @router.post("/login") 45 | async def login(form_data: OAuth2PasswordRequestForm = Depends()): 46 | try: 47 | res = supabase.auth.sign_in_with_password({"email": form_data.username, "password": form_data.password}) 48 | return {"access_token": res.session.access_token, "token_type": "bearer"} 49 | except Exception as e: 50 | raise HTTPException(status_code=400, detail=f"Login failed: {str(e)}") 51 | 52 | @router.post("/register") 53 | async def register(user: UserCreate): 54 | try: 55 | res = supabase.auth.sign_up({"email": user.email, "password": user.password}) 56 | return {"message": "Registration successful! Please check your email to verify your account."} 57 | except Exception as e: 58 | raise HTTPException(status_code=400, detail=f"Registration failed: {str(e)}") 59 | 60 | @router.post("/reset-password") 61 | async def reset_password(email: str = Form(...)): 62 | try: 63 | res = supabase.auth.reset_password_email(email) 64 | return {"message": "Password reset link sent to your email!"} 65 | except Exception as e: 66 | raise HTTPException(status_code=400, detail=f"Failed to send reset link: {str(e)}") 67 | 68 | @router.get("/confirm") 69 | async def confirm(token: str): 70 | try: 71 | res = supabase.auth.verify_otp({"token": token, "type": "email"}) 72 | return {"message": "Email confirmed successfully! You can now log in."} 73 | except Exception as e: 74 | raise HTTPException(status_code=400, detail=f"Confirmation failed: {str(e)}") 75 | 76 | @router.post("/logout") 77 | async def logout(token: str = Depends(oauth2_scheme)): 78 | try: 79 | supabase.auth.sign_out(token) 80 | return {"message": "Logged out successfully!"} 81 | except Exception as e: 82 | raise HTTPException(status_code=400, detail=f"Logout failed: {str(e)}") 83 | EOF 84 | 85 | # Create api/profile.py 86 | cat << EOF > app/api/profile.py 87 | from fastapi import APIRouter, Depends, HTTPException 88 | from app.models.user import User, Settings 89 | from app.api.auth import get_current_user 90 | from app.db.supabase import supabase 91 | from app.services.llama_index import get_user_index 92 | 93 | router = APIRouter() 94 | 95 | @router.get("/profile", response_model=User) 96 | async def read_profile(current_user: dict = Depends(get_current_user)): 97 | try: 98 | response = supabase.table("profiles").select("*").eq("user_id", current_user.id).execute() 99 | if not response.data: 100 | raise HTTPException(status_code=404, detail="Profile not found") 101 | profile = response.data[0] 102 | return User(**profile) 103 | except Exception as e: 104 | raise HTTPException(status_code=400, detail=f"Failed to fetch profile: {str(e)}") 105 | 106 | @router.put("/settings", response_model=User) 107 | async def update_settings(settings: Settings, current_user: dict = Depends(get_current_user)): 108 | try: 109 | response = supabase.table("profiles").update(settings.dict(exclude_unset=True)).eq("user_id", current_user.id).execute() 110 | if not response.data: 111 | raise HTTPException(status_code=404, detail="Profile not found") 112 | updated_profile = response.data[0] 113 | return User(**updated_profile) 114 | except Exception as e: 115 | raise HTTPException(status_code=400, detail=f"Failed to update settings: {str(e)}") 116 | 117 | @router.post("/index") 118 | async def create_user_index(current_user: dict = Depends(get_current_user)): 119 | try: 120 | index = get_user_index(current_user.id) 121 | return {"message": "User index created successfully"} 122 | except Exception as e: 123 | raise HTTPException(status_code=400, detail=f"Failed to create user index: {str(e)}") 124 | EOF 125 | 126 | # Create models/user.py 127 | cat << EOF > app/models/user.py 128 | from pydantic import BaseModel 129 | from typing import Optional 130 | 131 | class User(BaseModel): 132 | id: str 133 | email: str 134 | full_name: Optional[str] = None 135 | bio: Optional[str] = None 136 | age: Optional[int] = None 137 | theme: Optional[str] = "light" 138 | notifications: Optional[bool] = True 139 | language: Optional[str] = "en" 140 | 141 | class Settings(BaseModel): 142 | full_name: Optional[str] = None 143 | bio: Optional[str] = None 144 | age: Optional[int] = None 145 | theme: Optional[str] = "light" 146 | notifications: Optional[bool] = True 147 | language: Optional[str] = "en" 148 | 149 | class UserCreate(BaseModel): 150 | email: str 151 | password: str 152 | EOF 153 | 154 | # Create db/supabase.py 155 | cat << EOF > app/db/supabase.py 156 | import os 157 | from supabase import create_client, Client 158 | 159 | SUPABASE_URL = os.getenv("SUPABASE_URL") 160 | SUPABASE_KEY = os.getenv("SUPABASE_KEY") 161 | supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY) 162 | EOF 163 | 164 | # Create services/llama_index.py 165 | cat << EOF > app/services/llama_index.py 166 | from llama_index.vector_stores.supabase import SupabaseVectorStore 167 | from llama_index import VectorStoreIndex, StorageContext 168 | from app.db.supabase import supabase 169 | 170 | def get_user_index(user_id: str): 171 | vector_store = SupabaseVectorStore( 172 | postgres_connection_string=f"{supabase.postgrest.url}?apikey={supabase.supabase_key}", 173 | collection_name=f"user_index_{user_id}" 174 | ) 175 | storage_context = StorageContext.from_defaults(vector_store=vector_store) 176 | return VectorStoreIndex([], storage_context=storage_context) 177 | EOF 178 | 179 | # Create SQL file 180 | cat << EOF > supabase_setup.sql 181 | -- Enable the pgvector extension 182 | CREATE EXTENSION IF NOT EXISTS vector; 183 | 184 | -- Create the profiles table 185 | CREATE TABLE IF NOT EXISTS profiles ( 186 | id UUID PRIMARY KEY REFERENCES auth.users(id), 187 | full_name TEXT, 188 | bio TEXT, 189 | age INT, 190 | theme TEXT DEFAULT 'light', 191 | notifications BOOLEAN DEFAULT true, 192 | language TEXT DEFAULT 'en' 193 | ); 194 | 195 | -- Create a function to handle new user signups 196 | CREATE OR REPLACE FUNCTION public.handle_new_user() 197 | RETURNS TRIGGER AS \$\$ 198 | BEGIN 199 | INSERT INTO public.profiles (id) 200 | VALUES (NEW.id); 201 | RETURN NEW; 202 | END; 203 | \$\$ LANGUAGE plpgsql SECURITY DEFINER; 204 | 205 | -- Create a trigger to call the function on new user signups 206 | CREATE TRIGGER on_auth_user_created 207 | AFTER INSERT ON auth.users 208 | FOR EACH ROW EXECUTE FUNCTION public.handle_new_user(); 209 | 210 | -- Create a table for storing user-specific vector indexes 211 | CREATE TABLE IF NOT EXISTS user_indexes ( 212 | id UUID PRIMARY KEY REFERENCES auth.users(id), 213 | index_name TEXT NOT NULL, 214 | embedding VECTOR(1536) 215 | ); 216 | 217 | -- Create an index on the embedding column for faster similarity searches 218 | CREATE INDEX ON user_indexes USING ivfflat (embedding vector_cosine_ops); 219 | EOF 220 | 221 | echo "Project structure and files created successfully!" 222 | echo "Don't forget to set the SUPABASE_URL and SUPABASE_KEY environment variables before running the application." 223 | echo "Execute the SQL in supabase_setup.sql in your Supabase project to set up the necessary tables and extensions." -------------------------------------------------------------------------------- /src/pages/chat.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import openai 3 | import os 4 | from llama_index.llms.openai import OpenAI 5 | from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, Settings, StorageContext, load_index_from_storage 6 | 7 | # Initialize session state 8 | if "user_name" not in st.session_state: 9 | st.session_state.user_name = "" 10 | 11 | if "messages" not in st.session_state: 12 | st.session_state.messages = [] 13 | 14 | if "conversation_context" not in st.session_state: 15 | st.session_state.conversation_context = {} 16 | 17 | if "model" not in st.session_state: 18 | st.session_state.model = "gpt-4o" 19 | 20 | if "temperature" not in st.session_state: 21 | st.session_state.temperature = 0.7 22 | 23 | if "top_p" not in st.session_state: 24 | st.session_state.top_p = 1.0 25 | 26 | # Set up OpenAI API key 27 | openai.api_key = os.environ.get("OPENAI_API_KEY") 28 | 29 | def print_data_contents(): 30 | data_dir = "./data" 31 | st.sidebar.write("Contents of ./data directory:") 32 | for filename in os.listdir(data_dir): 33 | file_path = os.path.join(data_dir, filename) 34 | if os.path.isfile(file_path): 35 | with open(file_path, 'r') as file: 36 | content = file.read() 37 | st.sidebar.text_area(f"Contents of {filename}:", content, height=200) 38 | 39 | @st.cache_resource(show_spinner=False) 40 | def load_data(): 41 | if os.path.exists("./index"): 42 | storage_context = StorageContext.from_defaults(persist_dir="./index") 43 | index = load_index_from_storage(storage_context) 44 | else: 45 | reader = SimpleDirectoryReader(input_dir="./data", recursive=True) 46 | docs = reader.load_data() 47 | st.sidebar.write(f"Number of documents loaded: {len(docs)}") 48 | if docs: 49 | for i, doc in enumerate(docs): 50 | st.sidebar.write(f"Document {i+1} content (truncated):\n{doc.text[:200]}...") 51 | Settings.llm = OpenAI(model=st.session_state.model, temperature=st.session_state.temperature) 52 | index = VectorStoreIndex.from_documents(docs) 53 | index.storage_context.persist("./index") 54 | return index 55 | 56 | index = load_data() 57 | 58 | def update_chat_engine(): 59 | st.session_state.chat_engine = index.as_chat_engine( 60 | chat_mode="condense_question", 61 | verbose=True, 62 | streaming=True, 63 | llm=OpenAI(model=st.session_state.model, temperature=st.session_state.temperature, top_p=st.session_state.top_p) 64 | ) 65 | 66 | if "chat_engine" not in st.session_state: 67 | update_chat_engine() 68 | 69 | def generate_response(prompt): 70 | name = st.session_state.user_name 71 | context = st.session_state.conversation_context 72 | system_prompt = f"""You are a friendly and helpful AI assistant named AI Assistant. 73 | You have a conversation history and can remember details about the user. 74 | The user's name is {name if name else 'unknown'}. 75 | Current context: {context} 76 | You should use this information to personalize your responses. 77 | If you don't know something about the user, admit that you don't know rather than making assumptions. 78 | Always be polite, engaging, and try to keep the conversation flowing naturally. 79 | When the user shares information about themselves, acknowledge it and remember it for future use. 80 | """ 81 | 82 | if "my name is" in prompt.lower(): 83 | name = prompt.lower().split("my name is")[-1].strip() 84 | st.session_state.user_name = name 85 | context['name'] = name 86 | return f"Nice to meet you, {name}! I'll remember that. How can I assist you today?" 87 | 88 | if "live in" in prompt.lower() or "from" in prompt.lower(): 89 | location = prompt.lower().split("live in")[-1].strip() if "live in" in prompt.lower() else prompt.lower().split("from")[-1].strip() 90 | context["location"] = location 91 | return f"Thank you for letting me know that you're from {location}, {name}. I'll remember that for future conversations. Is there anything specific about {location} you'd like to discuss?" 92 | 93 | if "what's my name" in prompt.lower() or "what is my name" in prompt.lower(): 94 | if name: 95 | return f"Your name is {name}. I remember it because you told me earlier!" 96 | else: 97 | return "I'm sorry, but I don't know your name yet. Would you like to tell me?" 98 | 99 | if "where do i live" in prompt.lower() or "where am i from" in prompt.lower(): 100 | if "location" in context: 101 | return f"Based on what you've told me, you live in {context['location']}. Is there anything specific about {context['location']} you'd like to know?" 102 | else: 103 | return "I'm sorry, but I don't have any information about where you live. Would you like to tell me?" 104 | 105 | if "tell me about yourself" in prompt.lower(): 106 | return "I'm an AI assistant created to help with a wide range of topics. I don't have personal experiences, but I'm here to assist you with information, answer questions, or discuss any topic you're interested in. Is there a particular subject you'd like to explore?" 107 | 108 | if "i like" in prompt.lower() or "my favorite" in prompt.lower(): 109 | preference = prompt.lower().split("i like")[-1].strip() if "i like" in prompt.lower() else prompt.lower().split("my favorite")[-1].strip() 110 | context.setdefault("preferences", []).append(preference) 111 | return f"That's interesting, {name}! I'll remember that you like {preference}. Would you like to discuss more about it or explore related topics?" 112 | 113 | full_prompt = f"{system_prompt}\n\nUser: {prompt}\nAI Assistant:" 114 | response_stream = st.session_state.chat_engine.stream_chat(full_prompt) 115 | return response_stream 116 | 117 | def test_data_retrieval(): 118 | test_query = "What are the main types of machine learning?" 119 | st.sidebar.write("Test Query:", test_query) 120 | response = generate_response(test_query) 121 | if isinstance(response, str): 122 | st.sidebar.write("Response:", response) 123 | else: # It's a stream 124 | full_response = "" 125 | for chunk in response.response_gen: 126 | full_response += chunk 127 | st.sidebar.write("Response:", full_response) 128 | 129 | def chat(): 130 | st.title("Memory-Enabled RAG Chatbot") 131 | 132 | if "conversation_context" not in st.session_state: 133 | st.session_state.conversation_context = {} 134 | 135 | # Sidebar for model settings and data verification 136 | st.sidebar.title("Model Settings and Data Verification") 137 | st.session_state.model = st.sidebar.selectbox( 138 | "Select Model", 139 | ["gpt-4o", "gpt-4o-mini", "gpt-4-turbo", "gpt-4", "gpt-3.5-turbo"], 140 | index=0 141 | ) 142 | st.session_state.temperature = st.sidebar.slider("Temperature", 0.0, 1.0, 0.7, 0.1) 143 | st.session_state.top_p = st.sidebar.slider("Top P", 0.0, 1.0, 1.0, 0.1) 144 | 145 | # Update chat engine when settings change 146 | if st.sidebar.button("Apply Settings"): 147 | update_chat_engine() 148 | 149 | # Print data contents and test data retrieval 150 | print_data_contents() 151 | if st.sidebar.button("Test Data Retrieval"): 152 | test_data_retrieval() 153 | 154 | if not st.session_state.user_name: 155 | st.session_state.user_name = st.text_input("What's your name?") 156 | if st.session_state.user_name: 157 | st.session_state.conversation_context['name'] = st.session_state.user_name 158 | st.success(f"Nice to meet you, {st.session_state.user_name}!") 159 | 160 | for message in st.session_state.messages: 161 | with st.chat_message(message["role"]): 162 | st.markdown(message["content"]) 163 | 164 | if prompt := st.chat_input("What would you like to talk about?"): 165 | st.session_state.messages.append({"role": "user", "content": prompt}) 166 | with st.chat_message("user"): 167 | st.markdown(prompt) 168 | 169 | with st.chat_message("assistant"): 170 | response = generate_response(prompt) 171 | if isinstance(response, str): 172 | st.markdown(response) 173 | final_response = response 174 | else: # It's a stream 175 | response_placeholder = st.empty() 176 | full_response = "" 177 | for chunk in response.response_gen: 178 | full_response += chunk 179 | response_placeholder.markdown(full_response + "▌") 180 | response_placeholder.markdown(full_response) 181 | final_response = full_response 182 | 183 | st.session_state.messages.append({"role": "assistant", "content": final_response}) 184 | 185 | if __name__ == "__main__": 186 | chat() -------------------------------------------------------------------------------- /index/default__vector_store.json: -------------------------------------------------------------------------------- 1 | {"embedding_dict": {"36815fa0-0599-4a15-82ad-eb4a9ba6a7cf": [0.0038680960424244404, 0.020332667976617813, -0.0147965457290411, -0.01379716768860817, -0.033101700246334076, 0.04011891409754753, -0.038479648530483246, -0.026573387905955315, -0.014523334801197052, -0.014070378616452217, 0.029233602806925774, 0.0038680960424244404, -0.01660836860537529, 0.004022676032036543, 0.0008344626403413713, -0.009864363819360733, 0.012639614753425121, -0.00034331149072386324, 0.01870778016746044, -0.025250470265746117, -0.005719461478292942, 0.013768408447504044, 0.017499899491667747, -0.0026853790041059256, -0.010195093229413033, -0.007836848497390747, 0.01284811832010746, -0.030657177790999413, -0.006377325393259525, 0.010432356037199497, 0.03468344733119011, -0.026041345670819283, 0.01836267113685608, -0.02254711650311947, -0.015256690792739391, -0.003871690947562456, 0.007836848497390747, -0.011942206881940365, 0.019872523844242096, 0.007707432843744755, -0.0023941933177411556, -0.007383893243968487, -0.008980021812021732, -0.003684756811708212, -0.025149814784526825, 0.011553959921002388, 0.0020436919294297695, -0.018578365445137024, -0.025207331404089928, 0.001242931466549635, 0.011093814857304096, 0.002645835280418396, -0.05116239935159683, -0.002049084287136793, -0.009936261922121048, 0.014839684590697289, 0.017643693834543228, 0.022115731611847878, -0.0062550995498895645, 0.0023959907703101635, 0.03451089560985565, -0.008174767717719078, -0.007527688983827829, -0.015141654759645462, 0.00011739545880118385, -0.012445490807294846, -0.0022809545043855906, 0.03229644522070885, 0.00911662820726633, -0.0025272041093558073, 0.0015260286163538694, 0.020907849073410034, -0.003914829343557358, -0.014286071993410587, 0.0005068787722848356, -0.009253233671188354, -0.006208366248756647, 0.005478604231029749, 0.006352161522954702, -0.011101004667580128, 0.0017677845899015665, -0.007671484258025885, 0.014652750454843044, 0.036524031311273575, 0.025164194405078888, 0.0032749399542808533, -0.006262289360165596, 0.026242658495903015, -0.02435893937945366, -0.02184252068400383, 0.014214174821972847, 0.005140685476362705, 0.020001938566565514, -0.012042864225804806, 0.006089734844863415, 0.016248879954218864, -0.0006048393552191556, 0.026904117316007614, 0.009806845337152481, -0.027436161413788795, -0.01660836860537529, -0.006898583844304085, -0.02307916060090065, -0.02036142721772194, -0.034999798983335495, -0.013315453194081783, 0.018477708101272583, -0.0020077431108802557, 0.01097158808261156, -0.006046596448868513, -0.0364377535879612, 0.008332942612469196, -0.007800899911671877, -0.025998206809163094, -0.03315921872854233, -0.011259179562330246, 0.026429593563079834, -0.014106327667832375, -0.018909094855189323, 0.00013559455692302436, 0.010331698693335056, 0.029708128422498703, 0.009929072111845016, -0.02194317616522312, -0.00016266854072455317, -0.011216040700674057, -0.00448641600087285, -0.026156382635235786, 0.020620258525013924, -0.00720774382352829, 0.031203599646687508, 0.03154870867729187, 0.0213679950684309, 0.003519392106682062, -0.023855656385421753, 0.027277985587716103, -0.010223852470517159, 0.026717184111475945, -0.005377947818487883, 0.0027518842834979296, 0.010820602998137474, 0.029132945463061333, -0.004191635642200708, 0.004328241106122732, -0.016953475773334503, -0.004939371719956398, -0.015975667163729668, -0.0076067764312028885, 0.008778708986938, -0.005554096773266792, 0.034999798983335495, -0.018060700967907906, -0.01156114973127842, -0.008785898797214031, -0.002165918005630374, 0.007700243033468723, 0.027436161413788795, 0.008749949745833874, 0.00704237911850214, -0.04187321662902832, 0.0011368823470547795, 0.05116239935159683, 0.005863257218152285, -0.03563249856233597, 0.02017449401319027, 0.03422330319881439, 0.026400834321975708, 0.0132507449015975, -0.007143035996705294, -0.0008268235251307487, -0.007153820712119341, 0.026098864153027534, -0.020188873633742332, -0.005151469726115465, -0.011855930089950562, 0.010856552049517632, -0.0091094383969903, 0.010324508883059025, -0.03284286707639694, 0.0049753207713365555, -0.03873847797513008, -0.002052679192274809, 0.029708128422498703, 0.028658421710133553, 0.010094436816871166, -0.03031206876039505, 0.006154442671686411, -0.0182763934135437, 0.005701487418264151, 0.0038213625084608793, 0.02254711650311947, 0.036754101514816284, 0.015860632061958313, -0.003095195861533284, -0.6248772740364075, -0.02489098161458969, 0.002595506841316819, -0.02036142721772194, 0.011101004667580128, 0.008376081474125385, -0.00944735761731863, 0.003361217211931944, -0.00923166424036026, 0.0018315938068553805, -0.006575044244527817, 0.01951303519308567, 0.015127275139093399, 2.926460911112372e-05, 0.010791843757033348, -0.009979399852454662, -0.0039328038692474365, -0.008095680736005306, -0.02668842487037182, 0.01558742020279169, -0.00901597086340189, 0.00329471193253994, -0.023956311866641045, 0.021022886037826538, 0.004195230547338724, 0.006154442671686411, 0.015601799823343754, 0.012366403825581074, 0.01588939130306244, 0.0022881440818309784, -0.06200457364320755, 0.00603221682831645, 0.013552716001868248, -0.013128519058227539, 0.044087667018175125, -0.01760055683553219, -0.03945745527744293, 0.005838092882186174, 0.025538060814142227, 0.05058721825480461, -0.012172279879450798, -0.014163846150040627, -0.006114899180829525, 0.008649292401969433, -0.009698999114334583, -0.013502387329936028, 0.03229644522070885, -0.008541446179151535, 0.0060789501294493675, 0.010360457934439182, 0.0060645705088973045, -0.01807508058845997, 0.004957346245646477, -0.0033162811305373907, -0.005845282692462206, -0.014645560644567013, 0.03200885280966759, -0.017197929322719574, 0.00935389008373022, -0.006884204223752022, 0.021166682243347168, 0.021497411653399467, -0.01731296442449093, -0.022949744015932083, -0.009454547427594662, -0.016335155814886093, -0.001534117036499083, 0.0009796060621738434, -0.019584931433200836, -0.0330154225230217, 0.016349535435438156, -0.002144348807632923, 0.001613204600289464, -0.015098515897989273, 0.013976912014186382, 0.0054462505504488945, 0.011259179562330246, 0.005906395614147186, -0.00719695957377553, -0.010540202260017395, 0.009476115927100182, 0.0216987244784832, -0.03241148218512535, 0.009432977996766567, 0.011927828192710876, 0.022920984774827957, -0.038048259913921356, -0.007915936410427094, 0.0012186659732833505, -0.003603871911764145, 0.021626826375722885, -0.000564396905247122, 0.012618045322597027, -0.022043833509087563, 0.027277985587716103, 0.03459716960787773, -0.0039759427309036255, 6.39215431874618e-05, 0.008253855630755424, -0.01496910024434328, -0.010719946585595608, 0.022920984774827957, 0.012840928509831429, 0.008994401432573795, 0.02979440428316593, 0.014616801403462887, -0.0344533771276474, -0.008455169387161732, -0.008620534092187881, -0.015544282272458076, -0.008749949745833874, 0.012984723784029484, -0.038278333842754364, 4.4879889173898846e-05, -0.0344533771276474, -0.03876723721623421, 0.03741556033492088, 0.010676807723939419, 0.008088490925729275, 0.008843416348099709, 0.028298933058977127, 0.014954720623791218, 0.01034607831388712, -0.008785898797214031, 0.0081172501668334, 0.006391705013811588, 0.03177878260612488, -0.028629662469029427, 0.009540824219584465, 0.017442381009459496, -0.0060933297500014305, -0.001194400480017066, 0.04782634600996971, -0.02973688766360283, 0.012380783446133137, -0.024430837482213974, 0.023898793384432793, 0.015429245308041573, -0.004004701506346464, -0.005471414886415005, -0.03632271662354469, -0.007276047021150589, -0.00890093483030796, -0.0069453176110982895, -0.0020454893819987774, -0.029348639771342278, -0.004878258798271418, 0.017097271978855133, 0.0040586250834167, -0.0015161426272243261, -0.014609611593186855, 0.002942413091659546, -0.012280126102268696, 0.020807193592190742, 0.02769499272108078, -0.01712603121995926, 0.019498655572533607, -0.03189381957054138, -0.010698377154767513, -0.01172651443630457, -0.01941237784922123, 0.030197031795978546, -0.02317981794476509, 0.011510821059346199, -0.004525959957391024, 0.00037656418862752616, 0.026156382635235786, 0.011172901839017868, -0.00119529920630157, -0.01201410498470068, 0.006718839518725872, 0.002663809573277831, -0.027335504069924355, 0.002067058812826872, 0.006147252861410379, -0.004648186266422272, -0.028816595673561096, 0.012941584922373295, 0.01279778964817524, -6.0045811551390216e-05, 0.0037890085950493813, 0.0071070874109864235, -0.012891257181763649, 0.007498929742723703, 0.03321673721075058, 0.021425513550639153, 0.0028920848853886127, 0.007369513623416424, 0.008829036727547646, 0.02274843119084835, -0.01831953227519989, 0.018103839829564095, -0.011949396692216396, -0.0022198413498699665, -0.004583477973937988, 0.03074345551431179, 0.00972775835543871, 0.0008749050903134048, -0.010770275257527828, -0.006237125024199486, -0.015040998347103596, 0.00523415207862854, 0.012172279879450798, -0.01669464446604252, 0.014336400665342808, -0.02264777384698391, 0.0008704114588908851, -0.026587767526507378, -0.005737436003983021, -0.017729971557855606, 0.020764054730534554, -0.01665150560438633, -0.01508413627743721, 0.008548635989427567, 0.015688076615333557, -0.008699621073901653, -0.01089969091117382, -0.0032893195748329163, -0.026156382635235786, 0.011036296375095844, -0.0015511928359046578, -0.01879405789077282, -0.0059171803295612335, -0.006682890933007002, 0.0018998966552317142, 0.018161358311772346, 0.019167926162481308, 0.02944929711520672, 0.014235743321478367, -0.011007537133991718, 0.030772214755415916, 0.024013830348849297, 0.01574559509754181, -0.022015074267983437, 0.007570827379822731, -0.0067116497084498405, -0.0024427243042737246, -0.04716488718986511, 0.03436709940433502, -0.03416578471660614, 0.005525337997823954, -0.0070495689287781715, 0.012495819479227066, -0.04345496743917465, 0.005043623503297567, -0.007229313254356384, 0.027191707864403725, 0.028155136853456497, -0.022805949673056602, 0.011374215595424175, -0.03353308513760567, 0.015644937753677368, 0.008433599956333637, 0.0055792611092329025, 0.023481788113713264, -0.0023312827106565237, -0.015774354338645935, 0.009497685357928276, 0.011913448572158813, 0.014221364632248878, 0.016478952020406723, 0.0039759427309036255, -0.006510336417704821, 0.010051297955214977, 0.017960043624043465, -0.031232358887791634, -0.049609411507844925, 0.0013804357731714845, -0.013940962962806225, -0.02112354338169098, 0.0049178022891283035, -0.0013669549953192472, -0.002271967241540551, -0.016335155814886093, -0.007693053688853979, -0.010698377154767513, 0.017960043624043465, 0.006136468145996332, -0.00017659871082287282, -0.008153198286890984, -0.04316737502813339, -0.01408475823700428, 0.029276741668581963, 0.0019735917448997498, -0.006812306586652994, -0.0026044941041618586, -0.014739027246832848, 0.02678908221423626, 0.016248879954218864, 0.0072257183492183685, 0.009224474430084229, 0.0033917739056050777, 0.012955964542925358, -0.031146081164479256, -0.015098515897989273, 0.007693053688853979, 0.015975667163729668, -0.012790599837899208, 0.015127275139093399, 0.0025901144836097956, -0.009303561411798, 0.005090356804430485, -0.01551552303135395, -0.013976912014186382, 0.0531180165708065, 0.0006749395979568362, 0.00885060615837574, -0.001050605089403689, 0.002967577427625656, -0.04126927629113197, 0.015688076615333557, 0.00239059841260314, -0.014142276719212532, 0.013459248468279839, -0.01063366886228323, -0.006772763095796108, -0.03215264901518822, -0.0031239548698067665, 0.03192257881164551, 0.019786246120929718, 0.0028974772430956364, -0.032900385558605194, -0.025724995881319046, 0.031232358887791634, -0.016061944887042046, 0.029823163524270058, 0.016119463369250298, -0.0029172489885240793, -0.009008781053125858, 0.016536470502614975, -0.030513381585478783, -0.007412652485072613, -0.007613965775817633, -0.02765185385942459, -0.012531768530607224, -0.02198631502687931, 0.007894366979598999, 0.009691809304058552, 0.030772214755415916, -0.0012519186129793525, -0.003163498593494296, -0.009742137975990772, -0.008800278417766094, -0.0075636375695466995, -0.005086761899292469, -0.047941382974386215, 0.00719695957377553, 0.00332526839338243, 0.04704985395073891, -0.0058668521232903, 0.01455928385257721, 0.013782788068056107, 0.00894407369196415, -0.025379886850714684, 0.0036110617220401764, 0.037214245647192, 0.030513381585478783, 0.01072713639587164, 0.010008159093558788, 0.04043526574969292, 0.007743381895124912, 0.023683100938796997, 0.0141350869089365, 0.019685588777065277, 0.015630559995770454, 0.00013615626085083932, 0.011374215595424175, -0.01472464855760336, 0.015716835856437683, 0.004759627394378185, -0.026242658495903015, 0.0453818254172802, 0.008893745020031929, -0.01607632450759411, 0.0021515386179089546, 0.0247903261333704, -0.0216987244784832, -0.005111926235258579, 0.02730674482882023, 0.004335430916398764, 0.031519949436187744, -0.00015424302546307445, -0.014214174821972847, -0.023438649252057076, -0.018017562106251717, -0.01803194172680378, 0.00823228619992733, 0.013660562224686146, 0.012905636802315712, 0.0018783273408189416, -0.022000694647431374, -0.004479226656258106, -0.026429593563079834, 0.011266369372606277, -0.012546148151159286, -0.004871068987995386, -0.02036142721772194, -0.0032300038728863, 0.028730319812893867, 0.012035674415528774, 0.012610855512320995, -0.030570900067687035, -0.02330923266708851, 0.014544904232025146, -0.016349535435438156, -0.019484275951981544, -0.01134545635432005, 9.706189302960411e-05, -0.009224474430084229, -0.007204148918390274, 0.021439893171191216, -0.007865607738494873, -0.0038537164218723774, 0.028586523607373238, 0.00021962499886285514, 0.02008821628987789, -0.007085517980158329, -0.00911662820726633, 0.03344680741429329, 0.008814657106995583, -0.0027554791886359453, 0.01373246032744646, -0.002534393686801195, 0.02460339106619358, -0.014688699506223202, -0.01994442008435726, -0.01256052777171135, 0.010187903419137001, 0.015400486066937447, 0.00894407369196415, 0.011223230510950089, 0.017902527004480362, 0.001517940079793334, -0.015788733959197998, 0.014070378616452217, 0.020390186458826065, -0.020591499283909798, -0.007441411726176739, 0.015472384169697762, 0.007089112885296345, 0.03031206876039505, 0.015573040582239628, -0.006186796817928553, -0.012387973256409168, 0.011647426523268223, -0.0017363294027745724, 0.04158562794327736, 0.026443973183631897, 0.017428001388907433, 0.005568476393818855, -0.016292016953229904, -0.012553337961435318, -0.022777190431952477, -0.006211961153894663, -0.010245421901345253, 0.007286831270903349, -0.02069215662777424, -0.021382374688982964, -0.012186659500002861, -0.021497411653399467, 0.007556447759270668, -0.0002480470575392246, -0.018923474475741386, -0.011151332408189774, -0.002031109994277358, -0.003372001927345991, 0.004734463058412075, -0.01850646734237671, -0.01163304690271616, -0.026372075080871582, -0.017816249281167984, -0.015817493200302124, 0.014868443831801414, 0.025394266471266747, 0.026616526767611504, 0.006154442671686411, -0.03479848429560661, -0.003663187613710761, -0.014206985011696815, -0.019671209156513214, -0.02145427279174328, 0.004392949398607016, 0.017586177214980125, 0.0005594539688900113, 0.05047218129038811, 0.003488835645839572, 0.015371727757155895, 0.004497200716286898, 0.021828141063451767, -0.027623094618320465, 0.028946012258529663, -0.012697133235633373, -0.0038033882156014442, 0.025782514363527298, 0.014710268937051296, 0.014386729337275028, 0.019354859367012978, -0.016723403707146645, 0.00436419015750289, 0.014710268937051296, 0.006801521871238947, -0.014825304970145226, -0.026386454701423645, -0.048804156482219696, -0.021526170894503593, -0.00706754345446825, -0.017557417973876, -0.0009247841080650687, -0.020519603043794632, -0.02868718095123768, 0.0330154225230217, -0.013480817899107933, 0.013056621886789799, -0.017686832696199417, 0.028946012258529663, -0.0028507437091320753, 0.01726982742547989, -0.024387698620557785, 0.003713516052812338, -0.027565576136112213, -0.0029478054493665695, -0.010906880721449852, -0.013761218637228012, 0.012704323045909405, 0.01732734404504299, 0.05363568291068077, 0.014854064211249352, -0.017816249281167984, 0.015069756656885147, 0.033619362860918045, -0.0059315599501132965, 0.0192685816437006, -0.003663187613710761, -0.020404566079378128, -0.0006443830789066851, -0.04101044684648514, -0.0165652297437191, -0.02408572845160961, -0.016018806025385857, -0.003889665240421891, -0.00565115874633193, 0.02660214714705944, -0.028370831161737442, -0.0008825442055240273, -0.0016671278281137347, -0.016536470502614975, 0.04785510525107384, 0.030570900067687035, 0.03649527207016945, 0.03206637129187584, 0.004428897984325886, -0.02944929711520672, 0.003979537636041641, -0.02612762339413166, -0.01472464855760336, 0.01889471523463726, 0.028859734535217285, -0.036524031311273575, -0.026142003014683723, 0.008936883881688118, 0.007139441091567278, 0.0210372656583786, -0.015040998347103596, 0.016349535435438156, 0.018190117552876472, 0.029823163524270058, -0.009418598376214504, 0.010051297955214977, -0.03830709308385849, 0.015055377967655659, 0.0014640168519690633, -0.006391705013811588, 0.003181473119184375, -0.006406084634363651, -0.017787490040063858, 0.0002911856572609395, -0.00834732223302126, 0.035862572491168976, 0.004213205073028803, 0.015314209274947643, -0.006488766986876726, -0.000693363428581506, -0.0008106465102173388, 0.007757761515676975, -0.011482061818242073, 0.020347047597169876, 0.00704237911850214, 0.0033684070222079754, 0.015141654759645462, 0.01698223501443863, -0.022489599883556366, 0.010856552049517632, 0.008167577907443047, 0.03620767965912819, -0.009425788186490536, -0.018592745065689087, -0.024373319000005722, 0.013653372414410114, 0.006082545034587383, 0.008131629787385464, 0.003857311327010393, -0.02907542884349823, -0.016392674297094345, -0.012531768530607224, 0.011295127682387829, -0.006970481481403112, 0.0012438301928341389, -0.008246665820479393, -0.009842794388532639, -0.03370564058423042, -0.0032551682088524103, -0.011927828192710876, 0.02108040452003479, -0.018664641305804253, -0.028111999854445457, -0.004551124293357134, 0.0009787073358893394, 0.024114487692713737, -0.014954720623791218, -0.005870447028428316, 0.0063881101086735725, 0.0399751178920269, -0.02307916060090065, 0.00662537245079875, -0.003132942132651806, 0.014717458747327328, -0.03416578471660614, 0.004820740316063166, -0.012359214015305042, 0.005230557173490524, -0.009864363819360733, -0.012991913594305515, -0.042333364486694336, -0.015616179443895817, 0.0052413418889045715, 0.01023104228079319, -0.003594884881749749, 0.00031455242424272, 0.018405809998512268, -0.0011045283172279596, -0.0036362260580062866, -0.0068877991288900375, -0.008318562991917133, 0.0020886280108243227, -0.03569001704454422, -6.201176438480616e-05, -0.004349810536950827, 0.0075636375695466995, -0.001919668517075479, -0.0057266512885689735, -0.008872175589203835, -0.002471483312547207, -0.03016827255487442, -0.018247636035084724, 0.008800278417766094, -0.01941237784922123, -0.009713378734886646, -0.0020742486231029034, -0.01335140224546194, -0.00478119682520628, -0.014940341003239155, 0.00617241719737649, 0.002609886461868882, -0.00022670242469757795, -0.008052541874349117, -0.007024405058473349, 0.006395299918949604, 0.00801659282296896, -0.010834982618689537, -0.012057243846356869, -0.04676226153969765, -0.0041233329102396965, -0.018635882064700127, -0.015630559995770454, -0.019167926162481308, 0.02664528600871563, 0.020332667976617813, -0.01683844067156315, -0.018578365445137024, -0.02046208456158638, -0.014645560644567013, -0.04135555401444435, -0.007642725016921759, 0.015026618726551533, 0.017873767763376236, -0.005672728177160025, 0.011172901839017868, 0.00662537245079875, -0.02194317616522312, 0.020303908735513687, 0.0010856551816686988, -0.004076599143445492, -0.023870034143328667, -0.004389354493469, 0.021339235827326775, -0.012747461907565594, -0.008304184302687645, -0.02340989001095295, -0.02746492065489292, -0.00258651957847178, 0.014825304970145226, 0.010820602998137474, -0.012086003087460995, -0.009325130842626095, -0.016105083748698235, -0.002841756446287036, 0.006276668980717659, 0.007265262305736542, 0.003661390161141753, -0.028270173817873, 0.02256149612367153, 0.029434917494654655, 0.01908164843916893, 0.0016212930204346776, -0.019527414813637733, 0.012826548889279366, 0.008469549007713795, 0.00484230974689126, -0.01698223501443863, -0.00010677931277314201, -0.014940341003239155, -0.010669617913663387, 0.029276741668581963, -0.0049465615302324295, 0.02312229946255684, 0.040952928364276886, 0.019498655572533607, 0.0037710340693593025, 3.6679059121524915e-05, 0.0268322192132473, -0.022259525954723358, -0.01028137095272541, -0.008829036727547646, -0.02607010491192341, 0.00106498459354043, 0.009713378734886646, 0.03082973137497902, -0.007872797548770905, -0.002243208000436425, -0.013782788068056107, -0.012366403825581074, -0.013128519058227539, -0.006438438780605793, 0.009267613291740417, -0.03134739398956299, -0.02645835280418396, 0.010741516016423702, -0.012747461907565594, 0.005216178018599749, 0.01035326812416315, 0.0007607674924656749, -0.02289222553372383, 0.005464225076138973, 0.007585207000374794, -0.013157278299331665, -0.012761840596795082, -0.004371379967778921, 0.032900385558605194, 0.010691187344491482, 0.015184793621301651, 0.18095211684703827, -0.009073489345610142, -0.006222745403647423, 0.011287937872111797, -0.0027375046629458666, -0.017729971557855606, 0.021569307893514633, 0.01213633082807064, -0.007347944658249617, 0.02944929711520672, -0.007714622654020786, 0.012704323045909405, 0.019858144223690033, -0.005385137628763914, 0.024862222373485565, -0.018434569239616394, -0.0502421110868454, -0.009332320652902126, -0.03988884016871452, -0.02217325009405613, 0.002322295680642128, -0.014314831234514713, -0.008879365399479866, -0.016867199912667274, 0.0034978229086846113, -0.002180297626182437, -0.012783410027623177, -0.004116143099963665, 0.022762810811400414, 0.018161358311772346, 0.005086761899292469, -0.006370136048644781, 0.008102870546281338, 0.001728240866214037, -0.008368891663849354, -0.0032174219377338886, 0.006794332060962915, -0.004619427025318146, 0.023280473425984383, 0.013617423363029957, -0.000420152151491493, -0.02283470891416073, -0.0030610444955527782, -0.00505800312384963, -0.031203599646687508, -0.00022625306155532598, -0.013617423363029957, 0.0011243001790717244, 0.002874110359698534, 0.037904463708400726, 0.006326997186988592, -0.006154442671686411, 0.0393136590719223, -0.005072382278740406, -0.02036142721772194, -0.024905361235141754, 0.01363180298358202, 0.01599004678428173, -0.012804979458451271, 0.004774007014930248, -0.01234483439475298, 0.028327692300081253, -0.002514621941372752, 0.028917253017425537, -0.028140757232904434, -0.007585207000374794, -0.008534256368875504, 0.0012725892011076212, 0.02944929711520672, -0.011295127682387829, 0.011402974836528301, -0.016680264845490456, -0.022719671949744225, -0.019196685403585434, -0.046129561960697174, -0.020332667976617813, 0.02950681373476982, 0.00036914972588419914, 0.008879365399479866, 0.03508607670664787, -0.03502855822443962, 0.006071760319173336, -0.001322018913924694, -0.008124439977109432, 0.005996267776936293, -0.032095130532979965, -0.002773453714326024, 0.0008465953869745135, -0.012294505722820759, -0.008541446179151535, -0.012582097202539444, 0.005392326973378658, -0.01044673565775156, -0.006963291671127081, -0.006014242302626371, -0.01918230578303337, 0.013876255601644516, -0.010288559831678867, -0.003922019153833389, -0.011424544267356396, -0.028140757232904434, 0.060739174485206604, 0.020519603043794632, 0.02270529232919216, -0.019872523844242096, -0.006736814044415951, -0.006693675648421049, 0.007887177169322968, 0.016637125983834267, 0.0165652297437191, 0.007344349753111601, -0.039802566170692444, -0.012711512856185436, 0.0014972694916650653, 0.0067835478112101555, 0.0012249569408595562, 0.01969996839761734, -0.0108493622392416, 0.0020706537179648876, -0.012114761397242546, 0.012567717581987381, -0.019383618608117104, 0.002437331946566701, -0.02431580051779747, -0.0010362255852669477, -0.004037055652588606, -0.018952231854200363, -0.021914416924118996, -0.010640858672559261, -0.04201701283454895, 0.023064780980348587, 0.006628967355936766, 0.022288285195827484, 0.005507363472133875, 0.012625235132873058, 0.026185140013694763, 0.03002447821199894, 0.004374974872916937, 0.002433737041428685, 0.0028651230968534946, -0.001957414671778679, -0.0012555135181173682, 0.004040650557726622, -0.002969374880194664, -0.0047847917303442955, -0.005985483061522245, 0.0017255446873605251, 0.006028621923178434, -0.00868524145334959, -0.03008199669420719, -0.024718428030610085, -0.005453440360724926, -0.022906605154275894, -0.0019304531160742044, 0.010935639962553978, 0.005471414886415005, -0.01230169553309679, -0.010755895636975765, -0.025063537061214447, 0.0027860356494784355, -0.0378757044672966, 0.016292016953229904, 0.03453965485095978, -0.015860632061958313, -0.00901597086340189, -0.033187977969646454, -0.18670393526554108, 0.02874469943344593, 0.023898793384432793, -0.009497685357928276, 0.031376153230667114, 0.027349883690476418, 0.007085517980158329, 0.001824403996579349, -0.0010802628239616752, 0.017902527004480362, 0.022619014605879784, 0.015443624928593636, -0.03451089560985565, 0.008433599956333637, 0.0007499828352592885, 0.0310310460627079, 0.006053785793483257, -0.016536470502614975, 0.02579689398407936, 0.020591499283909798, 0.017169170081615448, -0.03154870867729187, -0.00811006035655737, 0.00972775835543871, -0.010885311290621758, -0.01139578502625227, -0.013955342583358288, 0.02460339106619358, -0.004209610167890787, -0.0055936407297849655, -0.015544282272458076, 0.019110407680273056, 0.032957904040813446, 0.01617698185145855, 0.012236988171935081, 0.009885933250188828, -0.011474872007966042, -0.016407053917646408, -0.009993779473006725, 0.004752437584102154, 0.021094784140586853, 0.017298584803938866, 0.01558742020279169, -0.018549606204032898, -0.002467888407409191, 0.0032857246696949005, 0.02993820048868656, 0.01541486568748951, 0.01650771126151085, -0.005191013682633638, 0.004047840368002653, -0.03088724985718727, -0.01501223910599947, -0.00658942386507988, 0.0057841697707772255, 0.012294505722820759, -0.021770622581243515, 0.0033773942850530148, -0.011949396692216396, -0.01798880286514759, 0.001259108423255384, -0.017485519871115685, 0.008253855630755424, -0.014171035960316658, -0.01584625244140625, -0.015961289405822754, -0.00979965552687645, 0.0031131701543927193, -0.0029442105442285538, -0.008491117507219315, 0.010295749641954899, -0.013696511276066303, 0.01588939130306244, 0.0031077777966856956, 0.025768134742975235, -0.007276047021150589, -0.030110755935311317, 0.007923126220703125, 0.02330923266708851, -0.0011979953851550817, -0.0025559631176292896, 0.04673350229859352, -0.011402974836528301, 0.0038788807578384876, 0.0032156244851648808, 0.01994442008435726, -0.0010425165528431535, 0.019901283085346222, -0.012071623466908932, -0.027867546305060387, 0.015443624928593636, 0.0013705497840419412, 0.0063881101086735725, -0.021828141063451767, 0.026328936219215393, -0.014940341003239155, -0.024301420897245407, 0.03068593703210354, -0.01617698185145855, -0.004428897984325886, 0.020447704941034317, -0.018779678270220757, -0.03082973137497902, 0.01574559509754181, 0.04937933757901192, 0.02254711650311947, -0.011043486185371876, 0.025135435163974762, 0.04319613426923752, -0.003440304659307003, -0.0516800619661808, 0.02921922318637371, 0.014271692372858524, -0.007369513623416424, 0.015918150544166565, 0.040809132158756256, 0.017773110419511795, -0.01836267113685608, 0.002192879794165492, 0.005924370139837265, 0.014494575560092926, -0.009504875168204308, 0.012244177982211113, 0.002228828612715006, -0.0022378158755600452, -0.020347047597169876, -0.07569389790296555, -0.0014109922340139747, 0.0008115452365018427, 0.0065642595291137695, -0.009929072111845016, 0.004684134852141142, -0.024804705753922462, 0.034338340163230896, -0.008879365399479866, 0.0030268928967416286, -0.017499899491667747, -0.02289222553372383, 0.003695541527122259, -0.017298584803938866, 0.019829384982585907, 0.00035926379496231675, -0.0075348783284425735, -0.0123951630666852, -0.00015311961760744452, 0.023194197565317154, -0.004791981540620327, -0.02993820048868656, 0.0025128244888037443, -0.007308400701731443, -0.009885933250188828, 0.017787490040063858, -0.031146081164479256, 0.033101700246334076, -0.006805116776376963, -0.005683512892574072, 0.01599004678428173, -0.027105432003736496, 0.019858144223690033, -0.01430045161396265, 0.006251504644751549, -0.0007072935695759952, -0.0003282579127699137, -0.01396972220391035, 0.0010748705826699734, -0.028615282848477364, -9.928060535457917e-06, 0.017643693834543228, 0.03189381957054138, -0.017816249281167984, -0.0007342552416957915, 0.01808946020901203, -0.030197031795978546, 0.023668721318244934, 0.011510821059346199, -0.021756242960691452, -0.009246043860912323, -0.014185415580868721, -0.007786520291119814, -0.00022816284035798162, 0.026185140013694763, -0.013437679037451744, 0.015184793621301651, 0.0049753207713365555, -0.008541446179151535, 0.0008209818042814732, -0.037214245647192, -0.01922544464468956, -0.008390461094677448, 0.00790155678987503, 0.024157626554369926, -0.0020760460756719112, -0.04187321662902832, -0.012121951207518578, 0.023438649252057076, 0.020246390253305435, 0.006805116776376963, 0.015731215476989746, -0.036897897720336914, -0.0044756317511200905, -0.0075348783284425735, -0.04201701283454895, -0.015227931551635265, -0.025868790224194527, 0.001982579007744789, -0.04995451867580414, -0.008814657106995583, -0.0029801595956087112, -0.031577467918395996, 0.006869825068861246, 0.018333911895751953, 0.0035391640849411488, 0.01941237784922123, 0.005701487418264151, 0.019613690674304962, -0.04963817074894905, -0.009871553629636765, 0.036092642694711685, 0.028802216053009033, -0.030657177790999413, -0.004022676032036543, 0.02874469943344593, 0.010547392070293427, -0.00505800312384963, -0.015501143410801888, -0.010331698693335056, -0.04638839513063431, -0.02579689398407936, -0.0783972442150116, 0.028759079053997993, -0.007455790881067514, -0.0039759427309036255, 0.00455471919849515, -0.0031419293954968452, -0.02279157005250454, -0.020879091694951057, -0.0031275497749447823, -0.007114277221262455, -0.0123951630666852, -0.00039296585600823164, -0.021137923002243042, -0.015688076615333557, -0.01912478730082512, -0.004385759588330984, 0.029003530740737915, 0.015184793621301651, 0.00581652345135808, 0.004899827763438225, 0.0031401319429278374, 0.01551552303135395, 0.026674045249819756, -0.0014819912612438202, -0.017111651599407196, 0.029104188084602356, -0.0330154225230217, 0.03916986286640167, -0.005510958377271891, -0.019685588777065277, 0.0192685816437006, -0.023165438324213028, -0.02256149612367153, 0.0108493622392416, 0.015458004549145699, 0.0010739718563854694, 0.016953475773334503, -0.00319944741204381, 0.04149935021996498, 0.017629314213991165, -0.0029783621430397034, -0.020965367555618286, -0.001322917640209198, -0.025164194405078888, -0.008023782633244991, 0.017643693834543228, -0.030225791037082672, 0.0059459395706653595, 0.029247982427477837, 0.020807193592190742, 0.02874469943344593, 0.0021713103633373976, -0.013322643004357815, -0.026242658495903015, 0.020663397386670113, -0.022158870473504066, 0.012912826612591743, 0.013452058658003807, -0.026630906388163567, 0.0015718634240329266, 0.021238578483462334, -0.009641480632126331, 0.02312229946255684, 0.003197649959474802, 0.02174186334013939, -0.019052889198064804, -0.016766542568802834, -0.0017875564517453313, 0.007319185417145491, -0.03353308513760567, -0.024948500096797943, -0.004849499557167292, 0.0007782925385981798, 0.02779565006494522, 0.011021916754543781, 0.025768134742975235, -0.0172266885638237, 0.01780186966061592, -0.009188525378704071, 0.025020398199558258, 0.004371379967778921, -0.018679020926356316, -0.003603871911764145, 0.03183630108833313, 0.010784653946757317, 0.04753875732421875, -0.01334421243518591, 0.005521743092685938, 0.007383893243968487, -0.0011854132171720266, -0.028442729264497757, 0.012661184184253216, 0.0007014519069343805, 0.00041071558371186256, 0.03356184437870979, -0.006905773654580116, 0.004479226656258106, 0.0012788802850991488, 0.02484784461557865, 0.01011600624769926, 0.0024768756702542305, -0.006190391723066568, -0.004529554862529039, -0.028960391879081726, -0.029708128422498703, -0.021066024899482727, -0.032095130532979965, -0.022906605154275894, 0.03212388977408409, 0.0026889739092439413, -0.001957414671778679, 0.015184793621301651, -0.01850646734237671, 0.012891257181763649, -0.019110407680273056, -0.0013993089087307453, 0.004533149767667055, 0.00022501731291413307, -0.025494923815131187, 0.025250470265746117, 0.010310129262506962, 0.01603318564593792, 0.02470404841005802, 0.0012060838053002954, 0.022734051570296288, 0.027105432003736496, 0.003280332311987877, -0.023208575323224068, 0.026961635798215866, 0.004889043513685465, -0.005147874820977449, 0.017614934593439102, -0.00039341519004665315, -0.015932530164718628, -0.03626519814133644, 0.003292914479970932, -0.019153546541929245, -0.00519820349290967, -0.012653994373977184, 0.06804397702217102, 0.03744431957602501, -0.02227390557527542, 0.01363180298358202, 0.008829036727547646, -0.006546285003423691, 0.01856398582458496, 0.004540339577943087, -0.01898099109530449, -0.03154870867729187, 0.018923474475741386, -0.007793710101395845, -0.022374562919139862, -0.008462359197437763, -0.03192257881164551, 0.02064901776611805, -0.015716835856437683, 0.0037027313373982906, 0.005004079546779394, 0.009238854050636292, 0.008325752802193165, 0.0316925048828125, 0.015529902651906013, 0.011244799941778183, -0.014401108026504517, -0.003979537636041641, 0.009691809304058552, 0.01665150560438633, 0.0034960254561156034, 0.0026835815515369177, 0.008836226537823677, 0.008951263502240181, -0.01650771126151085, -0.01712603121995926, -0.01341610960662365, -0.018664641305804253, -0.007822468876838684, -0.012790599837899208, 0.03649527207016945, 0.02911856770515442, 0.0011072244960814714, -0.015429245308041573, -0.025164194405078888, -0.020490843802690506, -0.0072257183492183685, 0.0011207053903490305, 0.011935017071664333, -0.013545526191592216, -0.012869687750935555]}, "text_id_to_ref_doc_id": {"36815fa0-0599-4a15-82ad-eb4a9ba6a7cf": "210c05e0-d41d-480f-ab17-20e3a4a1d9cc"}, "metadata_dict": {"36815fa0-0599-4a15-82ad-eb4a9ba6a7cf": {"file_path": "/workspaces/supabase-authentication/data/data.md", "file_name": "data.md", "file_type": "text/markdown", "file_size": 0, "creation_date": "2024-08-30", "last_modified_date": "2024-08-30", "_node_type": "TextNode", "document_id": "210c05e0-d41d-480f-ab17-20e3a4a1d9cc", "doc_id": "210c05e0-d41d-480f-ab17-20e3a4a1d9cc", "ref_doc_id": "210c05e0-d41d-480f-ab17-20e3a4a1d9cc"}}} --------------------------------------------------------------------------------