├── .editorconfig ├── .github ├── FUNDING.yml └── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── other.md ├── .gitignore ├── README.md ├── app.py ├── charts ├── __init__.py ├── bar_chart.py ├── base_chart.py ├── heatmap_chart.py ├── line_chart.py ├── map_chart.py └── pie_chart.py ├── data ├── config │ ├── databases.json │ └── models.json ├── models │ └── .gitignore ├── sample │ └── sample.db └── schemas │ └── sample.txt ├── db ├── __init__.py ├── base_database.py └── sql_database.py ├── docs └── CLOUD_MODELS.md ├── extras └── images │ ├── logo.png │ ├── screenshot-2.png │ └── screenshot.png ├── helpers ├── __init__.py ├── chart.py ├── db.py ├── file.py ├── model.py ├── prompt.py ├── response.py ├── schema.py └── string.py ├── models ├── __init__.py ├── anthropic_model.py ├── base_model.py ├── deep_seek_model.py ├── gemini_model.py ├── grok_model.py ├── local_gguf_model.py ├── local_model.py └── openai_model.py ├── prompts.txt ├── requirements-gguf.txt └── requirements.txt /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | charset = utf-8 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 4 9 | trim_trailing_whitespace = true 10 | 11 | [*.html] 12 | indent_size = 4 13 | 14 | [*.json] 15 | indent_size = 2 16 | insert_final_newline = false 17 | 18 | [{*[Mm]akefile*,*.mak,*.mk,depend}] 19 | indent_style = tab 20 | 21 | [*.{bat, cmd, cmd.*}] 22 | end_of_line = crlf 23 | 24 | [*.{yml, yaml}] 25 | indent_size = 2 26 | 27 | [*.gemspec] 28 | indent_size = 2 29 | 30 | [*.rb,Fastfile,Gemfile,Brewfile,Podfile] 31 | indent_size = 2 32 | 33 | [*.{kt,kts}] 34 | ktlint_standard_argument-list-wrapping = disabled 35 | ktlint_standard_trailing-comma-on-call-site = disabled 36 | ktlint_standard_trailing-comma-on-declaration-site = disabled 37 | 38 | [*.dart] 39 | indent_size = 2 40 | indent_brace_style = K&R 41 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: paulocoutinhox 2 | ko_fi: paulocoutinho 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 16 | 1. Go to '...' 17 | 2. Execute on terminal '....' 18 | 3. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **System (please complete the following information):** 27 | - OS: [e.g. Ubuntu] 28 | - Browser [e.g. Chrome, Safari, Firefox] 29 | 30 | **Additional context** 31 | Add any other context about the problem here. 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/other.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Other type 3 | about: Other issue type not related to bug or feature. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .DS_Store 3 | node_modules/ 4 | build/ 5 | .idea/ 6 | venv 7 | .env 8 | *.iml 9 | Thumbs.db 10 | .vscode 11 | __pycache__ 12 | *.lock 13 | .gradio/ 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Logo 4 | 5 |

