Spaces:
Runtime error
Runtime error
Upload folder using huggingface_hub
Browse files- Dockerfile +2 -2
- pyproject.toml +15 -0
- run.py +10 -0
- src/chatbot_v2/Dockerfile +35 -0
- src/chatbot_v2/README.md +223 -0
- src/chatbot_v2/app.py +274 -0
- src/chatbot_v2/core/__init__.py +4 -0
- src/chatbot_v2/core/model.py +36 -0
- src/chatbot_v2/layers/__init__.py +0 -0
- src/chatbot_v2/layers/action.py +71 -0
- src/chatbot_v2/layers/cognition.py +111 -0
- src/chatbot_v2/layers/memory.py +20 -0
- src/chatbot_v2/layers/perception.py +79 -0
- src/chatbot_v2/patterns/__init__.py +0 -0
- src/chatbot_v2/patterns/orchestrator.py +66 -0
- src/chatbot_v2/prompts/economic_news.txt +27 -0
- src/chatbot_v2/prompts/entertainment_updates.txt +26 -0
- src/chatbot_v2/prompts/india_news.txt +26 -0
- src/chatbot_v2/prompts/market_sentiment.txt +34 -0
- src/chatbot_v2/prompts/news_headlines.txt +28 -0
- src/chatbot_v2/prompts/odia_news.txt +26 -0
- src/chatbot_v2/prompts/trade_recommendation.txt +40 -0
- src/chatbot_v2/prompts/upcoming_earnings.txt +27 -0
- src/chatbot_v2/trace_config.py +37 -0
- uv.lock +0 -0
Dockerfile
CHANGED
|
@@ -21,7 +21,7 @@ COPY uv.lock .
|
|
| 21 |
|
| 22 |
# Copy required folders
|
| 23 |
COPY common/ ./common/
|
| 24 |
-
COPY src/
|
| 25 |
|
| 26 |
# Install dependencies using uv, then export and install with pip to system
|
| 27 |
RUN uv sync --frozen --no-dev && \
|
|
@@ -32,4 +32,4 @@ COPY run.py .
|
|
| 32 |
|
| 33 |
EXPOSE 7860
|
| 34 |
|
| 35 |
-
CMD ["python", "run.py", "
|
|
|
|
| 21 |
|
| 22 |
# Copy required folders
|
| 23 |
COPY common/ ./common/
|
| 24 |
+
COPY src/chatbot_v2/ ./src/chatbot_v2/
|
| 25 |
|
| 26 |
# Install dependencies using uv, then export and install with pip to system
|
| 27 |
RUN uv sync --frozen --no-dev && \
|
|
|
|
| 32 |
|
| 33 |
EXPOSE 7860
|
| 34 |
|
| 35 |
+
CMD ["python", "run.py", "chatbot_v2", "--port", "7860"]
|
pyproject.toml
CHANGED
|
@@ -33,6 +33,16 @@ dependencies = [
|
|
| 33 |
"html2text>=2025.4.15",
|
| 34 |
"traceloop-sdk>=0.33.0",
|
| 35 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
# =======================
|
| 37 |
# VECTOR DB / INDEXING
|
| 38 |
# =======================
|
|
@@ -94,6 +104,11 @@ dependencies = [
|
|
| 94 |
"reportlab>=4.4.5",
|
| 95 |
"fastapi",
|
| 96 |
"Pillow",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
|
| 98 |
# =======================
|
| 99 |
# AUDIO / VIDEO
|
|
|
|
| 33 |
"html2text>=2025.4.15",
|
| 34 |
"traceloop-sdk>=0.33.0",
|
| 35 |
|
| 36 |
+
# =======================
|
| 37 |
+
# MICROSOFT AGENT FRAMEWORK
|
| 38 |
+
# =======================
|
| 39 |
+
#"agent-framework==1.0.0b251204",
|
| 40 |
+
#"agent-framework-azure-ai==1.0.0b251204",
|
| 41 |
+
#"azure-ai-projects",
|
| 42 |
+
#"azure-ai-agents",
|
| 43 |
+
#"azure-ai-agents>=1.2.0b5",
|
| 44 |
+
#"agent-framework-azure-ai",
|
| 45 |
+
|
| 46 |
# =======================
|
| 47 |
# VECTOR DB / INDEXING
|
| 48 |
# =======================
|
|
|
|
| 104 |
"reportlab>=4.4.5",
|
| 105 |
"fastapi",
|
| 106 |
"Pillow",
|
| 107 |
+
"python-docx",
|
| 108 |
+
"matplotlib",
|
| 109 |
+
"fpdf",
|
| 110 |
+
"extra-streamlit-components",
|
| 111 |
+
"nest_asyncio",
|
| 112 |
|
| 113 |
# =======================
|
| 114 |
# AUDIO / VIDEO
|
run.py
CHANGED
|
@@ -58,6 +58,11 @@ APP_REGISTRY: Dict[str, Dict[str, str]] = {
|
|
| 58 |
"entry": "app.py",
|
| 59 |
"description": "General Chatbot - Multi-purpose conversational AI"
|
| 60 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
"accessibility": {
|
| 62 |
"path": "src/accessibility",
|
| 63 |
"entry": "app.py",
|
|
@@ -77,6 +82,11 @@ APP_REGISTRY: Dict[str, Dict[str, str]] = {
|
|
| 77 |
"path": "src/image-generator",
|
| 78 |
"entry": "app.py",
|
| 79 |
"description": "Image Generator - Multi-agent image generation tool"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
}
|
| 81 |
}
|
| 82 |
|
|
|
|
| 58 |
"entry": "app.py",
|
| 59 |
"description": "General Chatbot - Multi-purpose conversational AI"
|
| 60 |
},
|
| 61 |
+
"chatbot_v2": {
|
| 62 |
+
"path": "src/chatbot_v2",
|
| 63 |
+
"entry": "app.py",
|
| 64 |
+
"description": "Layered Chatbot (ReAct) - Advanced Architecture"
|
| 65 |
+
},
|
| 66 |
"accessibility": {
|
| 67 |
"path": "src/accessibility",
|
| 68 |
"entry": "app.py",
|
|
|
|
| 82 |
"path": "src/image-generator",
|
| 83 |
"entry": "app.py",
|
| 84 |
"description": "Image Generator - Multi-agent image generation tool"
|
| 85 |
+
},
|
| 86 |
+
"interview-assistant": {
|
| 87 |
+
"path": "src/interview-assistant",
|
| 88 |
+
"entry": "app.py",
|
| 89 |
+
"description": "Interview Assistant - Multi-agent interview tool"
|
| 90 |
}
|
| 91 |
}
|
| 92 |
|
src/chatbot_v2/Dockerfile
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.12-slim
|
| 2 |
+
|
| 3 |
+
ENV PYTHONUNBUFFERED=1 \
|
| 4 |
+
DEBIAN_FRONTEND=noninteractive \
|
| 5 |
+
PYTHONPATH=/app:/app/common:$PYTHONPATH
|
| 6 |
+
|
| 7 |
+
WORKDIR /app
|
| 8 |
+
|
| 9 |
+
# System deps
|
| 10 |
+
RUN apt-get update && apt-get install -y \
|
| 11 |
+
git build-essential curl \
|
| 12 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 13 |
+
|
| 14 |
+
# Install uv
|
| 15 |
+
RUN curl -LsSf https://astral.sh/uv/install.sh | sh
|
| 16 |
+
ENV PATH="/root/.local/bin:$PATH"
|
| 17 |
+
|
| 18 |
+
# Copy project metadata
|
| 19 |
+
COPY pyproject.toml .
|
| 20 |
+
COPY uv.lock .
|
| 21 |
+
|
| 22 |
+
# Copy required folders
|
| 23 |
+
COPY common/ ./common/
|
| 24 |
+
COPY src/chatbot_v2/ ./src/chatbot_v2/
|
| 25 |
+
|
| 26 |
+
# Install dependencies using uv, then export and install with pip to system
|
| 27 |
+
RUN uv sync --frozen --no-dev && \
|
| 28 |
+
uv pip install -e . --system
|
| 29 |
+
|
| 30 |
+
# Copy entry point
|
| 31 |
+
COPY run.py .
|
| 32 |
+
|
| 33 |
+
EXPOSE 7860
|
| 34 |
+
|
| 35 |
+
CMD ["python", "run.py", "chatbot_v2", "--port", "7860"]
|
src/chatbot_v2/README.md
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: AI Chatbot
|
| 3 |
+
emoji: 🤖
|
| 4 |
+
colorFrom: pink
|
| 5 |
+
colorTo: yellow
|
| 6 |
+
sdk: docker
|
| 7 |
+
sdk_version: "0.0.1"
|
| 8 |
+
app_file: app.py
|
| 9 |
+
pinned: false
|
| 10 |
+
license: mit
|
| 11 |
+
tags:
|
| 12 |
+
- text-generation
|
| 13 |
+
- agentic-ai
|
| 14 |
+
- openai-sdk
|
| 15 |
+
short_description: An Experimental Agentic Chatbot (uses OpenAI Agent SDK)
|
| 16 |
+
---
|
| 17 |
+
|
| 18 |
+
# AI Chatbot
|
| 19 |
+
|
| 20 |
+
This is an experimental chatbot for chatting with AI. It is equipped with agents & tools to give you realtime data from the web. It uses **OpenAI - SDK** and **OpenAI - Agents**...
|
| 21 |
+
|
| 22 |
+
## Features
|
| 23 |
+
- Predefined prompts for quick analysis
|
| 24 |
+
- Chat interface with AI responses
|
| 25 |
+
- Enter key support and responsive design
|
| 26 |
+
- Latest messages appear on top
|
| 27 |
+
|
| 28 |
+
## Usage
|
| 29 |
+
1. Type a message or select a predefined prompt
|
| 30 |
+
2. Press **Enter** or click **Send**
|
| 31 |
+
3. AI responses appear instantly in the chat interface
|
| 32 |
+
|
| 33 |
+
## Supported APIs
|
| 34 |
+
- OpenAI
|
| 35 |
+
- Google
|
| 36 |
+
- GROQ
|
| 37 |
+
- SERPER
|
| 38 |
+
- News API
|
| 39 |
+
|
| 40 |
+
## Notes
|
| 41 |
+
- Make sure your API keys are configured in the Space secrets
|
| 42 |
+
- Built using Streamlit and deployed as a Docker Space
|
| 43 |
+
|
| 44 |
+
## References
|
| 45 |
+
|
| 46 |
+
https://openai.github.io/openai-agents-python/
|
| 47 |
+
|
| 48 |
+
https://github.com/openai/openai-agents-python/tree/main/examples/mcp
|
| 49 |
+
|
| 50 |
+
## Project Folder Structure
|
| 51 |
+
|
| 52 |
+
```
|
| 53 |
+
chatbot/
|
| 54 |
+
├── app.py # Main Streamlit chatbot interface
|
| 55 |
+
├── appagents/
|
| 56 |
+
│ ├── __init__.py # Package initialization
|
| 57 |
+
│ ├── OrchestratorAgent.py # Main orchestrator - coordinates all agents
|
| 58 |
+
│ ├── FinancialAgent.py # Financial data and analysis agent
|
| 59 |
+
│ ├── NewsAgent.py # News retrieval and summarization agent
|
| 60 |
+
│ ├── SearchAgent.py # General web search agent
|
| 61 |
+
│ └── InputValidationAgent.py # Input validation and sanitization agent
|
| 62 |
+
├── core/
|
| 63 |
+
│ ├── __init__.py # Package initialization
|
| 64 |
+
│ └── logger.py # Centralized logging configuration
|
| 65 |
+
├── tools/
|
| 66 |
+
│ ├── __init__.py # Package initialization
|
| 67 |
+
│ ├── google_tools.py # Google search API wrapper
|
| 68 |
+
│ ├── yahoo_tools.py # Yahoo Finance API wrapper
|
| 69 |
+
│ ├── news_tools.py # News API wrapper
|
| 70 |
+
│ └── time_tools.py # Time-related utility functions
|
| 71 |
+
├── prompts/
|
| 72 |
+
│ ├── economic_news.txt # Prompt for economic news analysis
|
| 73 |
+
│ ├── market_sentiment.txt # Prompt for market sentiment analysis
|
| 74 |
+
│ ├── news_headlines.txt # Prompt for news headline summarization
|
| 75 |
+
│ ├── trade_recommendation.txt # Prompt for trade recommendations
|
| 76 |
+
│ └── upcoming_earnings.txt # Prompt for upcoming earnings alerts
|
| 77 |
+
├── Dockerfile # Docker configuration for container deployment
|
| 78 |
+
├── pyproject.toml # Project metadata and dependencies (copied from root)
|
| 79 |
+
├── uv.lock # Locked dependency versions (copied from root)
|
| 80 |
+
├── README.md # Project documentation
|
| 81 |
+
└── run.py # Script to run the application locally
|
| 82 |
+
```
|
| 83 |
+
|
| 84 |
+
## File Descriptions
|
| 85 |
+
|
| 86 |
+
### UI Layer
|
| 87 |
+
- **app.py** - Main Streamlit chatbot interface that provides:
|
| 88 |
+
- Chat message display with user and AI messages
|
| 89 |
+
- Text input for user queries
|
| 90 |
+
- Predefined prompt buttons for quick analysis
|
| 91 |
+
- Real-time AI responses
|
| 92 |
+
- Support for Enter key submission
|
| 93 |
+
- Responsive design with latest messages appearing first
|
| 94 |
+
|
| 95 |
+
### Agents (`appagents/`)
|
| 96 |
+
- **OrchestratorAgent.py** - Main orchestrator that:
|
| 97 |
+
- Coordinates communication between all specialized agents
|
| 98 |
+
- Routes user queries to appropriate agents
|
| 99 |
+
- Manages conversation flow and context
|
| 100 |
+
- Integrates tool responses
|
| 101 |
+
|
| 102 |
+
- **FinancialAgent.py** - Financial data and analysis:
|
| 103 |
+
- Retrieves stock prices and financial metrics
|
| 104 |
+
- Performs financial analysis using Yahoo Finance API
|
| 105 |
+
- Provides market insights and recommendations
|
| 106 |
+
- Integrates with yahoo_tools for data fetching
|
| 107 |
+
|
| 108 |
+
- **NewsAgent.py** - News retrieval and summarization:
|
| 109 |
+
- Fetches latest news articles
|
| 110 |
+
- Summarizes news content
|
| 111 |
+
- Integrates with News API for real-time updates
|
| 112 |
+
- Provides news-based market insights
|
| 113 |
+
|
| 114 |
+
- **SearchAgent.py** - General web search:
|
| 115 |
+
- Performs web searches for general queries
|
| 116 |
+
- Integrates with Google Search / Serper API
|
| 117 |
+
- Returns relevant search results
|
| 118 |
+
- Supports multi-source data gathering
|
| 119 |
+
|
| 120 |
+
- **InputValidationAgent.py** - Input validation:
|
| 121 |
+
- Sanitizes user input
|
| 122 |
+
- Validates query format and content
|
| 123 |
+
- Prevents malicious inputs
|
| 124 |
+
- Ensures appropriate content
|
| 125 |
+
|
| 126 |
+
### Core Utilities (`core/`)
|
| 127 |
+
- **logger.py** - Centralized logging configuration:
|
| 128 |
+
- Provides consistent logging across agents
|
| 129 |
+
- Handles different log levels
|
| 130 |
+
- Formats log messages for clarity
|
| 131 |
+
|
| 132 |
+
### Tools (`tools/`)
|
| 133 |
+
- **google_tools.py** - Google Search API wrapper:
|
| 134 |
+
- Executes web searches via Google Search / Serper API
|
| 135 |
+
- Parses and returns search results
|
| 136 |
+
- Handles API authentication
|
| 137 |
+
|
| 138 |
+
- **yahoo_tools.py** - Yahoo Finance API integration:
|
| 139 |
+
- Retrieves stock price data
|
| 140 |
+
- Fetches financial metrics and indicators
|
| 141 |
+
- Provides historical price data
|
| 142 |
+
- Returns earnings information
|
| 143 |
+
|
| 144 |
+
- **news_tools.py** - News API integration:
|
| 145 |
+
- Fetches latest news articles
|
| 146 |
+
- Filters news by category and keywords
|
| 147 |
+
- Returns news headlines and summaries
|
| 148 |
+
- Provides market-related news feeds
|
| 149 |
+
|
| 150 |
+
- **time_tools.py** - Time utility functions:
|
| 151 |
+
- Provides current time information
|
| 152 |
+
- Formats timestamps
|
| 153 |
+
- Handles timezone conversions
|
| 154 |
+
|
| 155 |
+
### Prompts (`prompts/`)
|
| 156 |
+
Predefined prompts for specialized analysis:
|
| 157 |
+
- **economic_news.txt** - Analyzes economic news and implications
|
| 158 |
+
- **market_sentiment.txt** - Analyzes market sentiment trends
|
| 159 |
+
- **news_headlines.txt** - Summarizes and explains news headlines
|
| 160 |
+
- **trade_recommendation.txt** - Provides trading recommendations
|
| 161 |
+
- **upcoming_earnings.txt** - Alerts about upcoming earnings reports
|
| 162 |
+
|
| 163 |
+
### Configuration Files
|
| 164 |
+
- **Dockerfile** - Container deployment:
|
| 165 |
+
- Builds Docker image with Python 3.12
|
| 166 |
+
- Installs dependencies using `uv`
|
| 167 |
+
- Sets up Streamlit server on port 8501
|
| 168 |
+
- Configures PYTHONPATH for module imports
|
| 169 |
+
|
| 170 |
+
- **pyproject.toml** - Project metadata:
|
| 171 |
+
- Package name: "agents"
|
| 172 |
+
- Python version requirement: 3.12
|
| 173 |
+
- Lists all dependencies (OpenAI, LangChain, Streamlit, etc.)
|
| 174 |
+
|
| 175 |
+
- **uv.lock** - Dependency lock file:
|
| 176 |
+
- Ensures reproducible builds
|
| 177 |
+
- Pins exact versions of all dependencies
|
| 178 |
+
|
| 179 |
+
## Key Technologies
|
| 180 |
+
|
| 181 |
+
| Component | Technology | Purpose |
|
| 182 |
+
|-----------|-----------|---------|
|
| 183 |
+
| LLM Framework | OpenAI Agents | Multi-agent orchestration |
|
| 184 |
+
| Chat Interface | Streamlit | User interaction and display |
|
| 185 |
+
| Web Search | Google Search / Serper API | Web search results |
|
| 186 |
+
| Financial Data | Yahoo Finance API | Stock prices and metrics |
|
| 187 |
+
| News Data | News API | Latest news articles |
|
| 188 |
+
| Async Operations | AsyncIO | Parallel agent execution |
|
| 189 |
+
| Dependencies | UV | Fast Python package management |
|
| 190 |
+
| Containerization | Docker | Cloud deployment |
|
| 191 |
+
|
| 192 |
+
## Predefined Prompts
|
| 193 |
+
|
| 194 |
+
The chatbot includes quick-access buttons for common analysis:
|
| 195 |
+
|
| 196 |
+
1. **Economic News** - Analyzes current economic trends and news
|
| 197 |
+
2. **Market Sentiment** - Provides market sentiment analysis
|
| 198 |
+
3. **News Headlines** - Summarizes latest news headlines
|
| 199 |
+
4. **Trade Recommendation** - Suggests trading strategies
|
| 200 |
+
5. **Upcoming Earnings** - Lists upcoming company earnings
|
| 201 |
+
|
| 202 |
+
## Running Locally
|
| 203 |
+
|
| 204 |
+
```bash
|
| 205 |
+
# Install dependencies
|
| 206 |
+
uv sync
|
| 207 |
+
|
| 208 |
+
# Set environment variables defined in .env.name file
|
| 209 |
+
export OPENAI_API_KEY="your-key"
|
| 210 |
+
export SERPER_API_KEY="your-key"
|
| 211 |
+
export NEWS_API_KEY="your-key"
|
| 212 |
+
|
| 213 |
+
# Run the Streamlit app (from the root)
|
| 214 |
+
python run.py chatbot
|
| 215 |
+
```
|
| 216 |
+
|
| 217 |
+
## Deployment
|
| 218 |
+
|
| 219 |
+
The project is deployed on Hugging Face Spaces as a Docker container:
|
| 220 |
+
- **Space**: https://huggingface.co/spaces/mishrabp/chatbot-app
|
| 221 |
+
- **URL**: https://mishrabp-chatbot-app.hf.space
|
| 222 |
+
- **Trigger**: Automatic deployment on push to `main` branch
|
| 223 |
+
- **Configuration**: `.github/workflows/chatbot-app-hf.yml`
|
src/chatbot_v2/app.py
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import glob
|
| 3 |
+
import uuid
|
| 4 |
+
import asyncio
|
| 5 |
+
import logging
|
| 6 |
+
import streamlit as st
|
| 7 |
+
|
| 8 |
+
# Import the new Orchestrator Pattern
|
| 9 |
+
from src.chatbot_v2.patterns.orchestrator import ChatbotOrchestrator
|
| 10 |
+
|
| 11 |
+
# -----------------------------
|
| 12 |
+
# Configuration & Utils
|
| 13 |
+
# -----------------------------
|
| 14 |
+
st.set_page_config(
|
| 15 |
+
page_title="Layered AI Assistant",
|
| 16 |
+
layout="wide",
|
| 17 |
+
page_icon="🧠"
|
| 18 |
+
)
|
| 19 |
+
|
| 20 |
+
def load_prompts(folder="prompts"):
|
| 21 |
+
prompts = []
|
| 22 |
+
prompt_labels = []
|
| 23 |
+
if os.path.exists(folder):
|
| 24 |
+
for file_path in glob.glob(os.path.join(folder, "*.txt")):
|
| 25 |
+
with open(file_path, "r", encoding="utf-8") as f:
|
| 26 |
+
content = f.read().strip()
|
| 27 |
+
if content:
|
| 28 |
+
prompts.append(content)
|
| 29 |
+
|
| 30 |
+
prompt_labels.append(os.path.basename(file_path).replace("_", " ").replace(".txt", "").title())
|
| 31 |
+
return prompts, prompt_labels
|
| 32 |
+
|
| 33 |
+
prompts, prompt_labels = load_prompts()
|
| 34 |
+
|
| 35 |
+
# -----------------------------
|
| 36 |
+
# Session State
|
| 37 |
+
# -----------------------------
|
| 38 |
+
if "messages" not in st.session_state:
|
| 39 |
+
st.session_state.messages = []
|
| 40 |
+
|
| 41 |
+
# Initialize the Agent
|
| 42 |
+
if "agent" not in st.session_state:
|
| 43 |
+
st.session_state.agent = ChatbotOrchestrator()
|
| 44 |
+
|
| 45 |
+
# -----------------------------
|
| 46 |
+
# Premium Styling
|
| 47 |
+
# -----------------------------
|
| 48 |
+
st.markdown("""
|
| 49 |
+
<style>
|
| 50 |
+
/* ---------------------------------------------------------------------
|
| 51 |
+
1. GLOBAL & RESET
|
| 52 |
+
--------------------------------------------------------------------- */
|
| 53 |
+
* {
|
| 54 |
+
box-sizing: border-box;
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
.stApp, [data-testid="stAppViewContainer"] {
|
| 58 |
+
/* Standard Streamlit background */
|
| 59 |
+
background-color: #f8f9fa;
|
| 60 |
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji';
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
html {
|
| 64 |
+
-webkit-text-size-adjust: 100%; /* Prevent iOS font boosting */
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
/* ---------------------------------------------------------------------
|
| 68 |
+
2. LAYOUT & HERO BANNER
|
| 69 |
+
--------------------------------------------------------------------- */
|
| 70 |
+
|
| 71 |
+
/* Mobile font optimization */
|
| 72 |
+
@media (max-width: 768px) {
|
| 73 |
+
/* Target all markdown text specifically */
|
| 74 |
+
.stMarkdown p, .stMarkdown li, .stChatMessage p, .message-content, .stDataFrame, .stTable {
|
| 75 |
+
font-size: 16px !important;
|
| 76 |
+
line-height: 1.6 !important;
|
| 77 |
+
color: #1a1a1a !important;
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
h1, h2, h3, h4, h5, h6 {
|
| 81 |
+
color: #1a1a1a !important;
|
| 82 |
+
}
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
/* Desktop Layout */
|
| 86 |
+
@media (min-width: 769px) {
|
| 87 |
+
.block-container {
|
| 88 |
+
padding-top: 0 !important;
|
| 89 |
+
padding-bottom: 2rem !important;
|
| 90 |
+
padding-left: 5rem !important;
|
| 91 |
+
padding-right: 5rem !important;
|
| 92 |
+
max-width: 100% !important;
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
.hero-container {
|
| 96 |
+
margin-top: -3rem;
|
| 97 |
+
margin-left: -5rem;
|
| 98 |
+
margin-right: -5rem;
|
| 99 |
+
/* Simple negative margins to pull edge-to-edge */
|
| 100 |
+
padding: 2.5rem 1rem 2rem 1rem; /* Compact desktop padding */
|
| 101 |
+
}
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
/* Mobile Layout */
|
| 105 |
+
@media (max-width: 768px) {
|
| 106 |
+
.block-container {
|
| 107 |
+
padding-left: 1rem !important;
|
| 108 |
+
padding-right: 1rem !important;
|
| 109 |
+
padding-top: 0 !important;
|
| 110 |
+
padding-bottom: 0rem !important;
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
.hero-container {
|
| 114 |
+
margin-top: -2rem;
|
| 115 |
+
margin-left: -1rem;
|
| 116 |
+
margin-right: -1rem;
|
| 117 |
+
/* Break out of the 1rem padding */
|
| 118 |
+
padding: 2rem 1rem 1.5rem 1rem; /* Compact mobile padding */
|
| 119 |
+
border-radius: 0 0 12px 12px;
|
| 120 |
+
}
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
/* Hero Styling */
|
| 124 |
+
.hero-container {
|
| 125 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 126 |
+
color: white;
|
| 127 |
+
text-align: center;
|
| 128 |
+
border-radius: 0 0 16px 16px;
|
| 129 |
+
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
|
| 130 |
+
margin-bottom: 2rem;
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
.hero-title {
|
| 134 |
+
font-size: 2rem; /* Slightly smaller */
|
| 135 |
+
font-weight: 700;
|
| 136 |
+
margin-bottom: 0.25rem;
|
| 137 |
+
color: white !important;
|
| 138 |
+
}
|
| 139 |
+
.hero-subtitle {
|
| 140 |
+
font-size: 1rem;
|
| 141 |
+
opacity: 0.95;
|
| 142 |
+
font-weight: 400;
|
| 143 |
+
color: rgba(255,255,255,0.95) !important;
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
/* Remove Header Decoration */
|
| 147 |
+
header[data-testid="stHeader"] {
|
| 148 |
+
background-color: transparent !important;
|
| 149 |
+
height: 0 !important;
|
| 150 |
+
z-index: 100;
|
| 151 |
+
}
|
| 152 |
+
div[data-testid="stDecoration"] { display: none; }
|
| 153 |
+
|
| 154 |
+
/* ---------------------------------------------------------------------
|
| 155 |
+
3. COMPONENT STYLING (Healthcare-like)
|
| 156 |
+
--------------------------------------------------------------------- */
|
| 157 |
+
|
| 158 |
+
/* Chat Bubbles - Clean & Readable */
|
| 159 |
+
.stChatMessage {
|
| 160 |
+
background-color: white;
|
| 161 |
+
border-radius: 12px;
|
| 162 |
+
border: 1px solid #e5e5e5;
|
| 163 |
+
box-shadow: 0 1px 2px rgba(0,0,0,0.05);
|
| 164 |
+
padding: 1rem;
|
| 165 |
+
}
|
| 166 |
+
|
| 167 |
+
.stChatMessage[data-testid="stChatMessage"]:nth-of-type(odd) {
|
| 168 |
+
background-color: #f8f9fa;
|
| 169 |
+
}
|
| 170 |
+
|
| 171 |
+
/* Input Fields */
|
| 172 |
+
.stTextInput input {
|
| 173 |
+
border-radius: 20px; /* Matching healthcare-assistant roundness */
|
| 174 |
+
border: 1px solid #ddd;
|
| 175 |
+
padding: 0.75rem 1rem;
|
| 176 |
+
}
|
| 177 |
+
|
| 178 |
+
/* Buttons */
|
| 179 |
+
.stButton button {
|
| 180 |
+
border-radius: 20px; /* Matching healthcare-assistant */
|
| 181 |
+
min-height: 48px;
|
| 182 |
+
font-weight: 500;
|
| 183 |
+
}
|
| 184 |
+
|
| 185 |
+
/* Sidebar */
|
| 186 |
+
section[data-testid="stSidebar"] {
|
| 187 |
+
background-color: #ffffff;
|
| 188 |
+
border-right: 1px solid #eaeaea;
|
| 189 |
+
}
|
| 190 |
+
|
| 191 |
+
/* Minimize Sidebar Top Padding */
|
| 192 |
+
section[data-testid="stSidebar"] .block-container {
|
| 193 |
+
padding-top: 0rem !important;
|
| 194 |
+
padding-bottom: 0rem !important;
|
| 195 |
+
}
|
| 196 |
+
</style>
|
| 197 |
+
""", unsafe_allow_html=True)
|
| 198 |
+
|
| 199 |
+
# -----------------------------
|
| 200 |
+
# Logic
|
| 201 |
+
# -----------------------------
|
| 202 |
+
async def get_ai_response(prompt: str) -> str:
|
| 203 |
+
try:
|
| 204 |
+
agent: ChatbotOrchestrator = st.session_state.agent
|
| 205 |
+
|
| 206 |
+
# We pass the *previous* history (messages excluding the latest one which we just appended)
|
| 207 |
+
# Actually, st.session_state.messages ALREADY has the new user message appended below.
|
| 208 |
+
# So we pass messages[:-1] as "history"
|
| 209 |
+
history = st.session_state.messages[:-1]
|
| 210 |
+
|
| 211 |
+
result = await agent.run(user_input=prompt, external_history=history)
|
| 212 |
+
return result
|
| 213 |
+
except Exception as e:
|
| 214 |
+
return f"❌ **Error**: {str(e)}"
|
| 215 |
+
|
| 216 |
+
# -----------------------------
|
| 217 |
+
# Sidebar - Quick Actions
|
| 218 |
+
# -----------------------------
|
| 219 |
+
with st.sidebar:
|
| 220 |
+
st.markdown("### ⚡ Quick Starters")
|
| 221 |
+
st.markdown("Select a prompt to start:")
|
| 222 |
+
|
| 223 |
+
# We use a trick with st.button to act as input triggers
|
| 224 |
+
# If a button is clicked, we'll handle it in the main loop logic
|
| 225 |
+
selected_prompt = None
|
| 226 |
+
for idx, prompt_text in enumerate(prompts):
|
| 227 |
+
label = prompt_labels[idx] if idx < len(prompt_labels) else f"Prompt {idx+1}"
|
| 228 |
+
if st.button(label, key=f"sidebar_btn_{idx}", use_container_width=True):
|
| 229 |
+
# Reset conversation
|
| 230 |
+
st.session_state.messages = []
|
| 231 |
+
st.session_state.agent = ChatbotOrchestrator() # Reset agent memory too
|
| 232 |
+
selected_prompt = prompt_text
|
| 233 |
+
|
| 234 |
+
st.markdown("---")
|
| 235 |
+
if st.button("🗑️ Clear Conversation", use_container_width=True):
|
| 236 |
+
st.session_state.messages = []
|
| 237 |
+
st.session_state.agent = ChatbotOrchestrator()
|
| 238 |
+
st.rerun()
|
| 239 |
+
|
| 240 |
+
# -----------------------------
|
| 241 |
+
# Main Content
|
| 242 |
+
# -----------------------------
|
| 243 |
+
|
| 244 |
+
# Hero Banner (Always visible & Sticky)
|
| 245 |
+
st.markdown("""
|
| 246 |
+
<div class="hero-container" role="banner">
|
| 247 |
+
<div class="hero-title">🧠 Layered AI Agent</div>
|
| 248 |
+
<div class="hero-subtitle">Architecture: Perception ➜ Cognition ➜ Action</div>
|
| 249 |
+
</div>
|
| 250 |
+
""", unsafe_allow_html=True)
|
| 251 |
+
|
| 252 |
+
# Display Chat History
|
| 253 |
+
for message in st.session_state.messages:
|
| 254 |
+
with st.chat_message(message["role"]):
|
| 255 |
+
st.markdown(message["content"], unsafe_allow_html=True)
|
| 256 |
+
|
| 257 |
+
# Chat Input Handling
|
| 258 |
+
# We handle both the chat input widget and the sidebar selection here
|
| 259 |
+
if prompt := (st.chat_input("Type your message...") or selected_prompt):
|
| 260 |
+
# User Message
|
| 261 |
+
st.session_state.messages.append({"role": "user", "content": prompt})
|
| 262 |
+
with st.chat_message("user"):
|
| 263 |
+
st.markdown(prompt)
|
| 264 |
+
|
| 265 |
+
# Assistant Response
|
| 266 |
+
with st.chat_message("assistant"):
|
| 267 |
+
with st.spinner("🧠 Thinking (Layers Active)..."):
|
| 268 |
+
response_text = asyncio.run(get_ai_response(prompt))
|
| 269 |
+
st.markdown(response_text, unsafe_allow_html=True)
|
| 270 |
+
|
| 271 |
+
st.session_state.messages.append({"role": "assistant", "content": response_text})
|
| 272 |
+
|
| 273 |
+
if selected_prompt:
|
| 274 |
+
st.rerun()
|
src/chatbot_v2/core/__init__.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
from .model import get_model_client
|
| 3 |
+
|
| 4 |
+
__all__ = ["get_model_client"]
|
src/chatbot_v2/core/model.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from common.utility.openai_model_factory import OpenAIModelFactory
|
| 2 |
+
|
| 3 |
+
def get_model_client(provider:str = "openai"):
|
| 4 |
+
if provider.lower() == "google":
|
| 5 |
+
return OpenAIModelFactory.get_model(
|
| 6 |
+
provider="google",
|
| 7 |
+
model_name="gemini-2.5-flash",
|
| 8 |
+
temperature=0
|
| 9 |
+
)
|
| 10 |
+
elif provider.lower() == "openai":
|
| 11 |
+
return OpenAIModelFactory.get_model(
|
| 12 |
+
provider="openai",
|
| 13 |
+
model_name="gpt-4o-mini",
|
| 14 |
+
temperature=0
|
| 15 |
+
)
|
| 16 |
+
elif provider.lower() == "azure":
|
| 17 |
+
return OpenAIModelFactory.get_model(
|
| 18 |
+
provider="azure",
|
| 19 |
+
model_name="gpt-4o-mini",
|
| 20 |
+
temperature=0
|
| 21 |
+
)
|
| 22 |
+
elif provider.lower() == "groq":
|
| 23 |
+
return OpenAIModelFactory.get_model(
|
| 24 |
+
provider="groq",
|
| 25 |
+
model_name="gpt-4o-mini",
|
| 26 |
+
temperature=0
|
| 27 |
+
)
|
| 28 |
+
elif provider.lower() == "ollama":
|
| 29 |
+
return OpenAIModelFactory.get_model(
|
| 30 |
+
provider="ollama",
|
| 31 |
+
model_name="gpt-4o-mini",
|
| 32 |
+
temperature=0
|
| 33 |
+
)
|
| 34 |
+
else:
|
| 35 |
+
raise ValueError(f"Unsupported provider: {provider}")
|
| 36 |
+
|
src/chatbot_v2/layers/__init__.py
ADDED
|
File without changes
|
src/chatbot_v2/layers/action.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import asyncio
|
| 2 |
+
from typing import Any, Dict, List
|
| 3 |
+
from agents import Runner
|
| 4 |
+
from common.aagents.search_agent import search_agent
|
| 5 |
+
from common.aagents.news_agent import news_agent
|
| 6 |
+
from common.aagents.yf_agent import yf_agent
|
| 7 |
+
|
| 8 |
+
class ActionLayer:
|
| 9 |
+
"""
|
| 10 |
+
The 'Hands' of the agent.
|
| 11 |
+
Responsibility: Execute specific, well-defined tools or side-effects.
|
| 12 |
+
Does NOT reason about 'why'.
|
| 13 |
+
"""
|
| 14 |
+
|
| 15 |
+
def __init__(self):
|
| 16 |
+
# Register available tools
|
| 17 |
+
self.tools = {
|
| 18 |
+
"web_search": self._tool_web_search,
|
| 19 |
+
"financial_data": self._tool_financial_data,
|
| 20 |
+
"news_search": self._tool_news_search,
|
| 21 |
+
"broadcast_research": self._tool_broadcast_research
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
async def execute(self, tool_name: str, args: Dict[str, Any]) -> str:
|
| 25 |
+
if tool_name not in self.tools:
|
| 26 |
+
return f"Error: Tool '{tool_name}' not found."
|
| 27 |
+
|
| 28 |
+
print(f"[Action] Executing tool: {tool_name} with args: {args}")
|
| 29 |
+
try:
|
| 30 |
+
return await self.tools[tool_name](**args)
|
| 31 |
+
except Exception as e:
|
| 32 |
+
return f"Error executing {tool_name}: {str(e)}"
|
| 33 |
+
|
| 34 |
+
async def _tool_web_search(self, query: str) -> str:
|
| 35 |
+
result = await Runner.run(search_agent, query)
|
| 36 |
+
return f"Web Search Result:\n{result.final_output}"
|
| 37 |
+
|
| 38 |
+
async def _tool_financial_data(self, query: str) -> str:
|
| 39 |
+
result = await Runner.run(yf_agent, query)
|
| 40 |
+
return f"Financial Data:\n{result.final_output}"
|
| 41 |
+
|
| 42 |
+
async def _tool_news_search(self, query: str) -> str:
|
| 43 |
+
result = await Runner.run(news_agent, query)
|
| 44 |
+
return f"News Result:\n{result.final_output}"
|
| 45 |
+
|
| 46 |
+
async def _tool_broadcast_research(self, query: str, include_finance: bool = True, include_news: bool = True, include_search: bool = True) -> str:
|
| 47 |
+
"""
|
| 48 |
+
Broadcasts the search query to selected specialized agents in parallel and aggregates their responses.
|
| 49 |
+
"""
|
| 50 |
+
active_agents = []
|
| 51 |
+
if include_finance: active_agents.append(("YahooFinanceAgent", Runner.run(yf_agent, query)))
|
| 52 |
+
if include_news: active_agents.append(("NewsAgent", Runner.run(news_agent, query)))
|
| 53 |
+
if include_search: active_agents.append(("WebSearchAgent", Runner.run(search_agent, query)))
|
| 54 |
+
|
| 55 |
+
if not active_agents:
|
| 56 |
+
return "No agents were selected for this query."
|
| 57 |
+
|
| 58 |
+
# Run in parallel
|
| 59 |
+
agent_names = [name for name, _ in active_agents]
|
| 60 |
+
coroutines = [coro for _, coro in active_agents]
|
| 61 |
+
|
| 62 |
+
results = await asyncio.gather(*coroutines, return_exceptions=True)
|
| 63 |
+
|
| 64 |
+
outputs = []
|
| 65 |
+
for name, res in zip(agent_names, results):
|
| 66 |
+
if isinstance(res, Exception):
|
| 67 |
+
outputs.append(f"❌ {name} Error: {str(res)}")
|
| 68 |
+
else:
|
| 69 |
+
outputs.append(f"✅ {name} Report:\n{res.final_output}")
|
| 70 |
+
|
| 71 |
+
return "\n--- START OF AGENT REPORTS ---\n\n" + "\n\n-----------------------------------\n\n".join(outputs) + "\n\n--- END OF AGENT REPORTS ---"
|
src/chatbot_v2/layers/cognition.py
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import os
|
| 3 |
+
from typing import List, Dict, Optional
|
| 4 |
+
from common.utility.openai_model_factory import OpenAIModelFactory
|
| 5 |
+
from openai import OpenAI, AsyncOpenAI
|
| 6 |
+
|
| 7 |
+
class CognitiveOutput:
|
| 8 |
+
def __init__(self, thought: str, action: Optional[str] = None, action_input: Optional[Dict] = None, final_answer: Optional[str] = None):
|
| 9 |
+
self.thought = thought
|
| 10 |
+
self.action = action
|
| 11 |
+
self.action_input = action_input
|
| 12 |
+
self.final_answer = final_answer
|
| 13 |
+
|
| 14 |
+
class CognitionLayer:
|
| 15 |
+
"""
|
| 16 |
+
The 'Brain' of the agent.
|
| 17 |
+
Uses OpenAI GPT-4o (via OpenAIModelFactory) to reason and decide which tools to use.
|
| 18 |
+
"""
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def __init__(self):
|
| 22 |
+
# Direct OpenAI client usage for maximum compatibility
|
| 23 |
+
# We circumvent the factory wrapper to access the raw client directly for JSON mode
|
| 24 |
+
api_key = os.environ.get("OPENAI_API_KEY")
|
| 25 |
+
self.client = OpenAI(api_key=api_key) if api_key else None
|
| 26 |
+
|
| 27 |
+
# If we wanted to use the factory strictly, we'd need to know the exact internal attribute
|
| 28 |
+
# self.model_wrapper = OpenAIModelFactory.get_model(...)
|
| 29 |
+
# self.client = self.model_wrapper.client # guessing 'client' vs 'openai_client'
|
| 30 |
+
# But to be safe and fix the user's error immediately:
|
| 31 |
+
from openai import AsyncOpenAI
|
| 32 |
+
self.client = AsyncOpenAI(api_key=api_key)
|
| 33 |
+
|
| 34 |
+
self.model_name = "gpt-4o"
|
| 35 |
+
|
| 36 |
+
self.system_prompt = """
|
| 37 |
+
You are the **AI Chat Orchestrator**.
|
| 38 |
+
Your goal is to provide a comprehensive, multi-perspective answer by synthesizing data from specialized sub-agents.
|
| 39 |
+
|
| 40 |
+
**Available Tools**:
|
| 41 |
+
1. `broadcast_research(query: str, include_finance: bool, include_news: bool, include_search: bool)`:
|
| 42 |
+
Broadcasts the query to specialized agents (Finance, News, Web Search). Use this for complex queries needing external info.
|
| 43 |
+
2. `web_search(query: str)`: Single web search.
|
| 44 |
+
3. `financial_data(query: str)`: Single financial check.
|
| 45 |
+
4. `news_search(query: str)`: Single news check.
|
| 46 |
+
|
| 47 |
+
**Workflow**:
|
| 48 |
+
1. **Analyze Request**: Understand the user's question.
|
| 49 |
+
2. **Determine Needs**: Decide calls are needed.
|
| 50 |
+
* **Finance**: For stock prices, market trends, company financials.
|
| 51 |
+
* **News**: For recent events, headlines.
|
| 52 |
+
* **Web Search**: For general knowledge, history.
|
| 53 |
+
3. **Action**:
|
| 54 |
+
If you need external info, PREFER `broadcast_research` to query multiple sources in parallel.
|
| 55 |
+
If it's a simple greeting or general chat not requiring data, just answer.
|
| 56 |
+
4. **Synthesize Results**:
|
| 57 |
+
When you receive tool outputs ("Agent Reports"), combine them into a clear, professional summary.
|
| 58 |
+
Do NOT simply paste the reports. Synthesize them.
|
| 59 |
+
|
| 60 |
+
**Output Format**:
|
| 61 |
+
You must output valid JSON only:
|
| 62 |
+
{
|
| 63 |
+
"thought": "Reasoning...",
|
| 64 |
+
"action": "tool_name_or_null",
|
| 65 |
+
"action_input": { "arg": "value" } or null,
|
| 66 |
+
"final_answer": "Final output to user" or null
|
| 67 |
+
}
|
| 68 |
+
"""
|
| 69 |
+
|
| 70 |
+
async def decide(self, history: List[Dict[str, str]]) -> CognitiveOutput:
|
| 71 |
+
# 1. Construct Messages
|
| 72 |
+
messages = [{"role": "system", "content": self.system_prompt}]
|
| 73 |
+
|
| 74 |
+
for entry in history:
|
| 75 |
+
role = entry['role']
|
| 76 |
+
content = entry['content']
|
| 77 |
+
|
| 78 |
+
# Map roles. 'system' in our history layer usually means tool output.
|
| 79 |
+
if role == 'user':
|
| 80 |
+
messages.append({"role": "user", "content": content})
|
| 81 |
+
elif role == 'assistant':
|
| 82 |
+
messages.append({"role": "assistant", "content": content})
|
| 83 |
+
elif role == 'system':
|
| 84 |
+
messages.append({"role": "user", "content": f"Observation/Tool Output: {content}"})
|
| 85 |
+
|
| 86 |
+
# 2. Call LLM (Async)
|
| 87 |
+
try:
|
| 88 |
+
completion = await self.client.chat.completions.create(
|
| 89 |
+
model=self.model_name,
|
| 90 |
+
messages=messages,
|
| 91 |
+
response_format={"type": "json_object"}
|
| 92 |
+
)
|
| 93 |
+
|
| 94 |
+
response_text = completion.choices[0].message.content
|
| 95 |
+
if response_text.startswith("```"):
|
| 96 |
+
response_text = response_text.strip("`").replace("json", "").strip()
|
| 97 |
+
|
| 98 |
+
data = json.loads(response_text)
|
| 99 |
+
|
| 100 |
+
return CognitiveOutput(
|
| 101 |
+
thought=data.get("thought", ""),
|
| 102 |
+
action=data.get("action"),
|
| 103 |
+
action_input=data.get("action_input"),
|
| 104 |
+
final_answer=data.get("final_answer")
|
| 105 |
+
)
|
| 106 |
+
|
| 107 |
+
except Exception as e:
|
| 108 |
+
return CognitiveOutput(
|
| 109 |
+
thought=f"Error: {str(e)}",
|
| 110 |
+
final_answer="I encountered an error processing your request."
|
| 111 |
+
)
|
src/chatbot_v2/layers/memory.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import List, Dict
|
| 2 |
+
|
| 3 |
+
class MemoryLayer:
|
| 4 |
+
"""
|
| 5 |
+
The 'Memory' of the agent.
|
| 6 |
+
Responsibility: Store and retrieve conversation history.
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
def __init__(self):
|
| 10 |
+
self.history: List[Dict[str, str]] = []
|
| 11 |
+
|
| 12 |
+
def add_entry(self, role: str, content: str):
|
| 13 |
+
self.history.append({"role": role, "content": content})
|
| 14 |
+
|
| 15 |
+
def get_history(self) -> List[Dict[str, str]]:
|
| 16 |
+
return self.history
|
| 17 |
+
|
| 18 |
+
def set_history(self, messages: List[Dict[str, str]]):
|
| 19 |
+
"""Allows initializing/overwriting memory from external source (e.g. Streamlit session)"""
|
| 20 |
+
self.history = messages
|
src/chatbot_v2/layers/perception.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import os
|
| 3 |
+
import datetime
|
| 4 |
+
from dataclasses import dataclass
|
| 5 |
+
from openai import OpenAI
|
| 6 |
+
|
| 7 |
+
@dataclass
|
| 8 |
+
class EnvironmentState:
|
| 9 |
+
user_input: str
|
| 10 |
+
timestamp: str
|
| 11 |
+
source: str
|
| 12 |
+
|
| 13 |
+
class PerceptionLayer:
|
| 14 |
+
"""
|
| 15 |
+
The 'Sensors' of the agent.
|
| 16 |
+
Responsibility: Accept raw data, clean it, validate it (Guardrails), and package it.
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
def __init__(self):
|
| 20 |
+
api_key = os.environ.get("OPENAI_API_KEY")
|
| 21 |
+
self.client = OpenAI(api_key=api_key) if api_key else None
|
| 22 |
+
self.model_name = "gpt-4o"
|
| 23 |
+
|
| 24 |
+
def perceive(self, raw_text: str, source: str = "user_input") -> EnvironmentState:
|
| 25 |
+
# 1. Basic Cleaning
|
| 26 |
+
clean_text = raw_text.strip()
|
| 27 |
+
|
| 28 |
+
# 2. Guardrail Check (Input Validation)
|
| 29 |
+
# We run this BEFORE accepting the input into the system state.
|
| 30 |
+
if self.client and clean_text:
|
| 31 |
+
validation = self._run_guardrail(clean_text)
|
| 32 |
+
if not validation["is_valid"]:
|
| 33 |
+
raise ValueError(f"Guardrail tripped: {validation['reasoning']}")
|
| 34 |
+
|
| 35 |
+
return EnvironmentState(
|
| 36 |
+
user_input=clean_text,
|
| 37 |
+
timestamp=datetime.datetime.now().isoformat(),
|
| 38 |
+
source=source
|
| 39 |
+
)
|
| 40 |
+
|
| 41 |
+
def _run_guardrail(self, text: str) -> dict:
|
| 42 |
+
"""
|
| 43 |
+
Validates if logic contains unparliamentary language.
|
| 44 |
+
"""
|
| 45 |
+
system_prompt = """
|
| 46 |
+
You are a highly efficient Guardrail Agent.
|
| 47 |
+
|
| 48 |
+
**Goal**: Validate that the user input is safe and polite.
|
| 49 |
+
|
| 50 |
+
**PASS / VALID Criteria**:
|
| 51 |
+
- The input is technical, professional, or casual.
|
| 52 |
+
- The input contains complex instructions, code features, or formatting instructions.
|
| 53 |
+
- The input is a valid request for information or action.
|
| 54 |
+
|
| 55 |
+
**FAIL / INVALID Criteria**:
|
| 56 |
+
- The input contains HATE SPEECH, EXPLICIT PROFANITY, or THREATS.
|
| 57 |
+
- The input is aggressive, insulting, or unparliamentary.
|
| 58 |
+
|
| 59 |
+
**Output Format**:
|
| 60 |
+
Return JSON only: { "is_valid": boolean, "reasoning": string }
|
| 61 |
+
|
| 62 |
+
If unsure, lean towards VALID.
|
| 63 |
+
"""
|
| 64 |
+
|
| 65 |
+
try:
|
| 66 |
+
response = self.client.chat.completions.create(
|
| 67 |
+
model=self.model_name,
|
| 68 |
+
messages=[
|
| 69 |
+
{"role": "system", "content": system_prompt},
|
| 70 |
+
{"role": "user", "content": text}
|
| 71 |
+
],
|
| 72 |
+
response_format={"type": "json_object"}
|
| 73 |
+
)
|
| 74 |
+
content = response.choices[0].message.content
|
| 75 |
+
return json.loads(content)
|
| 76 |
+
except Exception as e:
|
| 77 |
+
# On failure, fail open or closed? Let's log and allow for now to prevent blocking on API errors.
|
| 78 |
+
print(f"Guardrail check failed: {e}")
|
| 79 |
+
return {"is_valid": True, "reasoning": "Guardrail check failed, allowing input."}
|
src/chatbot_v2/patterns/__init__.py
ADDED
|
File without changes
|
src/chatbot_v2/patterns/orchestrator.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from src.chatbot_v2.layers.perception import PerceptionLayer
|
| 2 |
+
from src.chatbot_v2.layers.cognition import CognitionLayer, CognitiveOutput
|
| 3 |
+
from src.chatbot_v2.layers.action import ActionLayer
|
| 4 |
+
from src.chatbot_v2.layers.memory import MemoryLayer
|
| 5 |
+
from typing import List, Dict
|
| 6 |
+
|
| 7 |
+
class ChatbotOrchestrator:
|
| 8 |
+
"""
|
| 9 |
+
DESIGN PATTERN: ReAct (Reason + Act) - Orchestrator
|
| 10 |
+
|
| 11 |
+
Structure:
|
| 12 |
+
1. Loop:
|
| 13 |
+
- Update Perception
|
| 14 |
+
- Consult Cognition (Reasoning)
|
| 15 |
+
- If Action needed -> Call Action Layer -> Loop
|
| 16 |
+
- If Final Answer -> Return
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
def __init__(self):
|
| 20 |
+
# Initialize the 'Organs' (Layers)
|
| 21 |
+
self.perception = PerceptionLayer()
|
| 22 |
+
self.brain = CognitionLayer()
|
| 23 |
+
self.hands = ActionLayer()
|
| 24 |
+
self.memory = MemoryLayer()
|
| 25 |
+
|
| 26 |
+
async def run(self, user_input: str, external_history: List[Dict[str, str]] = None):
|
| 27 |
+
"""
|
| 28 |
+
Runs the agent loop.
|
| 29 |
+
Args:
|
| 30 |
+
user_input: The new message from the user.
|
| 31 |
+
external_history: Existing chat history (e.g. from Streamlit).
|
| 32 |
+
"""
|
| 33 |
+
# 1. Sync Memory
|
| 34 |
+
if external_history:
|
| 35 |
+
self.memory.set_history(list(external_history)) # Copy
|
| 36 |
+
|
| 37 |
+
# 2. Perception Layer (Input)
|
| 38 |
+
env_state = self.perception.perceive(user_input)
|
| 39 |
+
self.memory.add_entry("user", env_state.user_input)
|
| 40 |
+
|
| 41 |
+
# Max steps to prevent infinite loops
|
| 42 |
+
for step in range(5):
|
| 43 |
+
print(f"\n--- Step {step+1} (ReAct Loop) ---")
|
| 44 |
+
|
| 45 |
+
# 3. Cognition Layer (Reasoning)
|
| 46 |
+
history = self.memory.get_history()
|
| 47 |
+
decision: CognitiveOutput = await self.brain.decide(history)
|
| 48 |
+
|
| 49 |
+
print(f"[Think]: {decision.thought}")
|
| 50 |
+
|
| 51 |
+
# 4. Handling Decision
|
| 52 |
+
if decision.final_answer:
|
| 53 |
+
print(f"[Final Answer]: {decision.final_answer}")
|
| 54 |
+
self.memory.add_entry("assistant", decision.final_answer)
|
| 55 |
+
return decision.final_answer
|
| 56 |
+
|
| 57 |
+
if decision.action:
|
| 58 |
+
# 5. Action Layer (Execution)
|
| 59 |
+
print(f"[Action Needed]: Call {decision.action} with {decision.action_input}")
|
| 60 |
+
tool_result = await self.hands.execute(decision.action, decision.action_input or {})
|
| 61 |
+
|
| 62 |
+
# 6. Feedback Loop
|
| 63 |
+
print(f"[Observation]: {tool_result}")
|
| 64 |
+
self.memory.add_entry("system", f"Tool {decision.action} returned: {tool_result}")
|
| 65 |
+
|
| 66 |
+
return "I apologize, but I got stuck in a loop trying to answer your request."
|
src/chatbot_v2/prompts/economic_news.txt
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
##### Task
|
| 2 |
+
Provide a concise update on **major economic indicators released recently** in USA.
|
| 3 |
+
|
| 4 |
+
###### Include
|
| 5 |
+
- **Interest Rates**: Latest central bank decisions, current policy rates, and forward guidance.
|
| 6 |
+
- **Labor Market**: Unemployment rate, job creation figures, and key labor metrics (if available).
|
| 7 |
+
- **Inflation**: CPI, PCE, or other inflation data with MoM and YoY changes.
|
| 8 |
+
- **Growth Indicators**: GDP, PMIs, or industrial production released recently.
|
| 9 |
+
- **Market Reaction**: Brief impact on equities, bonds, FX, and commodities.
|
| 10 |
+
|
| 11 |
+
###### Guidelines
|
| 12 |
+
- Compare results against forecasts and prior releases
|
| 13 |
+
- Highlight notable surprises and their implications
|
| 14 |
+
- Keep the summary brief, factual, and structured
|
| 15 |
+
- **Always retrieve numerical data from primary or authoritative sources**
|
| 16 |
+
|
| 17 |
+
###### Fallback
|
| 18 |
+
- If no relevant data was released recently, explicitly state **“No major economic indicators were released during this period.”**
|
| 19 |
+
- If data is partially unavailable, summarize what is available and clearly note missing indicators.
|
| 20 |
+
- Do not infer or fabricate numbers under any circumstance.
|
| 21 |
+
|
| 22 |
+
###### Output Style
|
| 23 |
+
- Concise, factual, and well-structured
|
| 24 |
+
- Use clear bullet points or short paragraphs
|
| 25 |
+
- Avoid speculation unless explicitly labeled as interpretation
|
| 26 |
+
- **Cite data sources clearly**
|
| 27 |
+
- Use color and emoji to make it more engaging.
|
src/chatbot_v2/prompts/entertainment_updates.txt
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
##### Task
|
| 2 |
+
Provide the **top 5 recent movie or series updates** that are trending or newly released in USA.
|
| 3 |
+
|
| 4 |
+
###### For each title, include:
|
| 5 |
+
- **Title in bold** and optionally use **color or emojis** to make it fun (e.g., 🎬, 🍿, 🌟)
|
| 6 |
+
- A **short, 2–3 line snippet** that generates excitement or humor about the plot, cast, or vibe
|
| 7 |
+
- **Platform or source** where it can be watched (Netflix, Prime, Disney+, etc.)
|
| 8 |
+
- **Release date or premiere date**
|
| 9 |
+
|
| 10 |
+
###### Requirements / Guidelines
|
| 11 |
+
- Focus on **recent releases** (last 2–4 weeks) or currently trending content
|
| 12 |
+
- Keep the tone **fun, witty, and engaging**, like a friend recommending a show
|
| 13 |
+
- Use **emojis liberally** to emphasize excitement, genre, or humor
|
| 14 |
+
- Call out the **main actors and actresses** to build the interest
|
| 15 |
+
- Where possible, add a **light humorous quip or pun** about the movie/series
|
| 16 |
+
- If color is supported, use HTML span tags, e.g., `<span style="color:orange">Title</span>` for emphasis
|
| 17 |
+
|
| 18 |
+
###### Fallback
|
| 19 |
+
- If fewer than 5 titles are available, provide what is available and indicate:
|
| 20 |
+
**“Only X recent releases found.”**
|
| 21 |
+
- Do not fabricate platforms or release dates — only use verified sources
|
| 22 |
+
|
| 23 |
+
###### Output Style
|
| 24 |
+
- List format (1–5) sorted by **popularity or release date**
|
| 25 |
+
- **Title + snippet + watch source + release date** per entry
|
| 26 |
+
- Use **color, emojis, and humor** to make the output visually appealing and fun to read
|
src/chatbot_v2/prompts/india_news.txt
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
##### Task
|
| 2 |
+
Tell me the **top 3 headlines from India**.
|
| 3 |
+
|
| 4 |
+
###### For each headline, provide:
|
| 5 |
+
- **Title in bold**
|
| 6 |
+
- A **3‑line summary**
|
| 7 |
+
- **Publish date and time**
|
| 8 |
+
- A **link to the exact source URL**
|
| 9 |
+
|
| 10 |
+
###### Requirements
|
| 11 |
+
- Use authoritative news sources (e.g., major national/regional news outlets)
|
| 12 |
+
- Headlines should be **recent (last 24 hours)**
|
| 13 |
+
- Provide timestamps in **UTC**
|
| 14 |
+
- If publish date/time is not available, indicate “Date/Time not provided”
|
| 15 |
+
|
| 16 |
+
###### Fallback
|
| 17 |
+
- If fewer than 3 headlines are found, provide what is available and state:
|
| 18 |
+
**“Only X recent headlines found for India.”**
|
| 19 |
+
- Do not fabricate headlines, dates, or URLs
|
| 20 |
+
|
| 21 |
+
###### Output Style
|
| 22 |
+
- Structured list sorted by **most recent first**
|
| 23 |
+
- Clear and concise formatting as requested
|
| 24 |
+
- Use color and emoji to make it more engaging.
|
| 25 |
+
- Use `<span style="color:...">` for coloring the title if the renderer supports it
|
| 26 |
+
- Keep the output **concise, factual, and visually engaging**
|
src/chatbot_v2/prompts/market_sentiment.txt
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
##### Task
|
| 2 |
+
Act as a **Senior Market Analyst** and provide a concise market sentiment update for the **US Stock Market / S&P 500**.
|
| 3 |
+
|
| 4 |
+
###### Steps
|
| 5 |
+
1. **Data Gathering**: Search for the **top 5 financial news headlines** from the last 24 hours related to the [US Stock Market / S&P 500].
|
| 6 |
+
2. **Market Check**: Retrieve the **current value** and **today’s percentage change** for:
|
| 7 |
+
- **S&P 500 (SPX)**
|
| 8 |
+
- **VIX (Volatility Index)**
|
| 9 |
+
3. **Synthesis**: Based on the **tone of the news headlines** and the **index performance**, determine whether the **current market sentiment** is:
|
| 10 |
+
- **Bullish**
|
| 11 |
+
- **Bearish**
|
| 12 |
+
- **Neutral**
|
| 13 |
+
4. **Output**: Provide a:
|
| 14 |
+
- **Sentiment Score (1–10)**
|
| 15 |
+
- **Top 3 key drivers** influencing this sentiment
|
| 16 |
+
|
| 17 |
+
###### Guidelines
|
| 18 |
+
- Prioritize **reliable financial news sources** (e.g., Bloomberg, Reuters, WSJ, CNBC)
|
| 19 |
+
- Use **accurate, real-time market data** for indices
|
| 20 |
+
- Base sentiment on both **news tone** and **market movement**
|
| 21 |
+
- Avoid subjective or unsupported judgments
|
| 22 |
+
|
| 23 |
+
###### Fallback
|
| 24 |
+
- If no relevant financial headlines are found in the last 24 hours, clearly state:
|
| 25 |
+
**“No significant market news available in the last 24 hours.”**
|
| 26 |
+
- If either index value or change is unavailable, report available data and note missing values explicitly
|
| 27 |
+
- Do not invent or estimate values — only use verified data
|
| 28 |
+
|
| 29 |
+
###### Output Style
|
| 30 |
+
- Concise, factual, and structured
|
| 31 |
+
- Use clear bullet points or short paragraphs
|
| 32 |
+
- Include numerical values and data timestamps
|
| 33 |
+
- Provide **sources for headlines and index data**
|
| 34 |
+
- Use color and emoji to make it more engaging.
|
src/chatbot_v2/prompts/news_headlines.txt
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
##### Task
|
| 2 |
+
Tell me the **top 3 USA headlines**. Use **emojis** and, where supported, **HTML color tags** to make the output engaging.
|
| 3 |
+
|
| 4 |
+
###### For each headline, provide:
|
| 5 |
+
- **Title in bold** and optionally in color, e.g., `<span style="color:blue">Title</span>`
|
| 6 |
+
- A **3-line summary** with an emoji indicating the type of news:
|
| 7 |
+
- 📰 Politics
|
| 8 |
+
- 💼 Business
|
| 9 |
+
- 🌎 World
|
| 10 |
+
- ⚡ Breaking news
|
| 11 |
+
- **Publish date and time** (UTC)
|
| 12 |
+
- A **link to the exact source URL**
|
| 13 |
+
|
| 14 |
+
###### Requirements
|
| 15 |
+
- Use **credible news sources** (Reuters, AP, BBC, Guardian, etc.)
|
| 16 |
+
- Headlines should be **recent (last 24 hours)**
|
| 17 |
+
- If publish time is unavailable, indicate **“Time not provided”**
|
| 18 |
+
|
| 19 |
+
###### Fallback
|
| 20 |
+
- If fewer than 3 headlines are found, state:
|
| 21 |
+
**“Only X recent headlines found for the USA.”**
|
| 22 |
+
- Do not fabricate headlines, dates, or URLs
|
| 23 |
+
|
| 24 |
+
###### Output Style
|
| 25 |
+
- Structured list sorted by **most recent first**
|
| 26 |
+
- Use emojis consistently to indicate news type
|
| 27 |
+
- Use `<span style="color:...">` for coloring the title if the renderer supports it
|
| 28 |
+
- Keep the output **concise, factual, and visually engaging**
|
src/chatbot_v2/prompts/odia_news.txt
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
##### Task
|
| 2 |
+
Tell me the **top 3 headlines from Odisha**.
|
| 3 |
+
|
| 4 |
+
###### For each headline, provide:
|
| 5 |
+
- **Title in bold**
|
| 6 |
+
- A **3‑line summary**
|
| 7 |
+
- **Publish date and time**
|
| 8 |
+
- A **link to the exact source URL**
|
| 9 |
+
|
| 10 |
+
###### Requirements
|
| 11 |
+
- Use authoritative news sources (e.g., major national/regional news outlets)
|
| 12 |
+
- Headlines should be **recent (last 24 hours)**
|
| 13 |
+
- Provide timestamps in **UTC**
|
| 14 |
+
- If publish date/time is not available, indicate “Date/Time not provided”
|
| 15 |
+
|
| 16 |
+
###### Fallback
|
| 17 |
+
- If fewer than 3 headlines are found, provide what is available and state:
|
| 18 |
+
**“Only X recent headlines found for Odisha.”**
|
| 19 |
+
- Do not fabricate headlines, dates, or URLs
|
| 20 |
+
|
| 21 |
+
###### Output Style
|
| 22 |
+
- Structured list sorted by **most recent first**
|
| 23 |
+
- Clear and concise formatting as requested
|
| 24 |
+
- Use color and emoji to make it more engaging.
|
| 25 |
+
- Use `<span style="color:...">` for coloring the title if the renderer supports it
|
| 26 |
+
- Keep the output **concise, factual, and visually engaging**
|
src/chatbot_v2/prompts/trade_recommendation.txt
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
##### Task
|
| 2 |
+
Recommend **three option spreads** with **>80% probability of profit**. Perform a thorough analysis of each underlying’s **3-month price trend** and current **market sentiment** before selecting spreads.
|
| 3 |
+
|
| 4 |
+
###### Steps
|
| 5 |
+
1. **Stock selection & analysis**
|
| 6 |
+
- Analyze the **last 3 months** of price action (trend, volatility, support/resistance).
|
| 7 |
+
- Assess market sentiment from the **last 7 days** of headlines and social/analyst tone.
|
| 8 |
+
2. **Spread construction**
|
| 9 |
+
- For each of the **3 recommended spreads**, specify:
|
| 10 |
+
- **Underlying ticker**
|
| 11 |
+
- **Spread type** (e.g., bull put, bear call, iron condor)
|
| 12 |
+
- **Exact expiry date** (YYYY-MM-DD)
|
| 13 |
+
- **Each leg**: side (sell/buy), option type (put/call), **strike price**
|
| 14 |
+
- **Premium entry**: exact net credit/debit per share (use live bid/ask midpoint)
|
| 15 |
+
- **Position size guidance** (risk per trade as % of portfolio) — optional
|
| 16 |
+
3. **Probability & rationale**
|
| 17 |
+
- Provide a **quantitative probability of profit (%)** (clearly state model/method used).
|
| 18 |
+
- Give a concise **rationale** linking 3-month trend, implied volatility, and sentiment to the spread choice.
|
| 19 |
+
- Show key supporting numbers: current spot, IV30, recent volatility, and relevant news headlines (with timestamps).
|
| 20 |
+
|
| 21 |
+
###### Requirements / Guidelines
|
| 22 |
+
- Target **>80% probability of profit** for each spread. Explain how the probability was computed (IV-based log-normal, normal approximation, or risk-neutral model).
|
| 23 |
+
- **Always** use live option-chain quotes (bid/ask midpoint) and authoritative sources for prices/IV (e.g., exchange data, major market data providers).
|
| 24 |
+
- Compare outcomes **vs. forecasts / recent range** and note any idiosyncratic risk (earnings, events).
|
| 25 |
+
- Include **exact timestamps** (UTC) for all quoted prices.
|
| 26 |
+
- Provide **sources** for price, IV, and headlines.
|
| 27 |
+
|
| 28 |
+
###### Fallback
|
| 29 |
+
- If live option-chain or price data is unavailable, state: **“Live market data unavailable — cannot generate exact strike/premium. Provide analysis based on most recent available snapshot.”**
|
| 30 |
+
- If sentiment or 3-month history is incomplete, present what is available and **explicitly list missing items**.
|
| 31 |
+
- **Do not fabricate** strikes, premiums, probabilities, or news — only use verified data.
|
| 32 |
+
|
| 33 |
+
###### Output Style
|
| 34 |
+
- For each spread, use a compact block with:
|
| 35 |
+
- Ticker — Spread type — Expiry (YYYY-MM-DD) — Net premium — PO P (%)
|
| 36 |
+
- Legs: bullet list of exact leg details (sell/buy, put/call, strike, premium)
|
| 37 |
+
- Rationale: 2–3 short sentences linking trend & sentiment to the trade
|
| 38 |
+
- Sources & timestamps
|
| 39 |
+
- Keep language concise, factual, and machine/agent friendly for downstream parsing.
|
| 40 |
+
- Use color and emoji to make it more engaging.
|
src/chatbot_v2/prompts/upcoming_earnings.txt
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
##### Task
|
| 2 |
+
Search for **upcoming critical earnings announcements** in the stock market.
|
| 3 |
+
|
| 4 |
+
###### Include
|
| 5 |
+
- **Ticker**
|
| 6 |
+
- **Company name**
|
| 7 |
+
- **Earnings date & time**
|
| 8 |
+
- **Expected EPS & revenue consensus**
|
| 9 |
+
- **Last quarter’s actual EPS & revenue**
|
| 10 |
+
- **Implied volatility trend ahead of earnings**
|
| 11 |
+
|
| 12 |
+
###### Requirements / Guidelines
|
| 13 |
+
- Focus on **high‑impact names** (large cap, high volume, sector leaders)
|
| 14 |
+
- Include **earnings expected within the next 7 calendar days**
|
| 15 |
+
- Use **primary/authoritative sources** for earnings dates and estimates (e.g., exchange calendars, Bloomberg/Refinitiv/Estimize)
|
| 16 |
+
- Show **timestamped data** (UTC)
|
| 17 |
+
|
| 18 |
+
###### Fallback
|
| 19 |
+
- If no critical earnings are found in the next 7 days, state:
|
| 20 |
+
**“No upcoming critical earnings announcements found within the specified period.”**
|
| 21 |
+
- If consensus estimates are unavailable, list the earnings date/time and note missing metrics.
|
| 22 |
+
|
| 23 |
+
###### Output Style
|
| 24 |
+
- Structured list sorted by **earnings date**
|
| 25 |
+
- Use clear bullet points or short paragraphs
|
| 26 |
+
- Provide **sources** for each item
|
| 27 |
+
- Use color and emoji to make it more engaging.
|
src/chatbot_v2/trace_config.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# trace_config.py
|
| 2 |
+
import openai
|
| 3 |
+
from langsmith.wrappers import wrap_openai
|
| 4 |
+
import os
|
| 5 |
+
|
| 6 |
+
print("🔌 APPLYING LANGSMITH TRACE PATCH...")
|
| 7 |
+
|
| 8 |
+
# 1. Save original classes
|
| 9 |
+
_OriginalOpenAI = openai.OpenAI
|
| 10 |
+
_OriginalAsyncOpenAI = openai.AsyncOpenAI
|
| 11 |
+
|
| 12 |
+
# 2. Define the shim
|
| 13 |
+
def PatchedOpenAI(*args, **kwargs):
|
| 14 |
+
print("✨ Creating Wrapped OpenAI Client (Sync)") # Debug print
|
| 15 |
+
client = _OriginalOpenAI(*args, **kwargs)
|
| 16 |
+
return wrap_openai(client)
|
| 17 |
+
|
| 18 |
+
def PatchedAsyncOpenAI(*args, **kwargs):
|
| 19 |
+
print("✨ Creating Wrapped OpenAI Client (Async)") # Debug print
|
| 20 |
+
client = _OriginalAsyncOpenAI(*args, **kwargs)
|
| 21 |
+
return wrap_openai(client)
|
| 22 |
+
|
| 23 |
+
# 3. Apply patch
|
| 24 |
+
openai.OpenAI = PatchedOpenAI
|
| 25 |
+
openai.AsyncOpenAI = PatchedAsyncOpenAI
|
| 26 |
+
|
| 27 |
+
from langsmith import traceable
|
| 28 |
+
|
| 29 |
+
# You can't decorate the class directly with @traceable,
|
| 30 |
+
# but you can use this helper to wrap all methods:
|
| 31 |
+
|
| 32 |
+
def instrument_class(cls):
|
| 33 |
+
for attr_name, attr_value in cls.__dict__.items():
|
| 34 |
+
if callable(attr_value) and not attr_name.startswith("__"):
|
| 35 |
+
setattr(cls, attr_name, traceable(attr_value, run_type="tool"))
|
| 36 |
+
return cls
|
| 37 |
+
|
uv.lock
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|