Spaces:
Sleeping
Sleeping
Add Gradio and Streamlit chat interfaces for Sales Assistant
Browse files- Created `run_ui.sh` script to launch the UI applications.
- Added README documentation for the Sales Assistant Chat UI, detailing available interfaces, quick start instructions, example queries, features, technical details, prerequisites, troubleshooting, and development tips.
- Implemented Gradio chat interface in `gradio_app.py` with features for product search, quote generation, and data analysis.
- Developed Streamlit chat interface in `streamlit_app.py` with similar functionalities and enhanced user interaction features.
- .gitignore +4 -1
- pyproject.toml +2 -1
- src/sales_assistant/agent_main/agent_node.py +1 -2
- src/sales_assistant/ui_dashboard/README.md +130 -0
- src/sales_assistant/ui_dashboard/gradio_app.py +216 -0
- src/sales_assistant/ui_dashboard/streamlit_app.py +273 -0
- uv.lock +0 -0
.gitignore
CHANGED
|
@@ -91,4 +91,7 @@ Thumbs.db
|
|
| 91 |
|
| 92 |
# Temporary files
|
| 93 |
*.tmp
|
| 94 |
-
*.temp
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
|
| 92 |
# Temporary files
|
| 93 |
*.tmp
|
| 94 |
+
*.temp
|
| 95 |
+
|
| 96 |
+
# Quotes
|
| 97 |
+
src/sales_assistant/created_quotes/*
|
pyproject.toml
CHANGED
|
@@ -17,11 +17,12 @@ dependencies = [
|
|
| 17 |
"langchain>=0.3.27",
|
| 18 |
"langgraph>=0.6.5",
|
| 19 |
"langchain-openai>=0.3.30",
|
| 20 |
-
"langchain-nvidia-ai-endpoints>=0.3.0",
|
| 21 |
"langsmith>=0.2.13",
|
| 22 |
"pydantic>=2.11.7",
|
| 23 |
"huggingface-hub>=0.34.4",
|
| 24 |
"langchain-huggingface>=0.3.1",
|
|
|
|
|
|
|
| 25 |
]
|
| 26 |
|
| 27 |
[build-system]
|
|
|
|
| 17 |
"langchain>=0.3.27",
|
| 18 |
"langgraph>=0.6.5",
|
| 19 |
"langchain-openai>=0.3.30",
|
|
|
|
| 20 |
"langsmith>=0.2.13",
|
| 21 |
"pydantic>=2.11.7",
|
| 22 |
"huggingface-hub>=0.34.4",
|
| 23 |
"langchain-huggingface>=0.3.1",
|
| 24 |
+
"gradio>=5.43.1",
|
| 25 |
+
"streamlit>=1.48.1",
|
| 26 |
]
|
| 27 |
|
| 28 |
[build-system]
|
src/sales_assistant/agent_main/agent_node.py
CHANGED
|
@@ -9,6 +9,7 @@ from langgraph.graph import MessagesState
|
|
| 9 |
from langchain_openai import ChatOpenAI
|
| 10 |
from dotenv import load_dotenv
|
| 11 |
from ..prompts.system_prompt import SYSTEM_PROMPT
|
|
|
|
| 12 |
|
| 13 |
# Load environment variables
|
| 14 |
load_dotenv()
|
|
@@ -51,8 +52,6 @@ def agent_node(state: MessagesState) -> Dict[str, Any]:
|
|
| 51 |
api_key=os.getenv("OPENAI_API_KEY"),
|
| 52 |
)
|
| 53 |
|
| 54 |
-
# Bind tools to the model
|
| 55 |
-
from .tools_node import get_all_tools
|
| 56 |
tools = get_all_tools()
|
| 57 |
model_with_tools = model.bind_tools(tools)
|
| 58 |
|
|
|
|
| 9 |
from langchain_openai import ChatOpenAI
|
| 10 |
from dotenv import load_dotenv
|
| 11 |
from ..prompts.system_prompt import SYSTEM_PROMPT
|
| 12 |
+
from .tools_node import get_all_tools
|
| 13 |
|
| 14 |
# Load environment variables
|
| 15 |
load_dotenv()
|
|
|
|
| 52 |
api_key=os.getenv("OPENAI_API_KEY"),
|
| 53 |
)
|
| 54 |
|
|
|
|
|
|
|
| 55 |
tools = get_all_tools()
|
| 56 |
model_with_tools = model.bind_tools(tools)
|
| 57 |
|
src/sales_assistant/ui_dashboard/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Sales Assistant Chat UI
|
| 2 |
+
|
| 3 |
+
This directory contains two different chat interfaces for the Sales Assistant:
|
| 4 |
+
|
| 5 |
+
## π¨ Available Interfaces
|
| 6 |
+
|
| 7 |
+
### 1. Gradio Chat Interface (`gradio_app.py`)
|
| 8 |
+
- **URL**: http://localhost:7860
|
| 9 |
+
- **Features**:
|
| 10 |
+
- Simple, clean chat interface
|
| 11 |
+
- Example queries sidebar
|
| 12 |
+
- Real-time responses
|
| 13 |
+
- Mobile-friendly design
|
| 14 |
+
- **Best for**: Quick testing and development
|
| 15 |
+
|
| 16 |
+
### 2. Streamlit Chat Interface (`streamlit_app.py`)
|
| 17 |
+
- **URL**: http://localhost:8501
|
| 18 |
+
- **Features**:
|
| 19 |
+
- Rich sidebar with capabilities overview
|
| 20 |
+
- Chat statistics
|
| 21 |
+
- Agent status monitoring
|
| 22 |
+
- Professional dashboard layout
|
| 23 |
+
- **Best for**: Demos and production use
|
| 24 |
+
|
| 25 |
+
## π Quick Start
|
| 26 |
+
|
| 27 |
+
### Option 1: Use the Launcher Script
|
| 28 |
+
```bash
|
| 29 |
+
# From the project root
|
| 30 |
+
./run_ui.sh
|
| 31 |
+
```
|
| 32 |
+
|
| 33 |
+
### Option 2: Run Individual Apps
|
| 34 |
+
|
| 35 |
+
**Gradio:**
|
| 36 |
+
```bash
|
| 37 |
+
uv run python src/sales_assistant/ui_dashboard/gradio_app.py
|
| 38 |
+
```
|
| 39 |
+
|
| 40 |
+
**Streamlit:**
|
| 41 |
+
```bash
|
| 42 |
+
uv run streamlit run src/sales_assistant/ui_dashboard/streamlit_app.py --server.port 8501
|
| 43 |
+
```
|
| 44 |
+
|
| 45 |
+
### Option 3: Run Both Simultaneously
|
| 46 |
+
```bash
|
| 47 |
+
# Terminal 1 - Gradio
|
| 48 |
+
uv run python src/sales_assistant/ui_dashboard/gradio_app.py
|
| 49 |
+
|
| 50 |
+
# Terminal 2 - Streamlit
|
| 51 |
+
uv run streamlit run src/sales_assistant/ui_dashboard/streamlit_app.py --server.port 8501
|
| 52 |
+
```
|
| 53 |
+
|
| 54 |
+
## π‘ Example Queries
|
| 55 |
+
|
| 56 |
+
Try these sample queries in either interface:
|
| 57 |
+
|
| 58 |
+
- `"Show me information about our products"`
|
| 59 |
+
- `"I need a quote for industrial equipment"`
|
| 60 |
+
- `"What are the current exchange rates?"`
|
| 61 |
+
- `"Find products with price under $1000"`
|
| 62 |
+
- `"Generate a quote for customer 'Tech Solutions Inc'"`
|
| 63 |
+
- `"Show me Samsung outdoor TVs"`
|
| 64 |
+
|
| 65 |
+
## π§ Features
|
| 66 |
+
|
| 67 |
+
Both interfaces provide access to:
|
| 68 |
+
|
| 69 |
+
- **Product Database Search**: Explore the complete product catalog
|
| 70 |
+
- **Quote Generation**: Create professional quotes with current pricing
|
| 71 |
+
- **Data Analysis**: Get statistical insights about products
|
| 72 |
+
- **Exchange Rates**: Current currency conversion information
|
| 73 |
+
- **Natural Language Processing**: Ask questions in plain English
|
| 74 |
+
|
| 75 |
+
## π Technical Details
|
| 76 |
+
|
| 77 |
+
- **Backend**: LangGraph-based conversational agent
|
| 78 |
+
- **Database**: MySQL with SSH tunnel connection
|
| 79 |
+
- **AI Models**: OpenAI GPT models with tool calling
|
| 80 |
+
- **State Management**: Persistent conversation history
|
| 81 |
+
- **Tracing**: Optional LangSmith integration for debugging
|
| 82 |
+
|
| 83 |
+
## π Prerequisites
|
| 84 |
+
|
| 85 |
+
Make sure you have:
|
| 86 |
+
|
| 87 |
+
1. **Environment Variables** set in `.env`:
|
| 88 |
+
- `OPENAI_API_KEY`
|
| 89 |
+
- `MODEL_PROVIDER=openai`
|
| 90 |
+
- `MODEL_NAME=gpt-4`
|
| 91 |
+
- `MYSQL_HOST`, `MYSQL_USER`, `MYSQL_PASSWORD`, `MYSQL_DB`
|
| 92 |
+
- Optional: `LANGSMITH_API_KEY`, `LANGSMITH_PROJECT`
|
| 93 |
+
|
| 94 |
+
2. **Dependencies** installed:
|
| 95 |
+
```bash
|
| 96 |
+
uv add gradio streamlit
|
| 97 |
+
```
|
| 98 |
+
|
| 99 |
+
3. **Database Access**: Ensure your MySQL database is accessible and the SSH tunnel is configured if needed.
|
| 100 |
+
|
| 101 |
+
## π Troubleshooting
|
| 102 |
+
|
| 103 |
+
### Common Issues:
|
| 104 |
+
|
| 105 |
+
1. **Agent Initialization Failed**
|
| 106 |
+
- Check your `.env` file configuration
|
| 107 |
+
- Verify database connectivity
|
| 108 |
+
- Ensure all required environment variables are set
|
| 109 |
+
|
| 110 |
+
2. **Port Already in Use**
|
| 111 |
+
- Change ports in the launch commands
|
| 112 |
+
- Kill existing processes: `pkill -f "gradio_app\|streamlit"`
|
| 113 |
+
|
| 114 |
+
3. **Database Connection Issues**
|
| 115 |
+
- Verify SSH tunnel configuration
|
| 116 |
+
- Check database credentials
|
| 117 |
+
- Ensure the database server is running
|
| 118 |
+
|
| 119 |
+
### Logs and Debugging:
|
| 120 |
+
|
| 121 |
+
- Both apps print initialization status to the console
|
| 122 |
+
- Database queries are logged with SQL statements
|
| 123 |
+
- LangSmith tracing can be enabled for detailed debugging
|
| 124 |
+
|
| 125 |
+
## π― Development Tips
|
| 126 |
+
|
| 127 |
+
- **Hot Reload**: Streamlit supports hot reload for development
|
| 128 |
+
- **Custom Styling**: Both apps support custom CSS/themes
|
| 129 |
+
- **Configuration**: Modify the `ConversationConfig` for different settings
|
| 130 |
+
- **Extensions**: Easy to add new features through the agent tools system
|
src/sales_assistant/ui_dashboard/gradio_app.py
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Gradio Chat UI for Sales Assistant
|
| 3 |
+
|
| 4 |
+
This module provides a simple Gradio-based chat interface for the sales assistant.
|
| 5 |
+
"""
|
| 6 |
+
import os
|
| 7 |
+
import sys
|
| 8 |
+
import gradio as gr
|
| 9 |
+
from typing import List, Tuple, Dict
|
| 10 |
+
|
| 11 |
+
# Add the src directory to the path
|
| 12 |
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../..'))
|
| 13 |
+
|
| 14 |
+
from sales_assistant.main import (
|
| 15 |
+
check_environment_setup,
|
| 16 |
+
create_custom_config
|
| 17 |
+
)
|
| 18 |
+
from sales_assistant.agent_main.agent_runner import (
|
| 19 |
+
create_agent_runner,
|
| 20 |
+
run_conversation_turn
|
| 21 |
+
)
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class SalesAssistantChat:
|
| 25 |
+
"""Chat interface for the sales assistant."""
|
| 26 |
+
|
| 27 |
+
def __init__(self):
|
| 28 |
+
"""Initialize the chat interface."""
|
| 29 |
+
self.compiled_graph = None
|
| 30 |
+
self.checkpointer = None
|
| 31 |
+
self.langsmith_client = None
|
| 32 |
+
self.thread_id = None
|
| 33 |
+
self.initialize_agent()
|
| 34 |
+
|
| 35 |
+
def initialize_agent(self):
|
| 36 |
+
"""Initialize the agent runner."""
|
| 37 |
+
try:
|
| 38 |
+
# Check environment setup
|
| 39 |
+
if not check_environment_setup():
|
| 40 |
+
print("β Environment setup failed")
|
| 41 |
+
return
|
| 42 |
+
|
| 43 |
+
# Create configuration
|
| 44 |
+
config = create_custom_config(
|
| 45 |
+
session_id="gradio_session",
|
| 46 |
+
user_id="gradio_user",
|
| 47 |
+
enable_langsmith=True
|
| 48 |
+
)
|
| 49 |
+
|
| 50 |
+
# Create agent runner
|
| 51 |
+
self.compiled_graph, self.checkpointer, self.langsmith_client, self.thread_id = create_agent_runner(config)
|
| 52 |
+
print("β
Sales Assistant initialized successfully!")
|
| 53 |
+
|
| 54 |
+
except Exception as e:
|
| 55 |
+
print(f"β Failed to initialize sales assistant: {e}")
|
| 56 |
+
self.compiled_graph = None
|
| 57 |
+
|
| 58 |
+
def chat_function(self, message: str, history: List[Dict[str, str]]) -> Tuple[str, List[Dict[str, str]]]:
|
| 59 |
+
"""
|
| 60 |
+
Process a chat message and return the response.
|
| 61 |
+
|
| 62 |
+
Args:
|
| 63 |
+
message: User's input message
|
| 64 |
+
history: Chat history as list of message dictionaries
|
| 65 |
+
|
| 66 |
+
Returns:
|
| 67 |
+
Tuple of (empty_string, updated_history)
|
| 68 |
+
"""
|
| 69 |
+
if not self.compiled_graph:
|
| 70 |
+
error_response = "β Sales Assistant is not properly initialized. Please check your environment configuration."
|
| 71 |
+
history.append({"role": "user", "content": message})
|
| 72 |
+
history.append({"role": "assistant", "content": error_response})
|
| 73 |
+
return "", history
|
| 74 |
+
|
| 75 |
+
try:
|
| 76 |
+
# Run conversation turn
|
| 77 |
+
response = run_conversation_turn(
|
| 78 |
+
compiled_graph=self.compiled_graph,
|
| 79 |
+
thread_id=self.thread_id,
|
| 80 |
+
user_input=message,
|
| 81 |
+
langsmith_client=self.langsmith_client
|
| 82 |
+
)
|
| 83 |
+
|
| 84 |
+
# Add to history
|
| 85 |
+
history.append({"role": "user", "content": message})
|
| 86 |
+
history.append({"role": "assistant", "content": response})
|
| 87 |
+
|
| 88 |
+
except Exception as e:
|
| 89 |
+
error_response = f"β Error processing your request: {str(e)}"
|
| 90 |
+
history.append({"role": "user", "content": message})
|
| 91 |
+
history.append({"role": "assistant", "content": error_response})
|
| 92 |
+
|
| 93 |
+
return "", history
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
def create_gradio_interface():
|
| 97 |
+
"""Create and configure the Gradio interface."""
|
| 98 |
+
|
| 99 |
+
# Initialize the chat assistant
|
| 100 |
+
chat_assistant = SalesAssistantChat()
|
| 101 |
+
|
| 102 |
+
# Create the interface
|
| 103 |
+
with gr.Blocks(
|
| 104 |
+
title="Sales Assistant with Quote Generation",
|
| 105 |
+
theme=gr.themes.Soft(),
|
| 106 |
+
css="""
|
| 107 |
+
.gradio-container {
|
| 108 |
+
max-width: 1200px !important;
|
| 109 |
+
}
|
| 110 |
+
"""
|
| 111 |
+
) as interface:
|
| 112 |
+
|
| 113 |
+
gr.Markdown(
|
| 114 |
+
"""
|
| 115 |
+
# π€ Sales Assistant with Quote Generation
|
| 116 |
+
|
| 117 |
+
Welcome to the Sales Assistant! I can help you:
|
| 118 |
+
- π Search and explore our product database
|
| 119 |
+
- π Analyze product data and statistics
|
| 120 |
+
- π° Generate detailed quotes with pricing
|
| 121 |
+
- π Provide exchange rate information
|
| 122 |
+
- β Answer questions about our products and services
|
| 123 |
+
|
| 124 |
+
Simply type your question or request below to get started!
|
| 125 |
+
"""
|
| 126 |
+
)
|
| 127 |
+
|
| 128 |
+
# Chat interface
|
| 129 |
+
chatbot = gr.Chatbot(
|
| 130 |
+
value=[],
|
| 131 |
+
height=500,
|
| 132 |
+
label="Sales Assistant Chat",
|
| 133 |
+
show_label=True,
|
| 134 |
+
avatar_images=("π€", "π€"),
|
| 135 |
+
type="messages"
|
| 136 |
+
)
|
| 137 |
+
|
| 138 |
+
# Input components
|
| 139 |
+
with gr.Row():
|
| 140 |
+
msg_input = gr.Textbox(
|
| 141 |
+
placeholder="Ask me about products, request a quote, or explore our database...",
|
| 142 |
+
label="Your Message",
|
| 143 |
+
scale=4,
|
| 144 |
+
lines=1
|
| 145 |
+
)
|
| 146 |
+
send_btn = gr.Button("Send", variant="primary", scale=1)
|
| 147 |
+
|
| 148 |
+
# Example queries
|
| 149 |
+
with gr.Row():
|
| 150 |
+
gr.Examples(
|
| 151 |
+
examples=[
|
| 152 |
+
"Show me information about our products",
|
| 153 |
+
"I need a quote for industrial equipment",
|
| 154 |
+
"What are the current exchange rates?",
|
| 155 |
+
"Find products with price under $1000",
|
| 156 |
+
"Generate a quote for customer 'Tech Solutions Inc'",
|
| 157 |
+
"Show me product statistics"
|
| 158 |
+
],
|
| 159 |
+
inputs=msg_input,
|
| 160 |
+
label="Example Queries"
|
| 161 |
+
)
|
| 162 |
+
|
| 163 |
+
# Additional information
|
| 164 |
+
with gr.Accordion("βΉοΈ About This Assistant", open=False):
|
| 165 |
+
gr.Markdown(
|
| 166 |
+
"""
|
| 167 |
+
This Sales Assistant is powered by advanced AI and has access to:
|
| 168 |
+
|
| 169 |
+
- **Product Database**: Complete catalog with specifications and pricing
|
| 170 |
+
- **Quote Generation**: Professional quote creation with current exchange rates
|
| 171 |
+
- **Data Analysis**: Statistical insights and product comparisons
|
| 172 |
+
- **Real-time Information**: Current pricing and availability
|
| 173 |
+
|
| 174 |
+
The assistant uses natural language processing to understand your requests
|
| 175 |
+
and can perform complex database queries to provide accurate information.
|
| 176 |
+
"""
|
| 177 |
+
)
|
| 178 |
+
|
| 179 |
+
# Event handlers
|
| 180 |
+
def submit_message(message, history):
|
| 181 |
+
return chat_assistant.chat_function(message, history)
|
| 182 |
+
|
| 183 |
+
# Wire up the events
|
| 184 |
+
msg_input.submit(
|
| 185 |
+
submit_message,
|
| 186 |
+
inputs=[msg_input, chatbot],
|
| 187 |
+
outputs=[msg_input, chatbot]
|
| 188 |
+
)
|
| 189 |
+
|
| 190 |
+
send_btn.click(
|
| 191 |
+
submit_message,
|
| 192 |
+
inputs=[msg_input, chatbot],
|
| 193 |
+
outputs=[msg_input, chatbot]
|
| 194 |
+
)
|
| 195 |
+
|
| 196 |
+
return interface
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
def main():
|
| 200 |
+
"""Main function to run the Gradio app."""
|
| 201 |
+
print("π Starting Gradio Sales Assistant...")
|
| 202 |
+
|
| 203 |
+
# Create interface
|
| 204 |
+
interface = create_gradio_interface()
|
| 205 |
+
|
| 206 |
+
# Launch the app
|
| 207 |
+
interface.launch(
|
| 208 |
+
server_name="0.0.0.0",
|
| 209 |
+
server_port=7860,
|
| 210 |
+
share=False,
|
| 211 |
+
show_error=True
|
| 212 |
+
)
|
| 213 |
+
|
| 214 |
+
|
| 215 |
+
if __name__ == "__main__":
|
| 216 |
+
main()
|
src/sales_assistant/ui_dashboard/streamlit_app.py
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Streamlit Chat UI for Sales Assistant
|
| 3 |
+
|
| 4 |
+
This module provides a simple Streamlit-based chat interface for the sales assistant.
|
| 5 |
+
"""
|
| 6 |
+
import os
|
| 7 |
+
import sys
|
| 8 |
+
import streamlit as st
|
| 9 |
+
from typing import Dict, Any, List
|
| 10 |
+
|
| 11 |
+
# Add the src directory to the path
|
| 12 |
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../..'))
|
| 13 |
+
|
| 14 |
+
from sales_assistant.main import (
|
| 15 |
+
check_environment_setup,
|
| 16 |
+
create_custom_config
|
| 17 |
+
)
|
| 18 |
+
from sales_assistant.agent_main.agent_runner import (
|
| 19 |
+
create_agent_runner,
|
| 20 |
+
run_conversation_turn
|
| 21 |
+
)
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
# Page configuration
|
| 25 |
+
st.set_page_config(
|
| 26 |
+
page_title="Sales Assistant with Quote Generation",
|
| 27 |
+
page_icon="π€",
|
| 28 |
+
layout="wide",
|
| 29 |
+
initial_sidebar_state="expanded"
|
| 30 |
+
)
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def initialize_session_state():
|
| 34 |
+
"""Initialize Streamlit session state variables."""
|
| 35 |
+
if "messages" not in st.session_state:
|
| 36 |
+
st.session_state.messages = []
|
| 37 |
+
|
| 38 |
+
if "compiled_graph" not in st.session_state:
|
| 39 |
+
st.session_state.compiled_graph = None
|
| 40 |
+
|
| 41 |
+
if "checkpointer" not in st.session_state:
|
| 42 |
+
st.session_state.checkpointer = None
|
| 43 |
+
|
| 44 |
+
if "langsmith_client" not in st.session_state:
|
| 45 |
+
st.session_state.langsmith_client = None
|
| 46 |
+
|
| 47 |
+
if "thread_id" not in st.session_state:
|
| 48 |
+
st.session_state.thread_id = None
|
| 49 |
+
|
| 50 |
+
if "agent_initialized" not in st.session_state:
|
| 51 |
+
st.session_state.agent_initialized = False
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def initialize_agent():
|
| 55 |
+
"""Initialize the sales assistant agent."""
|
| 56 |
+
if st.session_state.agent_initialized:
|
| 57 |
+
return True
|
| 58 |
+
|
| 59 |
+
try:
|
| 60 |
+
with st.spinner("Initializing Sales Assistant..."):
|
| 61 |
+
# Check environment setup
|
| 62 |
+
if not check_environment_setup():
|
| 63 |
+
st.error("β Environment setup failed. Please check your configuration.")
|
| 64 |
+
return False
|
| 65 |
+
|
| 66 |
+
# Create configuration
|
| 67 |
+
config = create_custom_config(
|
| 68 |
+
session_id="streamlit_session",
|
| 69 |
+
user_id="streamlit_user",
|
| 70 |
+
enable_langsmith=True
|
| 71 |
+
)
|
| 72 |
+
|
| 73 |
+
# Create agent runner
|
| 74 |
+
compiled_graph, checkpointer, langsmith_client, thread_id = create_agent_runner(config)
|
| 75 |
+
|
| 76 |
+
# Store in session state
|
| 77 |
+
st.session_state.compiled_graph = compiled_graph
|
| 78 |
+
st.session_state.checkpointer = checkpointer
|
| 79 |
+
st.session_state.langsmith_client = langsmith_client
|
| 80 |
+
st.session_state.thread_id = thread_id
|
| 81 |
+
st.session_state.agent_initialized = True
|
| 82 |
+
|
| 83 |
+
st.success("β
Sales Assistant initialized successfully!")
|
| 84 |
+
return True
|
| 85 |
+
|
| 86 |
+
except Exception as e:
|
| 87 |
+
st.error(f"β Failed to initialize sales assistant: {e}")
|
| 88 |
+
return False
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def display_sidebar():
|
| 92 |
+
"""Display the sidebar with information and controls."""
|
| 93 |
+
with st.sidebar:
|
| 94 |
+
st.markdown("# π€ Sales Assistant")
|
| 95 |
+
st.markdown("---")
|
| 96 |
+
|
| 97 |
+
# Status indicator
|
| 98 |
+
if st.session_state.agent_initialized:
|
| 99 |
+
st.success("π’ Agent Online")
|
| 100 |
+
else:
|
| 101 |
+
st.error("π΄ Agent Offline")
|
| 102 |
+
|
| 103 |
+
st.markdown("---")
|
| 104 |
+
|
| 105 |
+
# Information section
|
| 106 |
+
st.markdown("### π§ Capabilities")
|
| 107 |
+
st.markdown("""
|
| 108 |
+
- π **Product Search**: Find products by criteria
|
| 109 |
+
- π **Data Analysis**: Statistical insights
|
| 110 |
+
- π° **Quote Generation**: Professional quotes
|
| 111 |
+
- π **Exchange Rates**: Current currency rates
|
| 112 |
+
- π **Database Insights**: Explore product data
|
| 113 |
+
""")
|
| 114 |
+
|
| 115 |
+
st.markdown("---")
|
| 116 |
+
|
| 117 |
+
# Example queries
|
| 118 |
+
st.markdown("### π‘ Example Queries")
|
| 119 |
+
example_queries = [
|
| 120 |
+
"Show me product information",
|
| 121 |
+
"Generate a quote for equipment",
|
| 122 |
+
"What are current exchange rates?",
|
| 123 |
+
"Find products under $1000",
|
| 124 |
+
"Show product statistics"
|
| 125 |
+
]
|
| 126 |
+
|
| 127 |
+
for query in example_queries:
|
| 128 |
+
if st.button(query, key=f"example_{hash(query)}", use_container_width=True):
|
| 129 |
+
st.session_state.messages.append({"role": "user", "content": query})
|
| 130 |
+
st.rerun()
|
| 131 |
+
|
| 132 |
+
st.markdown("---")
|
| 133 |
+
|
| 134 |
+
# Clear chat button
|
| 135 |
+
if st.button("ποΈ Clear Chat", use_container_width=True):
|
| 136 |
+
st.session_state.messages = []
|
| 137 |
+
st.rerun()
|
| 138 |
+
|
| 139 |
+
# Restart agent button
|
| 140 |
+
if st.button("π Restart Agent", use_container_width=True):
|
| 141 |
+
st.session_state.agent_initialized = False
|
| 142 |
+
st.session_state.compiled_graph = None
|
| 143 |
+
st.session_state.checkpointer = None
|
| 144 |
+
st.session_state.langsmith_client = None
|
| 145 |
+
st.session_state.thread_id = None
|
| 146 |
+
st.rerun()
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
def display_chat_messages():
|
| 150 |
+
"""Display the chat messages."""
|
| 151 |
+
for message in st.session_state.messages:
|
| 152 |
+
with st.chat_message(message["role"]):
|
| 153 |
+
st.markdown(message["content"])
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
def process_user_input(user_input: str) -> str:
|
| 157 |
+
"""
|
| 158 |
+
Process user input and get response from the sales assistant.
|
| 159 |
+
|
| 160 |
+
Args:
|
| 161 |
+
user_input: The user's message
|
| 162 |
+
|
| 163 |
+
Returns:
|
| 164 |
+
The assistant's response
|
| 165 |
+
"""
|
| 166 |
+
if not st.session_state.compiled_graph:
|
| 167 |
+
return "β Sales Assistant is not properly initialized. Please restart the agent."
|
| 168 |
+
|
| 169 |
+
try:
|
| 170 |
+
# Run conversation turn
|
| 171 |
+
response = run_conversation_turn(
|
| 172 |
+
compiled_graph=st.session_state.compiled_graph,
|
| 173 |
+
thread_id=st.session_state.thread_id,
|
| 174 |
+
user_input=user_input,
|
| 175 |
+
langsmith_client=st.session_state.langsmith_client
|
| 176 |
+
)
|
| 177 |
+
return response
|
| 178 |
+
|
| 179 |
+
except Exception as e:
|
| 180 |
+
return f"β Error processing your request: {str(e)}"
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
def main():
|
| 184 |
+
"""Main Streamlit application."""
|
| 185 |
+
# Initialize session state
|
| 186 |
+
initialize_session_state()
|
| 187 |
+
|
| 188 |
+
# Main title
|
| 189 |
+
st.title("π€ Sales Assistant with Quote Generation")
|
| 190 |
+
st.markdown("---")
|
| 191 |
+
|
| 192 |
+
# Initialize agent if not done
|
| 193 |
+
if not st.session_state.agent_initialized:
|
| 194 |
+
if not initialize_agent():
|
| 195 |
+
st.stop()
|
| 196 |
+
|
| 197 |
+
# Display sidebar
|
| 198 |
+
display_sidebar()
|
| 199 |
+
|
| 200 |
+
# Main chat area
|
| 201 |
+
col1, col2 = st.columns([3, 1])
|
| 202 |
+
|
| 203 |
+
with col1:
|
| 204 |
+
# Introduction message
|
| 205 |
+
if not st.session_state.messages:
|
| 206 |
+
st.markdown("""
|
| 207 |
+
### Welcome to the Sales Assistant! π
|
| 208 |
+
|
| 209 |
+
I'm here to help you with:
|
| 210 |
+
- π **Product Information**: Search and explore our product database
|
| 211 |
+
- π **Data Analysis**: Get insights and statistics about products
|
| 212 |
+
- π° **Quote Generation**: Create professional quotes with current pricing
|
| 213 |
+
- π **Exchange Rates**: Get up-to-date currency information
|
| 214 |
+
- β **General Questions**: Ask me anything about our products and services
|
| 215 |
+
|
| 216 |
+
**To get started**, type your question in the chat box below or click one of the example queries in the sidebar.
|
| 217 |
+
""")
|
| 218 |
+
st.markdown("---")
|
| 219 |
+
|
| 220 |
+
# Display chat messages
|
| 221 |
+
display_chat_messages()
|
| 222 |
+
|
| 223 |
+
# Chat input
|
| 224 |
+
if prompt := st.chat_input("Ask me about products, request a quote, or explore our database..."):
|
| 225 |
+
# Add user message to chat history
|
| 226 |
+
st.session_state.messages.append({"role": "user", "content": prompt})
|
| 227 |
+
|
| 228 |
+
# Display user message
|
| 229 |
+
with st.chat_message("user"):
|
| 230 |
+
st.markdown(prompt)
|
| 231 |
+
|
| 232 |
+
# Get and display assistant response
|
| 233 |
+
with st.chat_message("assistant"):
|
| 234 |
+
with st.spinner("Thinking..."):
|
| 235 |
+
response = process_user_input(prompt)
|
| 236 |
+
st.markdown(response)
|
| 237 |
+
|
| 238 |
+
# Add assistant response to chat history
|
| 239 |
+
st.session_state.messages.append({"role": "assistant", "content": response})
|
| 240 |
+
|
| 241 |
+
with col2:
|
| 242 |
+
# Stats section
|
| 243 |
+
st.markdown("### π Chat Statistics")
|
| 244 |
+
total_messages = len(st.session_state.messages)
|
| 245 |
+
user_messages = len([m for m in st.session_state.messages if m["role"] == "user"])
|
| 246 |
+
assistant_messages = len([m for m in st.session_state.messages if m["role"] == "assistant"])
|
| 247 |
+
|
| 248 |
+
st.metric("Total Messages", total_messages)
|
| 249 |
+
st.metric("Your Messages", user_messages)
|
| 250 |
+
st.metric("Assistant Responses", assistant_messages)
|
| 251 |
+
|
| 252 |
+
# Recent activity
|
| 253 |
+
if st.session_state.messages:
|
| 254 |
+
st.markdown("### π Recent Activity")
|
| 255 |
+
recent_messages = st.session_state.messages[-3:]
|
| 256 |
+
for msg in recent_messages:
|
| 257 |
+
role_icon = "π€" if msg["role"] == "user" else "π€"
|
| 258 |
+
st.text(f"{role_icon} {msg['content'][:50]}...")
|
| 259 |
+
|
| 260 |
+
# Footer
|
| 261 |
+
st.markdown("---")
|
| 262 |
+
st.markdown(
|
| 263 |
+
"""
|
| 264 |
+
<div style='text-align: center; color: #666;'>
|
| 265 |
+
Sales Assistant powered by AI | Built with Streamlit
|
| 266 |
+
</div>
|
| 267 |
+
""",
|
| 268 |
+
unsafe_allow_html=True
|
| 269 |
+
)
|
| 270 |
+
|
| 271 |
+
|
| 272 |
+
if __name__ == "__main__":
|
| 273 |
+
main()
|
uv.lock
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|