6 | 7 | # DB TALK - AI 🧠 8 | 9 | **DB TALK - AI** is an interactive application built with **Python** and **Streamlit**, allowing users to query databases using **AI-generated SQL**. It supports **local AI models** or **cloud-based models** (such as **OpenAI GPT**) and provides results as **tables** and **charts**. 10 | 11 | ## 🚀 Features 12 | 13 | - Supports **SQLite, PostgreSQL, and MySQL** 14 | - **AI-powered SQL generation** using **cloud-based models** or **local AI models** 15 | - **Automatic database schema extraction** with overwrite confirmation 16 | - **Schema files saved with unique UUIDs or predefined names** 17 | - **Chart visualization** (Bar, Pie, and Line) 18 | - **Streamlit web interface** for easy interaction 19 | - **Secure database connection management** using a configuration file 20 | - **Modular database support** sql databases 21 | - **Dynamic model discovery** for both: 22 | - Local GGUF models (auto-detected from the `data/models/` directory) 23 | - Local Transformer models (loaded from `data/config/models.json`) 24 | - Supports various AI providers: 25 | - **OpenAI GPT** 26 | - **DeepSeek AI** 27 | - **Gemini AI** 28 | - **Grok AI** 29 | - **Anthropic AI** 30 | - **Local Transformer models** (using Hugging Face) 31 | - **Local GGUF models** (using GPT4All) 32 | - Supports a custom root directory using the **`DB_TALK_AI_ROOT`** environment variable 33 | 34 | ## 📞 Installation 35 | 36 | ### **1. Clone the Repository** 37 | ```sh 38 | git clone https://github.com/paulocoutinhox/db-talk-ai.git 39 | cd db-talk-ai 40 | ``` 41 | 42 | ### **2. Create a Virtual Environment** 43 | ```sh 44 | python3 -m venv .venv 45 | source .venv/bin/activate # macOS/Linux 46 | .venv\Scripts\activate # Windows 47 | ``` 48 | 49 | ### **3. Install Dependencies** 50 | ```sh 51 | pip install -r requirements.txt 52 | ``` 53 | 54 | If you want use local GGUF models install it too: 55 | ```sh 56 | pip install -r requirements-gguf.txt 57 | ``` 58 | 59 | ### **4. Download Local AI Models (Optional)** 60 | If you prefer to use a **local AI model** instead of a **cloud-based model** (e.g., OpenAI GPT), follow these steps: 61 | 62 | #### **Step 1: Download a Local AI Model** 63 | You can download **GGUF format** models from Hugging Face: 64 | - [Hugging Face - GGUF Models](https://huggingface.co/models) 65 | 66 | #### **Step 2: Place the Model in the `models/` Directory** 67 | 68 | #### **Step 3: Select the Model in the Streamlit App** 69 | The model selector will automatically list `.gguf` files in `models/`. Choose one in the UI. 70 | 71 | #### **Step 4: Run the Application** 72 | ```sh 73 | streamlit run app.py 74 | ``` 75 | 76 | ## ⚙️ Configuration 77 | 78 | ### **1. Set API Keys (For Cloud Models)** 79 | 80 | For setting up cloud-based models like **OpenAI GPT**, **DeepSeek AI**, **Gemini AI**, **Grok AI** and **Anthropic**, please refer to the separate guide: 81 | 82 | 📖 [Cloud Models Configuration](docs/CLOUD_MODELS.md) 83 | 84 | ### **2. Set Custom Root Directory (Optional)** 85 | You can specify a custom root directory by setting the **`DB_TALK_AI_ROOT`** environment variable. If set, the app will look for the `data/` directory under this path instead of the default local `data/`. 86 | 87 | #### **Linux/macOS** 88 | ```sh 89 | export DB_TALK_AI_ROOT="/path/to/custom/root" 90 | ``` 91 | 92 | #### **Windows (Command Prompt)** 93 | ```sh 94 | set DB_TALK_AI_ROOT="C:\path\to\custom\root" 95 | ``` 96 | 97 | #### **Windows (PowerShell)** 98 | ```powershell 99 | $env:DB_TALK_AI_ROOT="C:\path\to\custom\root" 100 | ``` 101 | 102 | ### **3. Configure Database Connections** 103 | Configure the `data/config/databases.json` file (or `{DB_TALK_AI_ROOT}/data/config/databases.json` if using the custom root): 104 | 105 | ```json 106 | [ 107 | { 108 | "name": "Sample SQLite DB", 109 | "type": "sql", 110 | "connection_string": "sqlite:///data/sample/sample.db", 111 | "schema": "sample.txt" 112 | }, 113 | { 114 | "name": "Production PostgreSQL", 115 | "type": "sql", 116 | "connection_string": "postgresql://user:password@localhost:5432/production_db" 117 | }, 118 | { 119 | "name": "MySQL Backup", 120 | "type": "sql", 121 | "connection_string": "mysql://user:password@host:3306/dbname" 122 | } 123 | ] 124 | ``` 125 | 126 | ### **4. Generate Database Schema** 127 | 1. Click **"Generate Database Schema"** in the UI. 128 | 2. A confirmation dialog will appear if a schema already exists. 129 | 3. The schema will be saved in the `data/schemas/` directory under the root directory. 130 | 4. The app automatically updates the `data/config/databases.json` with the new schema filename. 131 | 132 | ## 🛠️ Usage 133 | 134 | 1. **Run the Application** 135 | ```sh 136 | streamlit run app.py 137 | ``` 138 | 139 | 2. **Steps in the Web UI** 140 | - **Select a database** from the configured options 141 | - **Generate the database schema** if needed 142 | - **Choose an AI model** (cloud or local) 143 | - **Ask questions in natural language** to generate queries 144 | - **View results** in tables or **charts** (Bar, Pie, Line) 145 | 146 | ## 📂 Project Structure 147 | 148 | ``` 149 | db-talk-ai/ 150 | │ 151 | ├── README.md # Project documentation 152 | ├── app.py # Main entry point (Streamlit interface) 153 | ├── requirements.txt # List of core dependencies 154 | ├── requirements-gguf.txt # Dependencies for GGUF models 155 | │ 156 | ├── data/ # Data storage and configurations 157 | │ ├── config/ # Configuration files 158 | │ │ ├── databases.json # Database connection configurations 159 | │ │ └── models.json # Local AI model configurations (path, dtype, etc.) 160 | │ ├── models/ # Local GGUF AI model files 161 | │ │ └── Llama-3.2-3B-Instruct-Q5_K_M.gguf # Example local GGUF model 162 | │ ├── sample/ # Sample databases for testing 163 | │ │ └── sample.db # Example SQLite database 164 | │ └── schemas/ # Generated database schema files 165 | │ └── sample.txt # Example schema output file 166 | │ 167 | ├── models/ # AI model implementations 168 | │ ├── anthropic_model.py # Integration with Anthropic API 169 | │ ├── base_model.py # Abstract base class for AI models 170 | │ ├── local_model.py # Local model implementation using Hugging Face Transformers 171 | │ ├── local_gguf_model.py # Local GGUF model implementation using GPT4All 172 | │ ├── openai_model.py # Integration with OpenAI API 173 | │ ├── deep_seek_model.py # Integration with DeepSeek models 174 | │ ├── grok_model.py # Integration with Grok AI models 175 | │ └── gemini_model.py # Integration with Gemini AI models 176 | │ 177 | ├── extras/ # Additional resources (images, icons, etc.) 178 | │ 179 | ├── helpers/ # Utility functions 180 | │ ├── chart.py # Functions for generating charts 181 | │ ├── db.py # Database connection handling 182 | │ ├── file.py # File system helpers (e.g., loading models) 183 | │ ├── model.py # AI model management functions 184 | │ ├── prompt.py # Functions to create SQL prompts 185 | │ ├── response.py # Functions to process and clean AI responses 186 | │ ├── schema.py # Functions to generate database schemas 187 | │ └── string.py # String manipulation utilities 188 | │ 189 | ├── db/ # Database connection implementations 190 | │ ├── base_database.py # Base class for database connections 191 | │ └── sql_database.py # SQL database connection implementation 192 | │ 193 | ├── charts/ # Chart implementations for data visualization 194 | │ ├── base_chart.py # Base class for chart types 195 | │ ├── bar_chart.py # Bar chart visualization 196 | │ ├── line_chart.py # Line chart visualization 197 | │ ├── pie_chart.py # Pie chart visualization 198 | │ ├── heatmap_chart.py # Heatmap visualization 199 | │ └── map_chart.py # Geographical map chart visualization 200 | │ 201 | ├── prompts.txt # Example prompts for generating SQL queries 202 | ``` 203 | 204 | ## 🤝 Contributing 205 | 206 | 1. Fork the repository 207 | 2. Create a new branch (`git checkout -b feature-xyz`) 208 | 3. Commit changes (`git commit -m "Added new feature"`) 209 | 4. Push to the branch (`git push origin feature-xyz`) 210 | 5. Open a **pull request** 211 | 212 | ## 📞 Contact 213 | 214 | For issues or contributions, open a **GitHub issue** or contact: 215 | 💎 **paulocoutinhox@gmail.com** 216 | 🔗 **[GitHub](https://github.com/paulocoutinho)** 217 | 218 | ## 🖼️ Screenshots 219 | 220 | 221 | 222 | 223 | 224 | ## 📜 License 225 | 226 | [MIT](http://opensource.org/licenses/MIT) 227 | 228 | Copyright (c) 2025, Paulo Coutinho 229 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | from helpers import chart, db, model, prompt, response, schema 4 | 5 | # Sidebar - Application title 6 | st.sidebar.title("DB TALK - AI 🧠") 7 | 8 | 9 | # Sidebar - Database Selection 10 | st.sidebar.subheader("🛢️ Database") 11 | 12 | 13 | # Load databases 14 | databases = db.load_databases() 15 | db_names = [db["name"] for db in databases] 16 | selected_db_name = st.sidebar.selectbox("Select Database:", db_names) 17 | 18 | 19 | # Retrieve the full configuration for the selected database 20 | selected_db = next((db for db in databases if db["name"] == selected_db_name), None) 21 | 22 | # Initialize database connection 23 | if selected_db: 24 | db_type = selected_db["type"] 25 | db_path = selected_db["connection_string"] 26 | 27 | try: 28 | db_conn = db.create_database(db_type, db_path) 29 | except ValueError as e: 30 | st.sidebar.error(str(e)) 31 | st.stop() 32 | else: 33 | st.sidebar.error("❌ Database configuration not found.") 34 | st.stop() 35 | 36 | 37 | # Generate schema file 38 | schema_file = selected_db.get("schema") 39 | 40 | 41 | # Dialogs 42 | @st.dialog("⚠️ Confirm Schema Overwrite", width="small") 43 | def confirm_overwrite(): 44 | st.write( 45 | "This will overwrite the existing schema if it exists. Do you want to continue?" 46 | ) 47 | if st.button("Yes, overwrite"): 48 | # Generate schema 49 | success, message = db.generate_schema(selected_db, databases, db_conn) 50 | 51 | if success: 52 | st.success(message) 53 | st.rerun() 54 | else: 55 | st.error(message) 56 | 57 | 58 | # Sidebar - Schema Management 59 | st.sidebar.subheader("📝 Database Schema") 60 | 61 | if schema_file: 62 | st.sidebar.success(f"✅ Schema was generated: {schema_file}") 63 | 64 | if st.sidebar.button("Regenerate Database Schema"): 65 | confirm_overwrite() 66 | else: 67 | if st.sidebar.button("Generate Database Schema"): 68 | confirm_overwrite() 69 | 70 | 71 | # Load database schema 72 | schema_info, schema_warning = schema.load_schema(schema_file) 73 | 74 | if schema_warning: 75 | st.sidebar.warning(schema_warning) 76 | 77 | 78 | # Sidebar - AI Model Selection 79 | st.sidebar.subheader("🤖 AI Model") 80 | 81 | models = model.load_models() 82 | 83 | if not models: 84 | st.sidebar.error("❌ No models available, please add models.") 85 | st.stop() 86 | 87 | model_names = [model.name() for model in models] 88 | selected_model_name = st.sidebar.selectbox("Select AI Model:", model_names) 89 | selected_model = next((m for m in models if m.name() == selected_model_name), None) 90 | 91 | model_variants = selected_model.get_variants() 92 | 93 | if model_variants: 94 | variant_keys = list(model_variants.keys()) 95 | default_variant = selected_model.get_default_variant() 96 | 97 | # Pre-select the default variant if available 98 | selected_model_variant = st.sidebar.selectbox( 99 | "Select Model Variant:", 100 | variant_keys, 101 | format_func=lambda key: model_variants[key], 102 | index=( 103 | variant_keys.index(default_variant) 104 | if default_variant in variant_keys 105 | else 0 106 | ), 107 | ) 108 | else: 109 | selected_model_variant = selected_model.get_default_variant() 110 | 111 | 112 | # Sidebar - Chart Options 113 | st.sidebar.subheader("📊 Chart Options") 114 | generate_chart = st.sidebar.checkbox("Enable Chart Generation", value=False) 115 | 116 | 117 | # Load list of charts 118 | chart_classes = chart.load_charts() 119 | chart_names = [chart_class.name for chart_class in chart_classes] 120 | 121 | selected_chart = ( 122 | st.sidebar.selectbox("Select Chart Type:", chart_names) if generate_chart else None 123 | ) 124 | 125 | 126 | # Sidebar - Options 127 | st.sidebar.subheader("⚙️ Options") 128 | show_query = st.sidebar.checkbox("Show Query After Execution", value=False) 129 | 130 | 131 | # Main Area - User prompt 132 | st.header("💬 Ask Something About the Database") 133 | user_prompt = st.text_input("Enter your question below:") 134 | 135 | if st.button("🚀 Generate"): 136 | if not user_prompt: 137 | st.warning("⚠️ Please enter a prompt.") 138 | else: 139 | chart_class = None 140 | chart_prompt = None 141 | 142 | if generate_chart and selected_chart: 143 | chart_class = next( 144 | (cls for cls in chart_classes if cls.name == selected_chart), None 145 | ) 146 | 147 | if chart_class: 148 | chart_prompt = chart_class.prompt 149 | 150 | # Build prompt with chart-specific prompt, if available 151 | messages = prompt.build( 152 | db_conn.get_driver_name(), 153 | schema_info, 154 | user_prompt, 155 | chart_prompt, 156 | ) 157 | 158 | # Generate query using the selected model 159 | try: 160 | query = selected_model.run( 161 | messages, 162 | variant=selected_model_variant, 163 | ) 164 | 165 | query = response.clean(query) 166 | 167 | # Validate the generated query 168 | if query.lower().startswith("error:"): 169 | st.error(query) 170 | else: 171 | # Show query 172 | if show_query: 173 | st.divider() 174 | 175 | with st.expander("🔍 Generated Query"): 176 | st.code( 177 | query, 178 | language=db_conn.get_code_language(), 179 | ) 180 | 181 | # Execute query 182 | df = db_conn.run_query(query) 183 | 184 | if df is None or df.empty: 185 | st.warning("⚠️ No results found.") 186 | else: 187 | st.divider() 188 | st.subheader("📊 Query Results") 189 | st.dataframe(df) 190 | 191 | # Generate chart if enabled 192 | if generate_chart: 193 | if chart_class: 194 | st.divider() 195 | st.subheader("📈 Generated Chart") 196 | 197 | # Pass DataFrame directly to the chart class 198 | chart_instance = chart_class() 199 | chart_instance.render(df) 200 | 201 | except ValueError as e: 202 | st.error(f"❌ Error: {str(e)}") 203 | -------------------------------------------------------------------------------- /charts/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocoutinhox/db-talk-ai/21e7f6380340461a81cb8dd1cbae88652b31f6a5/charts/__init__.py -------------------------------------------------------------------------------- /charts/bar_chart.py: -------------------------------------------------------------------------------- 1 | import plotly.express as px 2 | from pandas import DataFrame 3 | 4 | from .base_chart import BaseChart 5 | 6 | 7 | class BarChart(BaseChart): 8 | name = "Bar Chart" 9 | prompt = """ 10 | Generate a bar chart using two columns: 11 | - One categorical column for the x-axis. 12 | - One numerical column for the y-axis. 13 | 14 | The output should clearly show the numerical values distributed across different categories. 15 | Ensure appropriate labels for clarity. 16 | """ 17 | 18 | def generate(self, df: DataFrame): 19 | if len(df.columns) >= 2: 20 | x, y = df.columns[0], df.columns[1] 21 | return px.bar(df, x=x, y=y, title=f"{y} by {x}") 22 | else: 23 | raise ValueError( 24 | "The DataFrame must have at least two columns for a bar chart." 25 | ) 26 | -------------------------------------------------------------------------------- /charts/base_chart.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | from pandas import DataFrame 3 | 4 | 5 | class BaseChart: 6 | name = "Base Chart" 7 | prompt = "Generate a simple chart." 8 | 9 | def generate(self, df: DataFrame): 10 | raise NotImplementedError("You must implement the generate method.") 11 | 12 | def render(self, df: DataFrame): 13 | chart = self.generate(df) 14 | 15 | if chart: 16 | st.plotly_chart(chart) 17 | -------------------------------------------------------------------------------- /charts/heatmap_chart.py: -------------------------------------------------------------------------------- 1 | import plotly.express as px 2 | from pandas import DataFrame 3 | 4 | from .base_chart import BaseChart 5 | 6 | 7 | class HeatmapChart(BaseChart): 8 | name = "Heatmap Chart" 9 | prompt = """ 10 | Generate a heatmap using geographical coordinates: 11 | - The second column should contain latitude values. 12 | - The third column should contain longitude values. 13 | - The fourth column (optional) should contain intensity values. 14 | 15 | The map should visualize data density or intensity using a heatmap overlay. 16 | """ 17 | 18 | def generate(self, df: DataFrame): 19 | if len(df.columns) >= 3: 20 | label, lat, lon = df.columns[0], df.columns[1], df.columns[2] 21 | intensity = df.columns[3] if len(df.columns) > 3 else None 22 | 23 | heatmap = px.density_map( 24 | df, 25 | lat=lat, 26 | lon=lon, 27 | z=intensity, 28 | radius=10, 29 | hover_name=label, 30 | hover_data=( 31 | {lat: True, lon: True, intensity: True} 32 | if intensity 33 | else {lat: True, lon: True} 34 | ), 35 | title="Geographical Heatmap", 36 | height=600, 37 | width=800, 38 | center=dict(lat=df[lat].mean(), lon=df[lon].mean()), 39 | color_continuous_scale="Viridis", 40 | ) 41 | 42 | return heatmap 43 | else: 44 | raise ValueError( 45 | "The DataFrame must have at least three columns: label, latitude, and longitude." 46 | ) 47 | -------------------------------------------------------------------------------- /charts/line_chart.py: -------------------------------------------------------------------------------- 1 | import plotly.express as px 2 | from pandas import DataFrame 3 | 4 | from .base_chart import BaseChart 5 | 6 | 7 | class LineChart(BaseChart): 8 | name = "Line Chart" 9 | prompt = """ 10 | Generate a line chart using time-series or ordered numerical data: 11 | - The x-axis should contain time-related or sequential data. 12 | - The y-axis should contain numerical values. 13 | 14 | The line should smoothly connect the points in order, representing the progression over time or sequence. 15 | """ 16 | 17 | def generate(self, df: DataFrame): 18 | if len(df.columns) >= 2: 19 | x, y = df.columns[0], df.columns[1] 20 | return px.line(df, x=x, y=y, title=f"{y} over {x}") 21 | else: 22 | raise ValueError( 23 | "The DataFrame must have at least two columns for a line chart." 24 | ) 25 | -------------------------------------------------------------------------------- /charts/map_chart.py: -------------------------------------------------------------------------------- 1 | import plotly.express as px 2 | from pandas import DataFrame 3 | 4 | from .base_chart import BaseChart 5 | 6 | 7 | class MapChart(BaseChart): 8 | name = "Map Chart" 9 | prompt = """ 10 | Generate a scatter map using geographical coordinates: 11 | - The second column should contain latitude values. 12 | - The third column should contain longitude values. 13 | 14 | The map should plot points based on the provided geographical coordinates. 15 | Additional columns can be used for hover information. 16 | """ 17 | 18 | def generate(self, df: DataFrame): 19 | if len(df.columns) >= 3: 20 | label, lat, lon = df.columns[0], df.columns[1], df.columns[2] 21 | 22 | # Using scatter_mapbox with default layout 23 | map = px.scatter_map( 24 | df, 25 | lat=lat, 26 | lon=lon, 27 | text=label, 28 | hover_name=label, 29 | hover_data={lat: True, lon: True}, 30 | title="Geographical Scatter Map", 31 | height=600, 32 | width=800, 33 | ) 34 | 35 | # Customize marker appearance 36 | map.update_traces( 37 | marker=dict( 38 | size=14, 39 | symbol="marker", 40 | color="red", 41 | opacity=0.9, 42 | ) 43 | ) 44 | 45 | return map 46 | else: 47 | raise ValueError( 48 | "The DataFrame must have at least three columns: label, latitude, and longitude." 49 | ) 50 | -------------------------------------------------------------------------------- /charts/pie_chart.py: -------------------------------------------------------------------------------- 1 | import plotly.express as px 2 | from pandas import DataFrame 3 | 4 | from .base_chart import BaseChart 5 | 6 | 7 | class PieChart(BaseChart): 8 | name = "Pie Chart" 9 | prompt = """ 10 | Generate a pie chart representing the distribution of categories: 11 | - The first column should contain category names (labels). 12 | - The second column should contain numerical values (sizes). 13 | 14 | The chart should clearly show the proportion of each category relative to the total. 15 | """ 16 | 17 | def generate(self, df: DataFrame): 18 | if len(df.columns) >= 2: 19 | x, y = df.columns[0], df.columns[1] 20 | return px.pie(df, names=x, values=y, title=f"{y} Distribution by {x}") 21 | else: 22 | raise ValueError( 23 | "The DataFrame must have at least two columns for a pie chart." 24 | ) 25 | -------------------------------------------------------------------------------- /data/config/databases.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Sample SQLite DB", 4 | "type": "sql", 5 | "connection_string": "sqlite:///data/sample/sample.db", 6 | "schema": "sample.txt" 7 | } 8 | ] -------------------------------------------------------------------------------- /data/config/models.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Qwen 2.5 3B Instruct", 4 | "path": "Qwen/Qwen2.5-3B-Instruct", 5 | "torch_dtype": "auto" 6 | } 7 | ] -------------------------------------------------------------------------------- /data/models/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /data/sample/sample.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocoutinhox/db-talk-ai/21e7f6380340461a81cb8dd1cbae88652b31f6a5/data/sample/sample.db -------------------------------------------------------------------------------- /data/schemas/sample.txt: -------------------------------------------------------------------------------- 1 | TABLE: US_CITIES 2 | Columns: 3 | - ID (INTEGER) PRIMARY KEY NULLABLE 4 | - ID_STATE (INTEGER) NOT NULL 5 | - CITY (TEXT(50)) NOT NULL 6 | - COUNTY (TEXT(50)) NOT NULL 7 | - LATITUDE (DOUBLE) NOT NULL 8 | - LONGITUDE (DOUBLE) NOT NULL 9 | 10 | Foreign Keys: 11 | - ID_STATE → US_STATES.ID ON DELETE NO ACTION ON UPDATE NO ACTION 12 | 13 | TABLE: US_STATES 14 | Columns: 15 | - ID (INTEGER) PRIMARY KEY NULLABLE 16 | - STATE_CODE (TEXT(2)) NOT NULL 17 | - STATE_NAME (TEXT(50)) NOT NULL 18 | 19 | TABLE: albums 20 | Columns: 21 | - AlbumId (INTEGER) PRIMARY KEY NOT NULL 22 | - Title (NVARCHAR(160)) NOT NULL 23 | - ArtistId (INTEGER) NOT NULL 24 | 25 | Foreign Keys: 26 | - ArtistId → artists.ArtistId ON DELETE NO ACTION ON UPDATE NO ACTION 27 | 28 | Indexes: 29 | - IFK_AlbumArtistId (NON-UNIQUE): ArtistId 30 | 31 | TABLE: artists 32 | Columns: 33 | - ArtistId (INTEGER) PRIMARY KEY NOT NULL 34 | - Name (NVARCHAR(120)) NULLABLE 35 | 36 | TABLE: customers 37 | Columns: 38 | - CustomerId (INTEGER) PRIMARY KEY NOT NULL 39 | - FirstName (NVARCHAR(40)) NOT NULL 40 | - LastName (NVARCHAR(20)) NOT NULL 41 | - Company (NVARCHAR(80)) NULLABLE 42 | - Address (NVARCHAR(70)) NULLABLE 43 | - City (NVARCHAR(40)) NULLABLE 44 | - State (NVARCHAR(40)) NULLABLE 45 | - Country (NVARCHAR(40)) NULLABLE 46 | - PostalCode (NVARCHAR(10)) NULLABLE 47 | - Phone (NVARCHAR(24)) NULLABLE 48 | - Fax (NVARCHAR(24)) NULLABLE 49 | - Email (NVARCHAR(60)) NOT NULL 50 | - SupportRepId (INTEGER) NULLABLE 51 | 52 | Foreign Keys: 53 | - SupportRepId → employees.EmployeeId ON DELETE NO ACTION ON UPDATE NO ACTION 54 | 55 | Indexes: 56 | - IFK_CustomerSupportRepId (NON-UNIQUE): SupportRepId 57 | 58 | TABLE: employees 59 | Columns: 60 | - EmployeeId (INTEGER) PRIMARY KEY NOT NULL 61 | - LastName (NVARCHAR(20)) NOT NULL 62 | - FirstName (NVARCHAR(20)) NOT NULL 63 | - Title (NVARCHAR(30)) NULLABLE 64 | - ReportsTo (INTEGER) NULLABLE 65 | - BirthDate (DATETIME) NULLABLE 66 | - HireDate (DATETIME) NULLABLE 67 | - Address (NVARCHAR(70)) NULLABLE 68 | - City (NVARCHAR(40)) NULLABLE 69 | - State (NVARCHAR(40)) NULLABLE 70 | - Country (NVARCHAR(40)) NULLABLE 71 | - PostalCode (NVARCHAR(10)) NULLABLE 72 | - Phone (NVARCHAR(24)) NULLABLE 73 | - Fax (NVARCHAR(24)) NULLABLE 74 | - Email (NVARCHAR(60)) NULLABLE 75 | 76 | Foreign Keys: 77 | - ReportsTo → employees.EmployeeId ON DELETE NO ACTION ON UPDATE NO ACTION 78 | 79 | Indexes: 80 | - IFK_EmployeeReportsTo (NON-UNIQUE): ReportsTo 81 | 82 | TABLE: genres 83 | Columns: 84 | - GenreId (INTEGER) PRIMARY KEY NOT NULL 85 | - Name (NVARCHAR(120)) NULLABLE 86 | 87 | TABLE: invoice_items 88 | Columns: 89 | - InvoiceLineId (INTEGER) PRIMARY KEY NOT NULL 90 | - InvoiceId (INTEGER) NOT NULL 91 | - TrackId (INTEGER) NOT NULL 92 | - UnitPrice (NUMERIC(10, 2)) NOT NULL 93 | - Quantity (INTEGER) NOT NULL 94 | 95 | Foreign Keys: 96 | - TrackId → tracks.TrackId ON DELETE NO ACTION ON UPDATE NO ACTION 97 | - InvoiceId → invoices.InvoiceId ON DELETE NO ACTION ON UPDATE NO ACTION 98 | 99 | Indexes: 100 | - IFK_InvoiceLineInvoiceId (NON-UNIQUE): InvoiceId 101 | - IFK_InvoiceLineTrackId (NON-UNIQUE): TrackId 102 | 103 | TABLE: invoices 104 | Columns: 105 | - InvoiceId (INTEGER) PRIMARY KEY NOT NULL 106 | - CustomerId (INTEGER) NOT NULL 107 | - InvoiceDate (DATETIME) NOT NULL 108 | - BillingAddress (NVARCHAR(70)) NULLABLE 109 | - BillingCity (NVARCHAR(40)) NULLABLE 110 | - BillingState (NVARCHAR(40)) NULLABLE 111 | - BillingCountry (NVARCHAR(40)) NULLABLE 112 | - BillingPostalCode (NVARCHAR(10)) NULLABLE 113 | - Total (NUMERIC(10, 2)) NOT NULL 114 | 115 | Foreign Keys: 116 | - CustomerId → customers.CustomerId ON DELETE NO ACTION ON UPDATE NO ACTION 117 | 118 | Indexes: 119 | - IFK_InvoiceCustomerId (NON-UNIQUE): CustomerId 120 | 121 | TABLE: media_types 122 | Columns: 123 | - MediaTypeId (INTEGER) PRIMARY KEY NOT NULL 124 | - Name (NVARCHAR(120)) NULLABLE 125 | 126 | TABLE: playlist_track 127 | Columns: 128 | - PlaylistId (INTEGER) PRIMARY KEY NOT NULL 129 | - TrackId (INTEGER) PRIMARY KEY NOT NULL 130 | 131 | Foreign Keys: 132 | - TrackId → tracks.TrackId ON DELETE NO ACTION ON UPDATE NO ACTION 133 | - PlaylistId → playlists.PlaylistId ON DELETE NO ACTION ON UPDATE NO ACTION 134 | 135 | Indexes: 136 | - IFK_PlaylistTrackTrackId (NON-UNIQUE): TrackId 137 | 138 | TABLE: playlists 139 | Columns: 140 | - PlaylistId (INTEGER) PRIMARY KEY NOT NULL 141 | - Name (NVARCHAR(120)) NULLABLE 142 | 143 | TABLE: tracks 144 | Columns: 145 | - TrackId (INTEGER) PRIMARY KEY NOT NULL 146 | - Name (NVARCHAR(200)) NOT NULL 147 | - AlbumId (INTEGER) NULLABLE 148 | - MediaTypeId (INTEGER) NOT NULL 149 | - GenreId (INTEGER) NULLABLE 150 | - Composer (NVARCHAR(220)) NULLABLE 151 | - Milliseconds (INTEGER) NOT NULL 152 | - Bytes (INTEGER) NULLABLE 153 | - UnitPrice (NUMERIC(10, 2)) NOT NULL 154 | 155 | Foreign Keys: 156 | - MediaTypeId → media_types.MediaTypeId ON DELETE NO ACTION ON UPDATE NO ACTION 157 | - GenreId → genres.GenreId ON DELETE NO ACTION ON UPDATE NO ACTION 158 | - AlbumId → albums.AlbumId ON DELETE NO ACTION ON UPDATE NO ACTION 159 | 160 | Indexes: 161 | - IFK_TrackAlbumId (NON-UNIQUE): AlbumId 162 | - IFK_TrackGenreId (NON-UNIQUE): GenreId 163 | - IFK_TrackMediaTypeId (NON-UNIQUE): MediaTypeId 164 | -------------------------------------------------------------------------------- /db/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocoutinhox/db-talk-ai/21e7f6380340461a81cb8dd1cbae88652b31f6a5/db/__init__.py -------------------------------------------------------------------------------- /db/base_database.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | 4 | class BaseDatabase(ABC): 5 | """Base class for all database connections.""" 6 | 7 | def __init__(self, db_url): 8 | self.db_url = db_url 9 | 10 | @abstractmethod 11 | def connect(self): 12 | """Establish connection with the database.""" 13 | pass 14 | 15 | @abstractmethod 16 | def run_query(self, query): 17 | """Run a query and return the result.""" 18 | pass 19 | 20 | @abstractmethod 21 | def generate_schema(self, schema_file): 22 | """Generate the database schema and save it to a file.""" 23 | pass 24 | 25 | @abstractmethod 26 | def get_code_language(self): 27 | """Get code language.""" 28 | pass 29 | 30 | @abstractmethod 31 | def get_driver_name(self): 32 | """Get database driver name.""" 33 | pass 34 | -------------------------------------------------------------------------------- /db/sql_database.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | from sqlalchemy import create_engine, inspect, text 3 | from streamlit import error 4 | 5 | from .base_database import BaseDatabase 6 | 7 | """ 8 | - SQLite: 9 | sqlite:///data/sample.db (relative) 10 | sqlite:///absolute/path/sample.db (absolute) 11 | 12 | - PostgreSQL: 13 | postgresql://user:password@host:port/dbname 14 | 15 | - MySQL: 16 | mysql://user:password@host:port/dbname 17 | """ 18 | 19 | 20 | class SQLDatabase(BaseDatabase): 21 | """SQL database implementation.""" 22 | 23 | def __init__(self, db_url): 24 | super().__init__(db_url) 25 | self.engine = create_engine(self.db_url) 26 | 27 | def connect(self): 28 | """Connect to the SQL database.""" 29 | try: 30 | return self.engine.connect() 31 | except Exception as e: 32 | error(f"❌ Error connecting to the database: {e}") 33 | return None 34 | 35 | def run_query(self, query): 36 | """Execute a SQL query and return the result.""" 37 | try: 38 | with self.engine.connect() as conn: 39 | return pd.read_sql(text(query), conn) 40 | except Exception as e: 41 | error(f"❌ Error executing the query: {e}") 42 | return None 43 | 44 | def get_code_language(self): 45 | """Get code language.""" 46 | return "sql" 47 | 48 | def get_driver_name(self): 49 | """Get database driver name.""" 50 | cs_data = self.db_url.split(":") 51 | return cs_data[0] 52 | 53 | def generate_schema(self, schema_file="schema.txt"): 54 | """Generates a schema file for the connected database with enhanced details.""" 55 | try: 56 | inspector = inspect(self.engine) 57 | tables = inspector.get_table_names() 58 | 59 | if not tables: 60 | return "⚠️ No tables found in the database." 61 | 62 | schema_details = [] 63 | 64 | for table in tables: 65 | schema_details.append(f"TABLE: {table}\nColumns:") 66 | 67 | # Get column information 68 | columns = inspector.get_columns(table) 69 | for col in columns: 70 | col_name = col["name"] 71 | col_type = col["type"] 72 | col_nullable = "NULLABLE" if col["nullable"] else "NOT NULL" 73 | col_default = f"DEFAULT {col['default']}" if col["default"] else "" 74 | col_pk = "PRIMARY KEY" if col.get("primary_key") else "" 75 | col_unique = "UNIQUE" if col.get("unique") else "" 76 | 77 | # Format column details 78 | col_details = f"- {col_name} ({col_type}) {col_pk} {col_unique} {col_nullable} {col_default}".strip() 79 | schema_details.append(col_details) 80 | 81 | # Get foreign keys 82 | foreign_keys = inspector.get_foreign_keys(table) 83 | if foreign_keys: 84 | schema_details.append("\nForeign Keys:") 85 | for fk in foreign_keys: 86 | fk_column = fk["constrained_columns"][0] 87 | ref_table = fk["referred_table"] 88 | ref_column = fk["referred_columns"][0] 89 | on_delete = f"ON DELETE {fk.get('options', {}).get('ondelete', 'NO ACTION')}".upper() 90 | on_update = f"ON UPDATE {fk.get('options', {}).get('onupdate', 'NO ACTION')}".upper() 91 | schema_details.append( 92 | f"- {fk_column} → {ref_table}.{ref_column} {on_delete} {on_update}" 93 | ) 94 | 95 | # Get indexes 96 | indexes = inspector.get_indexes(table) 97 | if indexes: 98 | schema_details.append("\nIndexes:") 99 | for idx in indexes: 100 | index_name = idx["name"] 101 | columns_str = ", ".join(idx["column_names"]) 102 | is_unique = "UNIQUE" if idx["unique"] else "NON-UNIQUE" 103 | schema_details.append( 104 | f"- {index_name} ({is_unique}): {columns_str}" 105 | ) 106 | 107 | # Get triggers (if supported) 108 | try: 109 | with self.engine.connect() as conn: 110 | triggers = conn.execute( 111 | text( 112 | f"SELECT name FROM sqlite_master WHERE type='trigger' AND tbl_name='{table}';" 113 | ) 114 | ) 115 | triggers = [row[0] for row in triggers] 116 | if triggers: 117 | schema_details.append("\nTriggers:") 118 | for trigger in triggers: 119 | schema_details.append(f"- {trigger}") 120 | except Exception: 121 | pass # Ignore if triggers are not supported 122 | 123 | # Get views (if applicable) 124 | views = inspector.get_view_names() 125 | if table in views: 126 | schema_details.append("\nVIEW (Readonly Table)") 127 | 128 | schema_details.append("") # Blank line to separate tables 129 | 130 | # Write the schema to the file 131 | with open(schema_file, "w", encoding="utf-8") as f: 132 | f.write("\n".join(schema_details)) 133 | 134 | return f"✅ Schema file '{schema_file}' generated successfully." 135 | 136 | except Exception as e: 137 | return f"❌ Error generating schema: {e}" 138 | -------------------------------------------------------------------------------- /docs/CLOUD_MODELS.md: -------------------------------------------------------------------------------- 1 | 2 | # CLOUD MODELS 3 | 4 | This document explains how to configure API keys for cloud-based models like **OpenAI GPT**, **DeepSeek AI**, **Gemini AI**, **Grok AI** and **Anthropic AI**. 5 | 6 | ## 🔑 **Supported API Providers** 7 | 8 | - **OpenAI GPT** 9 | - **DeepSeek AI** 10 | - **Gemini AI** 11 | - **Grok AI** 12 | - **Anthropic AI** 13 | 14 | ## ⚙️ **How to Set API Keys** 15 | 16 | Set the appropriate API keys for each provider as environment variables. 17 | 18 | ### 🔸 **OpenAI API Key** 19 | 20 | #### Linux/macOS: 21 | ```bash 22 | export OPENAI_API_KEY="your-openai-api-key" 23 | ``` 24 | 25 | #### Windows (Command Prompt): 26 | ```cmd 27 | set OPENAI_API_KEY="your-openai-api-key" 28 | ``` 29 | 30 | #### Windows (PowerShell): 31 | ```powershell 32 | $env:OPENAI_API_KEY="your-openai-api-key" 33 | ``` 34 | 35 | ### 🔸 **DeepSeek API Key** 36 | 37 | #### Linux/macOS: 38 | ```bash 39 | export DEEPSEEK_API_KEY="your-deepseek-api-key" 40 | ``` 41 | 42 | #### Windows (Command Prompt): 43 | ```cmd 44 | set DEEPSEEK_API_KEY="your-deepseek-api-key" 45 | ``` 46 | 47 | #### Windows (PowerShell): 48 | ```powershell 49 | $env:DEEPSEEK_API_KEY="your-deepseek-api-key" 50 | ``` 51 | 52 | ### 🔸 **Gemini API Key** 53 | 54 | #### Linux/macOS: 55 | ```bash 56 | export GEMINI_API_KEY="your-gemini-api-key" 57 | ``` 58 | 59 | #### Windows (Command Prompt): 60 | ```cmd 61 | set GEMINI_API_KEY="your-gemini-api-key" 62 | ``` 63 | 64 | #### Windows (PowerShell): 65 | ```powershell 66 | $env:GEMINI_API_KEY="your-gemini-api-key" 67 | ``` 68 | 69 | ### 🔸 **Grok AI API Key** 70 | 71 | #### Linux/macOS: 72 | ```bash 73 | export XAI_API_KEY="your-xai-api-key" 74 | ``` 75 | 76 | #### Windows (Command Prompt): 77 | ```cmd 78 | set XAI_API_KEY="your-xai-api-key" 79 | ``` 80 | 81 | #### Windows (PowerShell): 82 | ```powershell 83 | $env:XAI_API_KEY="your-xai-api-key" 84 | ``` 85 | 86 | ### 🔸 **Anthropic AI API Key** 87 | 88 | #### Linux/macOS: 89 | ```bash 90 | export ANTHROPIC_API_KEY="your-anthropic-api-key" 91 | ``` 92 | 93 | #### Windows (Command Prompt): 94 | ```cmd 95 | set ANTHROPIC_API_KEY="your-anthropic-api-key" 96 | ``` 97 | 98 | #### Windows (PowerShell): 99 | ```powershell 100 | $env:ANTHROPIC_API_KEY="your-anthropic-api-key" 101 | ``` 102 | 103 | ## 📌 **Persistent Configuration** 104 | 105 | To make these environment variables permanent: 106 | 107 | - **Linux/macOS:** Add the export commands to your `~/.bashrc` or `~/.zshrc` file. 108 | - **Windows:** Set environment variables via **System Properties** > **Advanced** > **Environment Variables**. 109 | 110 | ## ❓ **Troubleshooting** 111 | 112 | - Ensure your API key is valid and active. 113 | - Restart your terminal or development environment after setting the environment variables. 114 | - Double-check the exact spelling of the environment variable names. 115 | -------------------------------------------------------------------------------- /extras/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocoutinhox/db-talk-ai/21e7f6380340461a81cb8dd1cbae88652b31f6a5/extras/images/logo.png -------------------------------------------------------------------------------- /extras/images/screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocoutinhox/db-talk-ai/21e7f6380340461a81cb8dd1cbae88652b31f6a5/extras/images/screenshot-2.png -------------------------------------------------------------------------------- /extras/images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocoutinhox/db-talk-ai/21e7f6380340461a81cb8dd1cbae88652b31f6a5/extras/images/screenshot.png -------------------------------------------------------------------------------- /helpers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocoutinhox/db-talk-ai/21e7f6380340461a81cb8dd1cbae88652b31f6a5/helpers/__init__.py -------------------------------------------------------------------------------- /helpers/chart.py: -------------------------------------------------------------------------------- 1 | from charts.bar_chart import BarChart 2 | from charts.heatmap_chart import HeatmapChart 3 | from charts.line_chart import LineChart 4 | from charts.map_chart import MapChart 5 | from charts.pie_chart import PieChart 6 | 7 | 8 | def load_charts(): 9 | # List of charts 10 | chart_classes = [ 11 | BarChart, 12 | LineChart, 13 | PieChart, 14 | MapChart, 15 | HeatmapChart, 16 | ] 17 | 18 | # Sort charts alphabetically by name 19 | sorted_chart_classes = sorted(chart_classes, key=lambda c: c.name) 20 | 21 | return sorted_chart_classes 22 | -------------------------------------------------------------------------------- /helpers/db.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import uuid 4 | 5 | from db.sql_database import SQLDatabase 6 | from helpers import file 7 | 8 | 9 | def load_databases(): 10 | """Load database configurations from a JSON file.""" 11 | file_path = file.get_databases_file() 12 | 13 | with open(file_path, "r", encoding="utf-8") as f: 14 | return json.load(f) 15 | 16 | 17 | def save_databases(databases): 18 | """Save the database configurations to a JSON file.""" 19 | file_path = file.get_databases_file() 20 | 21 | with open(file_path, "w", encoding="utf-8") as f: 22 | json.dump(databases, f, indent=4) 23 | 24 | 25 | def create_database(db_type, db_url): 26 | """Create a database connection based on the provided type.""" 27 | if db_type == "sql": 28 | return SQLDatabase(db_url) 29 | else: 30 | raise ValueError(f"❌ Unsupported database type: {db_type}") 31 | 32 | 33 | def generate_schema(selected_db, databases, db_conn): 34 | """Generate and save the database schema, updating the configuration if successful.""" 35 | schema_dir = file.get_schemas_folder() 36 | os.makedirs(schema_dir, exist_ok=True) 37 | 38 | schema_file = selected_db.get("schema", f"{uuid.uuid4()}.txt") 39 | schema_path = os.path.join(schema_dir, schema_file) 40 | 41 | # Generate the schema using the database connection 42 | schema_message = db_conn.generate_schema(schema_path) 43 | 44 | if "✅" in schema_message: 45 | # Update schema key 46 | selected_db["schema"] = schema_file 47 | save_databases(databases) 48 | 49 | return True, schema_message 50 | else: 51 | return False, schema_message 52 | -------------------------------------------------------------------------------- /helpers/file.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | def get_root_folder(): 5 | """Get the root folder path.""" 6 | 7 | return os.getenv( 8 | "DB_TALK_AI_ROOT", 9 | os.path.dirname( 10 | os.path.abspath(os.path.join(__file__, "..")), 11 | ), 12 | ) 13 | 14 | 15 | def get_data_folder(): 16 | """Get the data folder path.""" 17 | return os.path.join(get_root_folder(), "data") 18 | 19 | 20 | def get_schemas_folder(): 21 | """Get the schemas folder path.""" 22 | return os.path.join(get_data_folder(), "schemas") 23 | 24 | 25 | def get_models_folder(): 26 | """Get the models folder path.""" 27 | return os.path.join(get_data_folder(), "models") 28 | 29 | 30 | def get_config_folder(): 31 | """Get the config folder path.""" 32 | return os.path.join(get_data_folder(), "config") 33 | 34 | 35 | def get_schema_file(name): 36 | """Get the schema file path for a given name.""" 37 | return os.path.join(get_schemas_folder(), name) 38 | 39 | 40 | def get_databases_file(): 41 | """Get the databases file path.""" 42 | return os.path.join(get_config_folder(), "databases.json") 43 | 44 | 45 | def get_models_file(): 46 | """Get the models file path.""" 47 | return os.path.join(get_config_folder(), "models.json") 48 | -------------------------------------------------------------------------------- /helpers/model.py: -------------------------------------------------------------------------------- 1 | from models.anthropic_model import AnthropicModel 2 | from models.deep_seek_model import DeepSeekModel 3 | from models.gemini_model import GeminiModel 4 | from models.grok_model import GrokModel 5 | from models.local_gguf_model import LocalGGUFModel 6 | from models.local_model import LocalModel 7 | from models.openai_model import OpenAIModel 8 | 9 | 10 | def load_models(): 11 | # Add fixed models 12 | models = [] 13 | models.append(OpenAIModel()) 14 | models.append(DeepSeekModel()) 15 | models.append(GrokModel()) 16 | models.append(GeminiModel()) 17 | models.append(AnthropicModel()) 18 | 19 | # Add local models 20 | local_model = LocalModel() 21 | 22 | if local_model.get_variants(): 23 | models.append(local_model) 24 | 25 | # Add local gguf models 26 | gguf_models = LocalGGUFModel() 27 | 28 | if gguf_models.get_variants(): 29 | models.append(gguf_models) 30 | 31 | return models 32 | -------------------------------------------------------------------------------- /helpers/prompt.py: -------------------------------------------------------------------------------- 1 | from helpers import string 2 | 3 | 4 | def build(db_driver, schema_info, user_prompt, chart_prompt): 5 | system_prompt = f""" 6 | Given the following {db_driver} database schema: 7 | 8 | {schema_info} 9 | 10 | ### Query Rules: 11 | - Use **only** tables and columns that exist in the schema. 12 | - **Do not invent** table names, column names, or values. 13 | - If the user refers to a table or column that **does not exist**, find the most relevant column based on its **meaning**. 14 | - If no relevant column exists, respond with: "Error: The requested table or column does not exist." 15 | - The output must contain **only** the SQL query, with no explanations, formatting, or additional text. 16 | - The query **must** start directly with 'SELECT'. 17 | - Dates should be formatted as 'YYYY-MM-DD'. 18 | - **Absolutely do not use markdown (` ```sql `) or any code blocks.** 19 | - **Do not wrap the query with triple backticks (` ``` `) or any other formatting characters.** 20 | - The output must be **plain text** containing only the SQL query. 21 | 22 | ### Optimization Rules: 23 | - Ensure queries are optimized for performance, using indexes where applicable. 24 | - Use `JOIN` only if necessary, avoiding unnecessary joins. 25 | - If filtering by date, ensure an indexed column is used whenever possible. 26 | """ 27 | 28 | # Add chart-specific rules if provided 29 | if chart_prompt: 30 | system_prompt += f""" 31 | ### Chart-Specific Instructions: 32 | {chart_prompt} 33 | """ 34 | 35 | system_prompt += """ 36 | Generate the SQL query for the following request **without any extra formatting**: 37 | """ 38 | 39 | # Clean formatting 40 | clean_prompt = string.clean_multiline(system_prompt) 41 | 42 | # Final prompt structure 43 | prompt = [ 44 | { 45 | "role": "system", 46 | "content": f"You are an advanced SQL assistant specialized in {db_driver} databases.", 47 | }, 48 | { 49 | "role": "user", 50 | "content": f"{clean_prompt}\n\n'{user_prompt}'", 51 | }, 52 | ] 53 | 54 | return prompt 55 | -------------------------------------------------------------------------------- /helpers/response.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | 4 | def clean(text: str) -> str: 5 | """ 6 | Removes markdown-style code formatting, keeping the sql content. 7 | """ 8 | # remove code block delimiters (```sql ... ``` or ``` ... ```) 9 | text = re.sub(r"```(?:\w+)?\n([\s\S]*?)\n```", r"\1", text, flags=re.MULTILINE) 10 | 11 | # remove inline code formatting (`...`) 12 | text = re.sub(r"`([^`]+)`", r"\1", text) 13 | 14 | return text.strip() 15 | -------------------------------------------------------------------------------- /helpers/schema.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from helpers import file 4 | 5 | 6 | def load_schema(schema_file): 7 | if schema_file: 8 | schema_path = file.get_schema_file(schema_file) 9 | 10 | if os.path.exists(schema_path): 11 | with open(schema_path, "r", encoding="utf-8") as f: 12 | return f.read().strip(), None 13 | else: 14 | return "", "⚠️ Schema file not found. Generate it first." 15 | else: 16 | return "", "⚠️ No schema generated yet." 17 | -------------------------------------------------------------------------------- /helpers/string.py: -------------------------------------------------------------------------------- 1 | import textwrap 2 | 3 | 4 | def clean_multiline(text): 5 | return textwrap.dedent(text).strip() 6 | -------------------------------------------------------------------------------- /models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocoutinhox/db-talk-ai/21e7f6380340461a81cb8dd1cbae88652b31f6a5/models/__init__.py -------------------------------------------------------------------------------- /models/anthropic_model.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from .base_model import BaseAIModel 4 | 5 | 6 | class AnthropicModel(BaseAIModel): 7 | def __init__(self): 8 | super().__init__() 9 | self.client = None 10 | self.api_key = os.getenv("ANTHROPIC_API_KEY") 11 | self.default_variant = "claude-3-7-sonnet-latest" 12 | 13 | def load(self): 14 | if not self.api_key: 15 | raise ValueError("The environment variable ANTHROPIC_API_KEY is missing.") 16 | 17 | try: 18 | import anthropic 19 | except ImportError: 20 | raise ImportError( 21 | "The 'anthropic' library is not installed. Please install it using 'pip install anthropic'." 22 | ) 23 | 24 | self.client = anthropic.Anthropic(api_key=self.api_key) 25 | 26 | def run(self, messages, variant=None): 27 | if self.client is None: 28 | self.load() 29 | 30 | model_variant = variant or self.default_variant 31 | prepared_input = self.prepare_messages(messages, model_variant) 32 | 33 | # Separate system prompt from user/assistant messages 34 | system_prompt = "" 35 | filtered_messages = [] 36 | 37 | for message in prepared_input: 38 | if message.get("role") == "system": 39 | system_prompt = message.get("content") 40 | else: 41 | filtered_messages.append(message) 42 | 43 | # Make the request 44 | response = self.client.messages.create( 45 | model=model_variant, 46 | max_tokens=4096, 47 | system=system_prompt or None, 48 | messages=filtered_messages, 49 | ) 50 | 51 | return response.content[0].text.strip() 52 | 53 | def name(self): 54 | return "Anthropic" 55 | 56 | def get_variants(self): 57 | return { 58 | "claude-3-7-sonnet-latest": "Claude 3.7 Sonnet (200k context)", 59 | "claude-3-5-sonnet-latest": "Claude 3.5 Sonnet (200k context)", 60 | } 61 | -------------------------------------------------------------------------------- /models/base_model.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | 4 | class BaseAIModel(ABC): 5 | def __init__(self): 6 | self.default_variant = None 7 | self.unsupported_role_models = [] 8 | self.force_string_prompt = False 9 | 10 | @abstractmethod 11 | def load(self): 12 | """ 13 | Load the AI model and any necessary resources. 14 | 15 | Raises: 16 | Exception: If the model cannot be loaded due to missing dependencies 17 | or invalid configurations. 18 | """ 19 | pass 20 | 21 | @abstractmethod 22 | def run(self, messages, variant=None): 23 | """ 24 | Run the AI model with the provided input messages and return the response. 25 | 26 | Args: 27 | messages (list): A list of message dictionaries with at least two keys: 28 | - "role": The role of the sender (e.g., "user", "assistant"). 29 | - "content": The actual text message to process. 30 | variant (str, optional): An internal model identifier specifying the variant to use. 31 | Defaults to None. 32 | 33 | Returns: 34 | str: The response generated by the AI model, as a string. 35 | 36 | Raises: 37 | Exception: If there is an issue during model execution or inference. 38 | """ 39 | pass 40 | 41 | @abstractmethod 42 | def name(self): 43 | """ 44 | Get the name of the AI model for display purposes. 45 | 46 | Returns: 47 | str: A user-friendly name of the AI model, e.g., "OpenAI GPT-4". 48 | """ 49 | pass 50 | 51 | def get_variants(self): 52 | """ 53 | Returns a dictionary of model variants. 54 | 55 | Returns: 56 | dict or None: A dictionary where: 57 | - Key (str): Internal model identifier used in API calls. 58 | - Value (str): User-friendly name for display in the UI. 59 | 60 | If no variants are available, returns None. 61 | """ 62 | return None 63 | 64 | def get_default_variant(self): 65 | """ 66 | Returns the default variant for this model. 67 | 68 | Returns: 69 | str: The internal identifier of the default variant. 70 | """ 71 | return self.default_variant 72 | 73 | def prepare_messages(self, messages, variant): 74 | """ 75 | Prepares messages for models that don't support role-based inputs 76 | or for models that always use a string prompt. 77 | 78 | Args: 79 | messages (list): List of message dictionaries with 'role' and 'content' keys. 80 | variant (str): The model variant to check against unsupported roles. 81 | 82 | Returns: 83 | str or list: A plain-text prompt (str) if roles are unsupported or if force_string_prompt is True. 84 | Otherwise, returns the original list of messages. 85 | """ 86 | if self.force_string_prompt or variant in self.unsupported_role_models: 87 | return "\n".join( 88 | f"{msg.get('role', 'user').capitalize()}: {msg['content']}" 89 | for msg in messages 90 | ) 91 | 92 | return messages 93 | -------------------------------------------------------------------------------- /models/deep_seek_model.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from .base_model import BaseAIModel 4 | 5 | 6 | class DeepSeekModel(BaseAIModel): 7 | def __init__(self): 8 | super().__init__() 9 | self.client = None 10 | self.api_key = os.getenv("DEEPSEEK_API_KEY") 11 | self.default_variant = "deepseek-chat" 12 | 13 | def load(self): 14 | if not self.api_key: 15 | raise ValueError("The environment variable DEEPSEEK_API_KEY is missing.") 16 | 17 | try: 18 | from openai import OpenAI 19 | except ImportError: 20 | raise ImportError( 21 | "The 'openai' library is not installed. Please install it using 'pip install openai'." 22 | ) 23 | 24 | self.client = OpenAI(api_key=self.api_key, base_url="https://api.deepseek.com") 25 | 26 | def run(self, messages, variant=None): 27 | if self.client is None: 28 | self.load() 29 | 30 | model_variant = variant or self.default_variant 31 | prepared_input = self.prepare_messages(messages, model_variant) 32 | 33 | response = self.client.chat.completions.create( 34 | model=model_variant, 35 | messages=prepared_input, 36 | temperature=0.2, 37 | ) 38 | 39 | return response.choices[0].message.content.strip() 40 | 41 | def name(self): 42 | return "DeepSeek" 43 | 44 | def get_variants(self): 45 | return { 46 | "deepseek-chat": "DeepSeek Chat (64k context)", 47 | "deepseek-reasoner": "DeepSeek Reasoner (64k context)", 48 | } 49 | -------------------------------------------------------------------------------- /models/gemini_model.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from .base_model import BaseAIModel 4 | 5 | 6 | class GeminiModel(BaseAIModel): 7 | def __init__(self): 8 | super().__init__() 9 | self.client = None 10 | self.api_key = os.getenv("GEMINI_API_KEY") 11 | self.default_variant = "gemini-2.0-flash" 12 | 13 | def load(self): 14 | if not self.api_key: 15 | raise ValueError("The environment variable GEMINI_API_KEY is missing.") 16 | 17 | try: 18 | from openai import OpenAI 19 | except ImportError: 20 | raise ImportError( 21 | "The 'openai' library is not installed. Please install it using 'pip install openai'." 22 | ) 23 | 24 | self.client = OpenAI( 25 | api_key=self.api_key, 26 | base_url="https://generativelanguage.googleapis.com/v1beta/openai/", 27 | ) 28 | 29 | def run(self, messages, variant=None): 30 | if self.client is None: 31 | self.load() 32 | 33 | model_variant = variant or self.default_variant 34 | prepared_input = self.prepare_messages(messages, model_variant) 35 | 36 | response = self.client.chat.completions.create( 37 | model=model_variant, 38 | messages=prepared_input, 39 | temperature=0.2, 40 | ) 41 | 42 | return response.choices[0].message.content.strip() 43 | 44 | def name(self): 45 | return "Gemini" 46 | 47 | def get_variants(self): 48 | return { 49 | "gemini-2.0-flash": "Gemini 2 Flash (1m context)", 50 | } 51 | -------------------------------------------------------------------------------- /models/grok_model.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from .base_model import BaseAIModel 4 | 5 | 6 | class GrokModel(BaseAIModel): 7 | def __init__(self): 8 | super().__init__() 9 | self.client = None 10 | self.api_key = os.getenv("XAI_API_KEY") 11 | self.default_variant = "grok-2-latest" 12 | 13 | def load(self): 14 | if not self.api_key: 15 | raise ValueError("The environment variable XAI_API_KEY is missing.") 16 | 17 | try: 18 | from openai import OpenAI 19 | except ImportError: 20 | raise ImportError( 21 | "The 'openai' library is not installed. Please install it using 'pip install openai'." 22 | ) 23 | 24 | self.client = OpenAI(api_key=self.api_key, base_url="https://api.x.ai/v1") 25 | 26 | def run(self, messages, variant=None): 27 | if self.client is None: 28 | self.load() 29 | 30 | model_variant = variant or self.default_variant 31 | prepared_input = self.prepare_messages(messages, model_variant) 32 | 33 | response = self.client.chat.completions.create( 34 | model=model_variant, 35 | messages=prepared_input, 36 | temperature=0.2, 37 | ) 38 | 39 | return response.choices[0].message.content.strip() 40 | 41 | def name(self): 42 | return "Grok" 43 | 44 | def get_variants(self): 45 | return { 46 | "grok-2-latest": "Grok 2 (128k context)", 47 | } 48 | -------------------------------------------------------------------------------- /models/local_gguf_model.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from helpers import file 4 | 5 | from .base_model import BaseAIModel 6 | 7 | 8 | class LocalGGUFModel(BaseAIModel): 9 | available_models = {} 10 | 11 | def __init__(self): 12 | super().__init__() 13 | self.llm = None 14 | self.force_string_prompt = True 15 | 16 | def load(self, model_config): 17 | """ 18 | Load a GGUF model using the specified configuration. 19 | 20 | Args: 21 | model_config (dict): Model configuration including the path. 22 | """ 23 | try: 24 | from gpt4all import GPT4All 25 | except ImportError: 26 | raise ImportError( 27 | "The 'gpt4all' library is not installed. Please install it using 'pip install gpt4all' before running this feature." 28 | ) 29 | 30 | self.llm = GPT4All(model_name=model_config["path"]) 31 | 32 | def run(self, messages, variant=None): 33 | if variant is None: 34 | raise ValueError("A valid model variant (ID) must be provided.") 35 | 36 | model_config = self.available_models.get(variant) 37 | if not model_config: 38 | raise ValueError(f"Model variant '{variant}' not found.") 39 | 40 | if self.llm is None: 41 | self.load(model_config) 42 | 43 | model_variant = variant or self.default_variant 44 | prepared_input = self.prepare_messages(messages, model_variant) 45 | 46 | response = self.llm.generate(prepared_input) 47 | 48 | return response.strip() 49 | 50 | def name(self): 51 | return "Local GGUF" 52 | 53 | def get_variants(self): 54 | if not self.available_models: 55 | self.load_models_from_directory() 56 | 57 | return { 58 | model_path: config["name"] 59 | for model_path, config in self.available_models.items() 60 | } 61 | 62 | @classmethod 63 | def load_models_from_directory(cls): 64 | """ 65 | Scans the models directory for GGUF files and loads their configurations. 66 | 67 | Returns: 68 | dict: A dictionary of available GGUF models. 69 | """ 70 | models_dir = file.get_models_folder() 71 | 72 | if not os.path.exists(models_dir): 73 | return {} 74 | 75 | cls.available_models = {} 76 | 77 | for f in os.listdir(models_dir): 78 | if f.endswith(".gguf"): 79 | model_path = os.path.join(models_dir, f) 80 | model_name = os.path.splitext(f)[0] 81 | 82 | cls.available_models[model_path] = { 83 | "name": model_name, 84 | "path": model_path, 85 | } 86 | 87 | return cls.available_models 88 | -------------------------------------------------------------------------------- /models/local_model.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | 4 | from helpers import file 5 | 6 | from .base_model import BaseAIModel 7 | 8 | 9 | class LocalModel(BaseAIModel): 10 | available_models = {} 11 | 12 | def __init__(self): 13 | super().__init__() 14 | self.client = None 15 | 16 | def load(self, model_config): 17 | """ 18 | Load the model using its configuration. 19 | 20 | Args: 21 | model_config (dict): The configuration of the model including path, torch_dtype, and max_new_tokens. 22 | """ 23 | try: 24 | from transformers import pipeline 25 | except ImportError: 26 | raise ImportError( 27 | "The 'transformers' and 'torch' libraries are not installed. Please install them using 'pip install transformers torch' before running this feature." 28 | ) 29 | 30 | self.client = pipeline( 31 | "text-generation", 32 | model=model_config["path"], 33 | torch_dtype=model_config.get("torch_dtype", "auto"), 34 | device_map="auto", 35 | ) 36 | 37 | def run(self, messages, variant=None): 38 | if variant is None: 39 | raise ValueError("A valid model variant (ID) must be provided.") 40 | 41 | model_config = self.available_models.get(variant) 42 | if not model_config: 43 | raise ValueError(f"Model variant '{variant}' not found.") 44 | 45 | if self.client is None: 46 | self.load(model_config) 47 | 48 | outputs = self.client( 49 | messages, 50 | max_new_tokens=model_config.get("max_new_tokens", 2048), 51 | return_full_text=False, 52 | ) 53 | 54 | if isinstance(outputs, list) and "generated_text" in outputs[0]: 55 | return str(outputs[0]["generated_text"]) 56 | else: 57 | raise ValueError("Unexpected output format from the model pipeline.") 58 | 59 | def name(self): 60 | return "Local" 61 | 62 | def get_variants(self): 63 | if not self.available_models: 64 | self.load_models_from_config() 65 | 66 | return { 67 | model_id: config["name"] 68 | for model_id, config in self.available_models.items() 69 | } 70 | 71 | @classmethod 72 | def load_models_from_config(self): 73 | """ 74 | Loads models from a JSON configuration file and stores them in available_models. 75 | 76 | Returns: 77 | dict: A dictionary of available model configurations. 78 | """ 79 | models_file = file.get_models_file() 80 | 81 | if not os.path.exists(models_file): 82 | return {} 83 | 84 | with open(models_file, "r", encoding="utf-8") as f: 85 | model_configs = json.load(f) 86 | 87 | self.available_models = {} 88 | 89 | for model_config in model_configs: 90 | model_name = model_config.get("name") 91 | model_path = model_config.get("path") 92 | torch_dtype = model_config.get("torch_dtype", "auto") 93 | max_new_tokens = model_config.get("max_new_tokens", 4096) 94 | 95 | if not model_name or not model_path: 96 | raise ValueError("Each model entry must have 'name' and 'path' fields.") 97 | 98 | self.available_models[model_path] = { 99 | "name": model_name, 100 | "path": model_path, 101 | "torch_dtype": torch_dtype, 102 | "max_new_tokens": max_new_tokens, 103 | } 104 | 105 | return self.available_models 106 | -------------------------------------------------------------------------------- /models/openai_model.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from .base_model import BaseAIModel 4 | 5 | 6 | class OpenAIModel(BaseAIModel): 7 | def __init__(self): 8 | super().__init__() 9 | self.client = None 10 | self.api_key = os.getenv("OPENAI_API_KEY") 11 | self.default_variant = "gpt-4o-mini" 12 | 13 | def load(self): 14 | if not self.api_key: 15 | raise ValueError("The environment variable OPENAI_API_KEY is missing.") 16 | 17 | try: 18 | import openai 19 | except ImportError: 20 | raise ImportError( 21 | "The 'openai' library is not installed. Please install it using 'pip install openai'." 22 | ) 23 | 24 | self.client = openai.OpenAI(api_key=self.api_key) 25 | 26 | def run(self, messages, variant=None): 27 | if self.client is None: 28 | self.load() 29 | 30 | model_variant = variant or self.default_variant 31 | 32 | # Prepare messages according to the model's role support 33 | prepared_input = self.prepare_messages(messages, model_variant) 34 | 35 | if isinstance(prepared_input, str): 36 | # Handle plain-text input for models without role support 37 | response = self.client.completions.create( 38 | model=model_variant, 39 | prompt=prepared_input, 40 | temperature=0.2, 41 | ) 42 | 43 | return response.choices[0].text.strip() 44 | elif isinstance(prepared_input, list): 45 | # Standard Chat Completions API with role-based messages 46 | response = self.client.chat.completions.create( 47 | model=model_variant, 48 | messages=prepared_input, 49 | temperature=0.2, 50 | ) 51 | 52 | return response.choices[0].message.content.strip() 53 | 54 | else: 55 | raise ValueError( 56 | f"Invalid input format for model variant '{model_variant}'." 57 | ) 58 | 59 | def name(self): 60 | return "OpenAI" 61 | 62 | def get_variants(self): 63 | return { 64 | "gpt-4o": "GPT-4o (128k context)", 65 | "chatgpt-4o-latest": "ChatGPT-4o (128k context)", 66 | "gpt-4o-mini": "GPT-4o Mini (128k context)", 67 | "gpt-4-turbo": "GPT-4 Turbo (128k context)", 68 | "gpt-4": "GPT-4 (8k context)", 69 | } 70 | -------------------------------------------------------------------------------- /prompts.txt: -------------------------------------------------------------------------------- 1 | - Extract the domain name from a customer's email address, specifically the part after the '@' symbol. 2 | - What is the sum and the name of each of the track genres order by biggest? 3 | - What was the total purchased in 2012? 4 | - What is the sum and the name of each country have sells order by first five biggest? 5 | - What was the total purchased separated by year? 6 | - What is the first 100 cities around "new york" city for 150km using latitude and longitude? 7 | -------------------------------------------------------------------------------- /requirements-gguf.txt: -------------------------------------------------------------------------------- 1 | gpt4all 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | streamlit 2 | plotly 3 | pandas 4 | sqlalchemy 5 | openai 6 | transformers>=4.45.0 7 | torch 8 | accelerate>=0.26.0 9 | anthropic 10 | --------------------------------------------------------------------------------