-
-
Notifications
You must be signed in to change notification settings - Fork 20
feat: Added AI-powered ChatBot #71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,40 @@ | ||||||||
| # 🔒 Security & Privacy Documentation | ||||||||
|
|
||||||||
| ## ✅ Data Protection Status: SECURE | ||||||||
|
|
||||||||
| ### What's Protected: | ||||||||
| - ✅ **Environment Variables**: All `.env` files are gitignored | ||||||||
| - ✅ **API Keys**: Only placeholder values used | ||||||||
| - ✅ **Database Files**: SQLite files are gitignored | ||||||||
MIHIR2006 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
| - ✅ **Personal Data**: No real personal information stored | ||||||||
| - ✅ **Credentials**: All auth tokens are placeholders | ||||||||
|
|
||||||||
| ### Files That Are Safe & Ignored: | ||||||||
| ``` | ||||||||
| backend/.env # API keys, secrets | ||||||||
| backend/*.db # Database files | ||||||||
| backend/*.sqlite # SQLite databases | ||||||||
| **/.env.* # All environment variants | ||||||||
| sessions/ # Session data | ||||||||
| credentials.json # Any credential files | ||||||||
| ``` | ||||||||
|
|
||||||||
| ### What We Created: | ||||||||
| 1. **AI Chatbot Backend** - Uses mock data, no real APIs | ||||||||
| 2. **Frontend Components** - No sensitive data embedded | ||||||||
| 3. **Database Schema** - Development only, no real user data | ||||||||
| 4. **Configuration Files** - Only placeholder values | ||||||||
|
|
||||||||
| ### API Keys Used: | ||||||||
| - `OPENAI_API_KEY=your_openai_api_key_here` *(placeholder)* | ||||||||
| - `ALPHA_VANTAGE_API_KEY=your_alpha_vantage_api_key` *(placeholder)* | ||||||||
| - `SECRET_KEY=your_secret_key_here` *(placeholder)* | ||||||||
|
|
||||||||
| ### Before Production: | ||||||||
| 1. Replace all placeholder API keys with real ones | ||||||||
| 2. Use environment-specific `.env` files | ||||||||
| 3. Set up proper authentication | ||||||||
| 4. Configure production database | ||||||||
|
|
||||||||
| ## 🛡️ Your Data is 100% Secure! | ||||||||
| No personal information, real API keys, or sensitive data has been committed to git. | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a common convention and good practice to end all text files with a single newline character. This prevents issues with some tools and makes file concatenation more reliable.
Suggested change
|
||||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,118 @@ | ||||||
| from fastapi import APIRouter, HTTPException, Depends | ||||||
| from pydantic import BaseModel | ||||||
| from typing import List, Dict, Any, Optional | ||||||
| import json | ||||||
| from datetime import datetime | ||||||
| from ..services.ai_services import AIStockAnalyzer, get_real_time_stock_data, get_stock_trends | ||||||
|
|
||||||
| router = APIRouter(prefix="/api/chatbot", tags=["chatbot"]) | ||||||
|
|
||||||
| # Pydantic models | ||||||
| class ChatRequest(BaseModel): | ||||||
| message: str | ||||||
| session_id: str | ||||||
| user_id: Optional[str] = None | ||||||
|
|
||||||
| class ChatResponse(BaseModel): | ||||||
| response: str | ||||||
| data: Optional[Dict[str, Any]] = None | ||||||
| session_id: str | ||||||
| timestamp: datetime | ||||||
|
|
||||||
| # In-memory storage for demo (replace with database later) | ||||||
| chat_sessions = {} | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using an in-memory dictionary for |
||||||
|
|
||||||
| @router.post("/chat", response_model=ChatResponse) | ||||||
| async def chat_with_ai(request: ChatRequest): | ||||||
| """Main chatbot endpoint""" | ||||||
| try: | ||||||
| # Initialize AI analyzer | ||||||
| ai_analyzer = AIStockAnalyzer() | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A new instance of # At module level
# You can use @lru_cache for a simple singleton
from functools import lru_cache
@lru_cache()
def get_ai_analyzer():
return AIStockAnalyzer()
@router.post("/chat", response_model=ChatResponse)
async def chat_with_ai(request: ChatRequest, ai_analyzer: AIStockAnalyzer = Depends(get_ai_analyzer)):
# ... use ai_analyzer directly without creating a new instance ... |
||||||
|
|
||||||
| # Analyze user query | ||||||
| analysis = await ai_analyzer.analyze_stock_query(request.message) | ||||||
|
|
||||||
| # Fetch required data based on analysis | ||||||
| data = await fetch_stock_data(analysis) | ||||||
|
|
||||||
| # Generate AI response | ||||||
| response = await ai_analyzer.generate_response(request.message, data) | ||||||
|
|
||||||
| # Store chat in memory (later: save to database) | ||||||
| if request.session_id not in chat_sessions: | ||||||
| chat_sessions[request.session_id] = [] | ||||||
|
|
||||||
| chat_sessions[request.session_id].extend([ | ||||||
| { | ||||||
| "role": "user", | ||||||
| "content": request.message, | ||||||
| "timestamp": datetime.now() | ||||||
| }, | ||||||
| { | ||||||
| "role": "assistant", | ||||||
| "content": response, | ||||||
| "timestamp": datetime.now(), | ||||||
| "data": data | ||||||
| } | ||||||
| ]) | ||||||
|
|
||||||
| return ChatResponse( | ||||||
| response=response, | ||||||
| data=data, | ||||||
| session_id=request.session_id, | ||||||
| timestamp=datetime.now() | ||||||
|
Comment on lines
+49
to
+63
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are calling |
||||||
| ) | ||||||
|
|
||||||
| except Exception as e: | ||||||
| print(f"Chat error: {e}") | ||||||
| raise HTTPException(status_code=500, detail=f"Chat error: {str(e)}") | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Returning the raw exception message
Suggested change
|
||||||
|
|
||||||
| @router.get("/sessions/{session_id}") | ||||||
| async def get_chat_history(session_id: str): | ||||||
| """Get chat history for a session""" | ||||||
| if session_id in chat_sessions: | ||||||
| return {"messages": chat_sessions[session_id]} | ||||||
| return {"messages": []} | ||||||
|
|
||||||
| @router.delete("/sessions/{session_id}") | ||||||
| async def clear_chat_session(session_id: str): | ||||||
| """Clear a chat session""" | ||||||
| if session_id in chat_sessions: | ||||||
| del chat_sessions[session_id] | ||||||
| return {"message": "Session cleared"} | ||||||
|
|
||||||
| async def fetch_stock_data(analysis: Dict[str, Any]) -> Dict[str, Any]: | ||||||
| """Fetch relevant stock data based on AI analysis""" | ||||||
| data = {} | ||||||
|
|
||||||
| try: | ||||||
| if analysis["action"] == "get_price": | ||||||
| for symbol in analysis.get("symbols", [])[:5]: # Limit to 5 symbols | ||||||
| stock_data = await get_real_time_stock_data(symbol) | ||||||
| data[symbol] = stock_data | ||||||
|
|
||||||
| elif analysis["action"] == "get_trends": | ||||||
| for symbol in analysis.get("symbols", [])[:3]: # Limit to 3 symbols for trends | ||||||
| trends = await get_stock_trends(symbol, analysis.get("time_range", 30)) | ||||||
| data[f"{symbol}_trends"] = trends | ||||||
|
|
||||||
| elif analysis["action"] == "market_summary": | ||||||
| # Get summary of major indices | ||||||
| major_stocks = ["AAPL", "GOOGL", "MSFT"] | ||||||
| for symbol in major_stocks: | ||||||
| stock_data = await get_real_time_stock_data(symbol) | ||||||
| data[symbol] = stock_data | ||||||
|
|
||||||
| # If no symbols found, provide general market info | ||||||
| if not data and analysis["action"] in ["get_price", "get_trends"]: | ||||||
| data["info"] = "No specific stocks mentioned. Try asking about stocks like AAPL, GOOGL, MSFT, or TSLA." | ||||||
|
|
||||||
| except Exception as e: | ||||||
| data["error"] = f"Error fetching stock data: {str(e)}" | ||||||
|
|
||||||
| return data | ||||||
|
Comment on lines
+84
to
+113
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||
|
|
||||||
| @router.get("/health") | ||||||
| async def chatbot_health(): | ||||||
| """Health check endpoint""" | ||||||
| return {"status": "healthy", "service": "AI Stock Chatbot"} | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While these patterns are good, they can be simplified. The pattern
**/.env.*on line 82 already covers*.env.*on line 84. Similarly,**/.envon line 81 covers*.envon line 83 for projects in subdirectories. You could potentially simplify this section for better readability.