22Chat API endpoints
33"""
44import logging
5+ from dotenv import load_dotenv
56from fastapi import APIRouter , HTTPException
67from fastapi .responses import StreamingResponse
78
8- from app .backend .models .api_models import ChatRequestEnhanced , ChatResponse
9+ # Ensure environment variables are loaded before importing services
10+ load_dotenv ()
11+
12+ from app .backend .models .api_models import ChatRequestEnhanced , ChatResponse , SerpTokenStatus
913from app .services .ollama import stream_ollama
1014from app .services .vllm_service import vllm_service
15+ from app .services .web_search_service import WebSearchService
16+ from app .services .web_search_crew import WebSearchOrchestrator
1117
1218logger = logging .getLogger (__name__ )
1319
14- router = APIRouter (prefix = "/api/chat" , tags = ["chat" ])
20+ router = APIRouter (tags = ["chat" ])
1521
1622
17- @router .post ("/" , response_model = ChatResponse )
23+ @router .post ("/api/chat " , response_model = ChatResponse )
1824async def chat_endpoint (request : ChatRequestEnhanced ):
1925 """Single chat message processing"""
2026 logger .info (f"Chat request - Backend: { request .backend } , Model: { request .model } " )
@@ -30,7 +36,7 @@ async def chat_endpoint(request: ChatRequestEnhanced):
3036 raise HTTPException (status_code = 500 , detail = str (e ))
3137
3238
33- @router .post ("/stream" )
39+ @router .post ("/api/chat/ stream" )
3440async def chat_stream (request : ChatRequestEnhanced ):
3541 """Stream chat responses"""
3642 logger .info (f"Streaming chat - Backend: { request .backend } , Model: { request .model } " )
@@ -105,3 +111,140 @@ async def _stream_ollama_chat(request: ChatRequestEnhanced):
105111 messages = [{"role" : "user" , "content" : request .message }]
106112 async for chunk in stream_ollama (messages , model = request .model , temperature = request .temperature ):
107113 yield chunk
114+
115+
116+ @router .post ("/api/chat/web-search" )
117+ async def chat_web_search (request : ChatRequestEnhanced ):
118+ """Process chat with optional web search integration"""
119+ logger .info (f"Web search chat request - use_web_search: { request .use_web_search } , Model: { request .model } " )
120+
121+ try :
122+ # Validate that web search is requested
123+ if not request .use_web_search :
124+ logger .info ("Web search not requested, routing to standard chat endpoint" )
125+ return await chat_stream (request )
126+
127+ # Check if SerpAPI token is configured
128+ web_search_service = WebSearchService ()
129+ token_validation = web_search_service .validate_token ()
130+
131+ if not token_validation ["valid" ]:
132+ logger .warning (f"SerpAPI token invalid: { token_validation ['message' ]} " )
133+ logger .info ("Falling back to standard chat due to invalid token" )
134+
135+ # Return error message and fallback to standard chat
136+ async def error_and_fallback ():
137+ error_msg = "SerpAPI token is required to perform Web search. Defaulting to local results.\n \n "
138+ yield error_msg
139+
140+ # Stream standard chat response
141+ async for chunk in _stream_ollama_chat (request ):
142+ yield chunk
143+
144+ return StreamingResponse (error_and_fallback (), media_type = "text/plain" )
145+
146+ # Route to web search handler
147+ return await _handle_web_search_chat (request )
148+
149+ except Exception as e :
150+ logger .error (f"Web search endpoint error: { e } " , exc_info = True )
151+ logger .info ("Falling back to standard chat due to error" )
152+
153+ # Fallback to standard chat on any error
154+ async def error_fallback ():
155+ error_msg = f"Web search error: { str (e )} . Defaulting to local results.\n \n "
156+ yield error_msg
157+
158+ async for chunk in _stream_ollama_chat (request ):
159+ yield chunk
160+
161+ return StreamingResponse (error_fallback (), media_type = "text/plain" )
162+
163+
164+ @router .get ("/api/chat/serp-status" , response_model = SerpTokenStatus )
165+ async def get_serp_token_status ():
166+ """Get SerpAPI token validation status"""
167+ logger .info ("Checking SerpAPI token status" )
168+
169+ try :
170+ web_search_service = WebSearchService ()
171+ validation = web_search_service .validate_token ()
172+
173+ if validation ["valid" ]:
174+ return SerpTokenStatus (
175+ status = "valid" ,
176+ message = validation ["message" ]
177+ )
178+ else :
179+ return SerpTokenStatus (
180+ status = "invalid" ,
181+ message = validation ["message" ]
182+ )
183+
184+ except Exception as e :
185+ logger .error (f"Error checking SerpAPI token status: { e } " )
186+ return SerpTokenStatus (
187+ status = "error" ,
188+ message = f"Error checking token status: { str (e )} "
189+ )
190+
191+
192+ async def _handle_web_search_chat (request : ChatRequestEnhanced ):
193+ """Handle web search chat request by routing to orchestrator"""
194+ logger .info ("Routing to web search orchestrator" )
195+
196+ async def event_generator ():
197+ try :
198+ # Initialize orchestrator
199+ orchestrator = WebSearchOrchestrator ()
200+
201+ # Build conversation history from request if available
202+ conversation_history = []
203+ # Note: ChatRequestEnhanced currently only has 'message', not full history
204+ # If history is needed, it would be added to the model
205+
206+ # Process query through orchestrator with streaming
207+ async for chunk in orchestrator .process_query (
208+ query = request .message ,
209+ conversation_history = conversation_history ,
210+ stream = True
211+ ):
212+ yield chunk + "\n "
213+
214+ except Exception as e :
215+ logger .error (f"Web search orchestrator error: { e } " , exc_info = True )
216+ yield f"Error during web search: { str (e )} \n "
217+ yield "Falling back to local results...\n \n "
218+
219+ # Fallback to standard chat
220+ async for chunk in _stream_ollama_chat (request ):
221+ yield chunk + "\n "
222+
223+ return StreamingResponse (event_generator (), media_type = "text/plain" )
224+
225+
226+ async def _stream_web_search_chat (request : ChatRequestEnhanced ):
227+ """Stream web search chat responses (helper for streaming)"""
228+ try :
229+ # Initialize orchestrator
230+ orchestrator = WebSearchOrchestrator ()
231+
232+ # Build conversation history from request if available
233+ conversation_history = []
234+
235+ # Process query through orchestrator with streaming
236+ async for chunk in orchestrator .process_query (
237+ query = request .message ,
238+ conversation_history = conversation_history ,
239+ stream = True
240+ ):
241+ yield chunk
242+
243+ except Exception as e :
244+ logger .error (f"Web search streaming error: { e } " , exc_info = True )
245+ yield f"Error during web search: { str (e )} \n "
246+ yield "Falling back to local results...\n \n "
247+
248+ # Fallback to standard chat
249+ async for chunk in _stream_ollama_chat (request ):
250+ yield chunk
0 commit comments