diff --git a/src/App/src/App.tsx b/src/App/src/App.tsx index cfd69cafb..229db1442 100644 --- a/src/App/src/App.tsx +++ b/src/App/src/App.tsx @@ -234,16 +234,6 @@ const Dashboard: React.FC = () => { const [ASSISTANT, TOOL, ERROR, USER] = ["assistant", "tool", "error", "user"]; - const getLastRagResponse = (messages: ChatMessage[]) => { - const lastAssistantObj = [...messages] - .reverse() - .find((obj) => obj.role === ASSISTANT && typeof obj.content === "string"); - if (typeof lastAssistantObj?.content === "string") { - return lastAssistantObj.content.trim(); - } - return null; - }; - const onSelectConversation = async (id: string) => { if (!id) { console.error("No conversation ID found"); @@ -257,10 +247,6 @@ const Dashboard: React.FC = () => { type: actionConstants.UPDATE_SELECTED_CONV_ID, payload: id, }); - dispatch({ - type: actionConstants.SET_LAST_RAG_RESPONSE, - payload: null, - }); try { const responseMessages = await historyRead(id); @@ -273,11 +259,7 @@ const Dashboard: React.FC = () => { }, }); } - const lastRagResponse = getLastRagResponse(responseMessages); - dispatch({ - type: actionConstants.SET_LAST_RAG_RESPONSE, - payload: lastRagResponse, - }); + } catch (error) { console.error("Error fetching conversation messages:", error); } finally { diff --git a/src/App/src/api/api.ts b/src/App/src/api/api.ts index 7d6c4f3f9..8199dcda0 100644 --- a/src/App/src/api/api.ts +++ b/src/App/src/api/api.ts @@ -287,9 +287,8 @@ export async function callConversationApi( "X-Ms-Client-Principal-Id": userId || "", }, body: JSON.stringify({ - messages: options.messages, - conversation_id: options.id, - last_rag_response: options.last_rag_response + query: options.query, + conversation_id: options.id }), signal: abortSignal, }); @@ -432,42 +431,6 @@ export const historyEnsure = async (): Promise => { return response; }; -export const historyGenerate = async ( - options: ConversationRequest, - abortSignal: AbortSignal, - convId?: string -): Promise => { - let body; - if (convId) { - body = JSON.stringify({ - conversation_id: convId, - messages: options.messages, - }); - } else { - body = JSON.stringify({ - messages: options.messages, - }); - } - const userId = getUserIdFromLocalStorage(); - const response = await fetch(`${baseURL}/history/generate`, { - method: "POST", - headers: { - "Content-Type": "application/json", - "X-Ms-Client-Principal-Id": userId || "", - }, - body: body, - signal: abortSignal, - }) - .then((res) => { - return res; - }) - .catch((_err) => { - console.error("There was an issue fetching your data."); - return new Response(); - }); - return response; -}; - export const fetchCitationContent = async (body: any) => { try { const response = await fetch(`${baseURL}/api/fetch-azure-search-content`, { diff --git a/src/App/src/components/Chat/Chat.tsx b/src/App/src/components/Chat/Chat.tsx index baf7176ff..e1db0679b 100644 --- a/src/App/src/components/Chat/Chat.tsx +++ b/src/App/src/components/Chat/Chat.tsx @@ -63,8 +63,8 @@ const Chat: React.FC = ({ } }, []); - const saveToDB = async (messages: ChatMessage[], convId: string, reqType: string = 'Text') => { - if (!convId || !messages.length) { + const saveToDB = async (newMessages: ChatMessage[], convId: string, reqType: string = 'Text') => { + if (!convId || !newMessages.length) { return; } const isNewConversation = reqType !== 'graph' ? !state.selectedConversationId : false; @@ -73,14 +73,14 @@ const Chat: React.FC = ({ payload: true, }); - if (((reqType !== 'graph' && reqType !== 'error') && messages[messages.length - 1].role !== ERROR) && isCharthDisplayDefault ){ + if (((reqType !== 'graph' && reqType !== 'error') && newMessages[newMessages.length - 1].role !== ERROR) && isCharthDisplayDefault ){ setIsChartLoading(true); setTimeout(()=>{ - makeApiRequestForChart('show in a graph by default', convId, messages[messages.length - 1].content as string) + makeApiRequestForChart('show in a graph by default', convId, newMessages[newMessages.length - 1].content as string) },5000) } - await historyUpdate(messages, convId) + await historyUpdate(newMessages, convId) .then(async (res) => { if (!res.ok) { if (!messages) { @@ -96,7 +96,7 @@ const Chat: React.FC = ({ const newConversation: Conversation = { id: responseJson?.data?.conversation_id, title: responseJson?.data?.title, - messages: messages, + messages: state.chat.messages, date: responseJson?.data?.date, updatedAt: responseJson?.data?.date, }; @@ -218,10 +218,7 @@ const Chat: React.FC = ({ const request: ConversationRequest = { id: conversationId, - messages: [...state.chat.messages, newMessage].filter( - (messageObj) => messageObj.role !== ERROR - ), - last_rag_response: lrg + query: question }; const streamMessage: ChatMessage = { @@ -271,7 +268,7 @@ const Chat: React.FC = ({ content: errorMsg, date: new Date().toISOString(), }; - updatedMessages = [...state.chat.messages, newMessage, errorMessage]; + updatedMessages = [newMessage, errorMessage]; dispatch({ type: actionConstants.UPDATE_MESSAGES, payload: [errorMessage], @@ -294,11 +291,7 @@ const Chat: React.FC = ({ parsedChartResponse.object as unknown as ChartDataResponse, date: new Date().toISOString(), }; - updatedMessages = [ - ...state.chat.messages, - newMessage, - chartMessage, - ]; + updatedMessages = [newMessage, chartMessage]; // Update messages with the response content dispatch({ type: actionConstants.UPDATE_MESSAGES, @@ -313,11 +306,7 @@ const Chat: React.FC = ({ content: "Error while generating Chart.", date: new Date().toISOString(), }; - updatedMessages = [ - ...state.chat.messages, - newMessage, - chartMessage, - ]; + updatedMessages = [newMessage, chartMessage]; dispatch({ type: actionConstants.UPDATE_MESSAGES, payload: [chartMessage], @@ -357,13 +346,9 @@ const Chat: React.FC = ({ console.log("Caught with an error while chat and save", e); if (abortController.signal.aborted) { if (streamMessage.content) { - updatedMessages = [ - ...state.chat.messages, - newMessage, - ...[streamMessage], - ]; + updatedMessages = [newMessage, streamMessage]; } else { - updatedMessages = [...state.chat.messages, newMessage]; + updatedMessages = [newMessage]; } console.log( "@@@ Abort Signal detected: Formed updated msgs", @@ -429,13 +414,7 @@ const Chat: React.FC = ({ const request: ConversationRequest = { id: conversationId, - messages: [...state.chat.messages, newMessage].filter( - (messageObj) => messageObj.role !== ERROR - ), - last_rag_response: - isChartQuery(userMessage) && state.chat.lastRagResponse - ? JSON.stringify(state.chat.lastRagResponse) - : null, + query: question }; const streamMessage: ChatMessage = { @@ -550,7 +529,7 @@ const Chat: React.FC = ({ content: errorMsg, date: new Date().toISOString(), }; - updatedMessages = [...state.chat.messages, newMessage, errorMessage]; + updatedMessages = [newMessage, errorMessage]; dispatch({ type: actionConstants.UPDATE_MESSAGES, payload: [errorMessage], @@ -593,11 +572,7 @@ const Chat: React.FC = ({ chartResponse as unknown as ChartDataResponse, date: new Date().toISOString(), }; - updatedMessages = [ - ...state.chat.messages, - newMessage, - chartMessage, - ]; + updatedMessages = [newMessage, chartMessage]; // Update messages with the response content dispatch({ type: actionConstants.UPDATE_MESSAGES, @@ -612,11 +587,7 @@ const Chat: React.FC = ({ content: "Error while generating Chart.", date: new Date().toISOString(), }; - updatedMessages = [ - ...state.chat.messages, - newMessage, - chartMessage, - ]; + updatedMessages = [newMessage, chartMessage]; dispatch({ type: actionConstants.UPDATE_MESSAGES, payload: [chartMessage], @@ -646,11 +617,7 @@ const Chat: React.FC = ({ content: errorMsg, date: new Date().toISOString(), }; - updatedMessages = [ - ...state.chat.messages, - newMessage, - errorMessage, - ]; + updatedMessages = [newMessage, errorMessage]; dispatch({ type: actionConstants.UPDATE_MESSAGES, payload: [errorMessage], @@ -661,15 +628,7 @@ const Chat: React.FC = ({ console.log("Error while parsing charts response", e); } } else if (!isChartResponseReceived) { - dispatch({ - type: actionConstants.SET_LAST_RAG_RESPONSE, - payload: streamMessage?.content as string, - }); - updatedMessages = [ - ...state.chat.messages, - newMessage, - ...[streamMessage], - ]; + updatedMessages = [newMessage, streamMessage]; } } if (updatedMessages[updatedMessages.length-1]?.role !== "error") { @@ -679,13 +638,9 @@ const Chat: React.FC = ({ console.log("Caught with an error while chat and save", e); if (abortController.signal.aborted) { if (streamMessage.content) { - updatedMessages = [ - ...state.chat.messages, - newMessage, - ...[streamMessage], - ]; + updatedMessages = [newMessage, streamMessage]; } else { - updatedMessages = [...state.chat.messages, newMessage]; + updatedMessages = [newMessage]; } console.log( "@@@ Abort Signal detected: Formed updated msgs", diff --git a/src/App/src/state/ActionConstants.tsx b/src/App/src/state/ActionConstants.tsx index 0468a9eee..3491fb218 100644 --- a/src/App/src/state/ActionConstants.tsx +++ b/src/App/src/state/ActionConstants.tsx @@ -22,7 +22,6 @@ export const actionConstants = { ADD_NEW_CONVERSATION_TO_CHAT_HISTORY: "ADD_NEW_CONVERSATION_TO_CHAT_HISTORY", UPDATE_APP_SPINNER_STATUS: "UPDATE_APP_SPINNER_STATUS", UPDATE_HISTORY_UPDATE_API_FLAG: "UPDATE_HISTORY_UPDATE_API_FLAG", - SET_LAST_RAG_RESPONSE: "SET_LAST_RAG_RESPONSE", UPDATE_MESSAGE_BY_ID: "UPDATE_MESSAGE_BY_ID", UPDATE_STREAMING_FLAG: "UPDATE_STREAMING_FLAG", UPDATE_CHARTS_FETCHING_FLAG: "UPDATE_CHARTS_FETCHING_FLAG", diff --git a/src/App/src/state/AppProvider.tsx b/src/App/src/state/AppProvider.tsx index 5e66fc3cc..7c812e435 100644 --- a/src/App/src/state/AppProvider.tsx +++ b/src/App/src/state/AppProvider.tsx @@ -28,7 +28,6 @@ export type AppState = { generatingResponse: boolean; messages: ChatMessage[]; userMessage: string; - lastRagResponse: string | null; isStreamingInProgress: boolean; citations: string |null; }; @@ -71,7 +70,6 @@ const initialState: AppState = { generatingResponse: false, messages: [], userMessage: "", - lastRagResponse: null, citations: "", isStreamingInProgress: false, }, @@ -187,10 +185,6 @@ export type Action = type: typeof actionConstants.UPDATE_HISTORY_UPDATE_API_FLAG; payload: boolean; } - | { - type: typeof actionConstants.SET_LAST_RAG_RESPONSE; - payload: string | null; - } | { type: typeof actionConstants.UPDATE_MESSAGE_BY_ID; payload: ChatMessage; diff --git a/src/App/src/state/AppReducer.tsx b/src/App/src/state/AppReducer.tsx index df47521ba..38ca78703 100644 --- a/src/App/src/state/AppReducer.tsx +++ b/src/App/src/state/AppReducer.tsx @@ -88,7 +88,7 @@ const appReducer = (state: AppState, action: Action): AppState => { case actionConstants.NEW_CONVERSATION_START: return { ...state, - chat: { ...state.chat, messages: [], lastRagResponse: null }, + chat: { ...state.chat, messages: [] }, selectedConversationId: "", generatedConversationId: generateUUIDv4(), }; @@ -199,14 +199,6 @@ const appReducer = (state: AppState, action: Action): AppState => { isHistoryUpdateAPIPending: action.payload, }, }; - case actionConstants.SET_LAST_RAG_RESPONSE: - return { - ...state, - chat: { - ...state.chat, - lastRagResponse: action.payload, - }, - }; case actionConstants.UPDATE_MESSAGE_BY_ID: const messageID = action.payload.id; // console.log("aaction::",action.payload) diff --git a/src/App/src/types/AppTypes.ts b/src/App/src/types/AppTypes.ts index 922c4dc9a..0db22ccdb 100644 --- a/src/App/src/types/AppTypes.ts +++ b/src/App/src/types/AppTypes.ts @@ -39,8 +39,7 @@ export type ChatMessage = { export type ConversationRequest = { id?: string; - messages: ChatMessage[]; - last_rag_response: string | null; + query: string; }; export type AskResponse = { @@ -146,10 +145,6 @@ export type HistoryMetaData = { export type ParsedChunk = { error?: string; - id: string; - model: string; - created: number; - object: string; choices: [ { messages: [ @@ -158,10 +153,8 @@ export type ParsedChunk = { role: string; } ]; - history_metadata: object; } ]; - "apim-request-id": string; }; export type ToolMessageContent = { diff --git a/src/api/api/api_routes.py b/src/api/api/api_routes.py index f93203be5..80cf483a2 100644 --- a/src/api/api/api_routes.py +++ b/src/api/api/api_routes.py @@ -117,9 +117,9 @@ async def conversation(request: Request): # Get the request JSON and last RAG response from the client request_json = await request.json() conversation_id = request_json.get("conversation_id") - query = request_json.get("messages")[-1].get("content") + query = request_json.get("query") chat_service = ChatService(request=request) - result = await chat_service.stream_chat_request(request_json, conversation_id, query) + result = await chat_service.stream_chat_request(conversation_id, query) track_event_if_configured( "ChatStreamSuccess", {"conversation_id": conversation_id, "query": query} diff --git a/src/api/api/history_routes.py b/src/api/api/history_routes.py index 011e1ec6e..a16ef459d 100644 --- a/src/api/api/history_routes.py +++ b/src/api/api/history_routes.py @@ -43,32 +43,6 @@ history_service = HistoryService() -@router.post("/generate") -async def add_conversation(request: Request): - try: - authenticated_user = get_authenticated_user_details( - request_headers=request.headers) - user_id = authenticated_user["user_principal_id"] - - # Parse request body - request_json = await request.json() - - response = await history_service.add_conversation(user_id, request_json) - track_event_if_configured("ConversationCreated", { - "user_id": user_id, - "request": request_json, - }) - return response - - except Exception as e: - logger.exception("Exception in /generate: %s", str(e)) - span = trace.get_current_span() - if span is not None: - span.record_exception(e) - span.set_status(Status(StatusCode.ERROR, str(e))) - return JSONResponse(content={"error": "An internal error has occurred!"}, status_code=500) - - @router.post("/update") async def update_conversation(request: Request): try: diff --git a/src/api/services/chat_service.py b/src/api/services/chat_service.py index a3321a6b5..2bb55866f 100644 --- a/src/api/services/chat_service.py +++ b/src/api/services/chat_service.py @@ -8,9 +8,6 @@ import json import logging -import time -import uuid -from types import SimpleNamespace import asyncio import random import re @@ -25,7 +22,6 @@ from cachetools import TTLCache -from helpers.utils import format_stream_response from common.config.config import Config # Constants @@ -135,11 +131,10 @@ async def stream_openai_text(self, conversation_id: str, query: str) -> Streamin ChatService.thread_cache[corrupt_key] = thread_id yield "I cannot answer this question with the current data. Please rephrase or add more details." - async def stream_chat_request(self, request_body, conversation_id, query): + async def stream_chat_request(self, conversation_id, query): """ Handles streaming chat requests. """ - history_metadata = request_body.get("history_metadata", {}) async def generate(): try: @@ -150,38 +145,17 @@ async def generate(): assistant_content += str(chunk) if assistant_content: - chat_completion_chunk = { - "id": "", - "model": "", - "created": 0, - "object": "", + # Optimized response - only send fields used by frontend + response = { "choices": [ { - "messages": [], - "delta": {}, + "messages": [ + {"role": "assistant", "content": assistant_content} + ] } - ], - "history_metadata": history_metadata, - "apim-request-id": "", + ] } - - chat_completion_chunk["id"] = str(uuid.uuid4()) - chat_completion_chunk["model"] = "rag-model" - chat_completion_chunk["created"] = int(time.time()) - chat_completion_chunk["object"] = "extensions.chat.completion.chunk" - chat_completion_chunk["choices"][0]["messages"].append( - {"role": "assistant", "content": assistant_content} - ) - chat_completion_chunk["choices"][0]["delta"] = { - "role": "assistant", - "content": assistant_content, - } - - completion_chunk_obj = json.loads( - json.dumps(chat_completion_chunk), - object_hook=lambda d: SimpleNamespace(**d), - ) - yield json.dumps(format_stream_response(completion_chunk_obj, history_metadata, "")) + "\n\n" + yield json.dumps(response) + "\n\n" except AgentException as e: error_message = str(e) @@ -201,27 +175,3 @@ async def generate(): yield json.dumps({"error": "An error occurred while processing the request."}) + "\n\n" return generate() - - async def complete_chat_request(self, query, last_rag_response=None): - """ - Completes a chat request by generating a chart from the RAG response. - """ - if not last_rag_response: - return {"error": "A previous RAG response is required to generate a chart."} - - # Process RAG response to generate chart data - chart_data = await self.process_rag_response(last_rag_response, query) - - if not chart_data or "error" in chart_data: - return { - "error": "Chart could not be generated from this data. Please ask a different question.", - "error_desc": str(chart_data), - } - - logger.info("Successfully generated chart data.") - return { - "id": str(uuid.uuid4()), - "model": "azure-openai", - "created": int(time.time()), - "object": chart_data, - } diff --git a/src/api/services/history_service.py b/src/api/services/history_service.py index 9eab4a130..a04d60d83 100644 --- a/src/api/services/history_service.py +++ b/src/api/services/history_service.py @@ -6,7 +6,6 @@ from azure.ai.agents.models import MessageRole, ListSortOrder from common.config.config import Config from common.database.cosmosdb_service import CosmosConversationClient -from helpers.chat_helper import complete_chat_request from helpers.azure_credential_utils import get_azure_credential # Configure logging @@ -123,41 +122,6 @@ async def generate_title(self, conversation_messages): return user_messages[-1]["content"][:50] return "New Conversation" - async def add_conversation(self, user_id: str, request_json: dict): - try: - conversation_id = request_json.get("conversation_id") - messages = request_json.get("messages", []) - - history_metadata = {} - - # make sure cosmos is configured - cosmos_conversation_client = self.init_cosmosdb_client() - if not cosmos_conversation_client: - raise ValueError("CosmosDB is not configured or unavailable") - - if not conversation_id: - title = await self.generate_title(messages) - conversation_dict = await cosmos_conversation_client.create_conversation(user_id, title) - conversation_id = conversation_dict["id"] - history_metadata["title"] = title - history_metadata["date"] = conversation_dict["createdAt"] - - if messages and messages[-1]["role"] == "user": - created_message = await cosmos_conversation_client.create_message(conversation_id, user_id, messages[-1]) - if created_message == "Conversation not found": - raise ValueError( - f"Conversation not found for ID: {conversation_id}") - else: - raise ValueError("No user message found") - - request_body = { - "messages": messages, "history_metadata": { - "conversation_id": conversation_id}} - return await complete_chat_request(request_body) - except Exception: - logger.exception("Error in add_conversation") - raise - async def update_conversation(self, user_id: str, request_json: dict): conversation_id = request_json.get("conversation_id") messages = request_json.get("messages", []) diff --git a/src/tests/api/api/test_history_routes.py b/src/tests/api/api/test_history_routes.py index b054f7236..41ae728d9 100644 --- a/src/tests/api/api/test_history_routes.py +++ b/src/tests/api/api/test_history_routes.py @@ -27,19 +27,6 @@ async def client(): yield ac -@pytest.mark.asyncio -@patch("auth.auth_utils.get_authenticated_user_details") -@patch("services.history_service.HistoryService.add_conversation", new_callable=AsyncMock) -@patch("common.logging.event_utils.track_event_if_configured") -async def test_add_conversation(mock_track, mock_add, mock_auth, client, headers): - mock_auth.return_value = {"user_principal_id": "user123"} - mock_add.return_value = {"result": "ok"} - - res = await client.post("/generate", json={"message": "hello"}, headers=headers) - assert res.status_code == 200 - assert res.json() == {"result": "ok"} - - @pytest.mark.asyncio @patch("auth.auth_utils.get_authenticated_user_details") @patch("services.history_service.HistoryService.update_conversation", new_callable=AsyncMock) @@ -311,8 +298,3 @@ async def test_ensure_cosmos_unknown_error(mock_track, mock_ensure, client): assert res.json()["error"] == "CosmosDB is not configured or not working" -@pytest.mark.asyncio -@patch("auth.auth_utils.get_authenticated_user_details", side_effect=Exception("auth error")) -async def test_add_conversation_exception(mock_auth, client, headers): - res = await client.post("/generate", json={"message": "hi"}, headers=headers) - assert res.status_code == 500 \ No newline at end of file diff --git a/src/tests/api/services/test_chat_service.py b/src/tests/api/services/test_chat_service.py index ffb45b4d8..09f091ff9 100644 --- a/src/tests/api/services/test_chat_service.py +++ b/src/tests/api/services/test_chat_service.py @@ -5,7 +5,6 @@ import pytest from fastapi import HTTPException, status from semantic_kernel.exceptions.agent_exceptions import AgentException as RealAgentException -from azure.ai.agents.models import MessageRole @@ -244,16 +243,8 @@ async def mock_invoke_stream(*args, **kwargs): assert "I cannot answer this question with the current data" in chunks[0] @pytest.mark.asyncio - @patch('services.chat_service.uuid.uuid4') - @patch('services.chat_service.time.time') - @patch('services.chat_service.format_stream_response') - async def test_stream_chat_request_success(self, mock_format_stream, mock_time, mock_uuid, chat_service): + async def test_stream_chat_request_success(self, chat_service): """Test successful stream chat request.""" - # Setup mocks - mock_uuid.return_value = "test-uuid" - mock_time.return_value = 1234567890 - mock_format_stream.return_value = {"formatted": "response"} - # Mock stream_openai_text async def mock_stream_openai_text(conversation_id, query): yield "Hello" @@ -261,8 +252,7 @@ async def mock_stream_openai_text(conversation_id, query): chat_service.stream_openai_text = mock_stream_openai_text - request_body = {"history_metadata": {"test": "metadata"}} - generator = await chat_service.stream_chat_request(request_body, "conv_1", "Hello") + generator = await chat_service.stream_chat_request("conv_1", "Hello") chunks = [] async for chunk in generator: @@ -272,7 +262,11 @@ async def mock_stream_openai_text(conversation_id, query): # Verify the chunks contain expected structure for chunk in chunks: chunk_data = json.loads(chunk.strip()) - assert "formatted" in chunk_data + assert "choices" in chunk_data + assert len(chunk_data["choices"]) > 0 + assert "messages" in chunk_data["choices"][0] + assert len(chunk_data["choices"][0]["messages"]) > 0 + assert chunk_data["choices"][0]["messages"][0]["role"] == "assistant" @pytest.mark.asyncio async def test_stream_chat_request_agent_exception_rate_limit(self, chat_service): @@ -285,8 +279,7 @@ async def mock_stream_openai_text_rate_limit_error(conversation_id, query): chat_service.stream_openai_text = mock_stream_openai_text_rate_limit_error - request_body = {"history_metadata": {}} - generator = await chat_service.stream_chat_request(request_body, "conv_1", "Hello") + generator = await chat_service.stream_chat_request("conv_1", "Hello") chunks = [] async for chunk in generator: @@ -309,8 +302,7 @@ async def mock_stream_openai_text_generic_error(conversation_id, query): chat_service.stream_openai_text = mock_stream_openai_text_generic_error - request_body = {"history_metadata": {}} - generator = await chat_service.stream_chat_request(request_body, "conv_1", "Hello") + generator = await chat_service.stream_chat_request("conv_1", "Hello") chunks = [] async for chunk in generator: @@ -333,8 +325,7 @@ async def mock_stream_openai_text_generic_error(conversation_id, query): chat_service.stream_openai_text = mock_stream_openai_text_generic_error - request_body = {"history_metadata": {}} - generator = await chat_service.stream_chat_request(request_body, "conv_1", "Hello") + generator = await chat_service.stream_chat_request("conv_1", "Hello") chunks = [] async for chunk in generator: @@ -345,46 +336,4 @@ async def mock_stream_openai_text_generic_error(conversation_id, query): error_data = json.loads(chunks[0].strip()) assert "error" in error_data assert "An error occurred while processing the request." == error_data["error"] - - @pytest.mark.asyncio - async def test_complete_chat_request_success(self, chat_service): - mock_chart_data = { - "type": "bar", - "data": { - "labels": ["A"], - "datasets": [{"data": [1]}] - } - } - - chat_service.process_rag_response = AsyncMock(return_value=mock_chart_data) - - result = await chat_service.complete_chat_request("Query", last_rag_response="RAG response") - - assert result["object"]["type"] == "bar" - - - @pytest.mark.asyncio - async def test_complete_chat_request_no_rag_response(self, chat_service): - """Test complete chat request without RAG response.""" - result = await chat_service.complete_chat_request("Query", last_rag_response=None) - - assert "error" in result - assert result["error"] == "A previous RAG response is required to generate a chart." - - @pytest.mark.asyncio - async def test_complete_chat_request_chart_error(self, chat_service): - chat_service.process_rag_response = AsyncMock(return_value={"error": "Chart generation failed"}) - - result = await chat_service.complete_chat_request("Query", last_rag_response="RAG response") - - assert "error" in result - - - @pytest.mark.asyncio - async def test_complete_chat_request_empty_chart_data(self, chat_service): - chat_service.process_rag_response = AsyncMock(return_value=None) - - result = await chat_service.complete_chat_request("Query", last_rag_response="RAG response") - - assert "error" in result diff --git a/src/tests/api/services/test_history_service.py b/src/tests/api/services/test_history_service.py index 59b84e5bc..5626bb78b 100644 --- a/src/tests/api/services/test_history_service.py +++ b/src/tests/api/services/test_history_service.py @@ -30,9 +30,8 @@ def history_service(mock_config_instance): with patch("services.history_service.Config", return_value=mock_config_instance): # Create patches for other dependencies used by HistoryService with patch("services.history_service.CosmosConversationClient"): - with patch("services.history_service.complete_chat_request"): - service = HistoryService() - return service + service = HistoryService() + return service @pytest.fixture @@ -146,95 +145,6 @@ async def test_generate_title_exception(self, history_service): result = await history_service.generate_title(conversation_messages) assert result == "Fallback content" - @pytest.mark.asyncio - async def test_add_conversation_new(self, history_service): - """Test adding a new conversation""" - user_id = "test-user-id" - request_json = { - "conversation_id": None, - "messages": [{"role": "user", "content": "Hello"}] - } - - mock_cosmos_client = AsyncMock() - mock_cosmos_client.create_conversation = AsyncMock( - return_value={"id": "new-conv-id", "title": "Test Title", "createdAt": "2023-01-01T00:00:00Z"} - ) - mock_cosmos_client.create_message = AsyncMock(return_value="success") - - with patch.object(history_service, "init_cosmosdb_client", return_value=mock_cosmos_client): - with patch.object(history_service, "generate_title", AsyncMock(return_value="Test Title")): - with patch("services.history_service.complete_chat_request", AsyncMock(return_value={"response": "test"})): - result = await history_service.add_conversation(user_id, request_json) - assert result == {"response": "test"} - - # Verify calls - mock_cosmos_client.create_conversation.assert_awaited_once() - mock_cosmos_client.create_message.assert_awaited_once() - - @pytest.mark.asyncio - async def test_add_conversation_existing(self, history_service): - """Test adding to an existing conversation""" - user_id = "test-user-id" - request_json = { - "conversation_id": "existing-id", - "messages": [{"role": "user", "content": "Hello"}] - } - - mock_cosmos_client = AsyncMock() - mock_cosmos_client.create_message = AsyncMock(return_value="success") - - with patch.object(history_service, "init_cosmosdb_client", return_value=mock_cosmos_client): - with patch("services.history_service.complete_chat_request", AsyncMock(return_value={"response": "test"})): - result = await history_service.add_conversation(user_id, request_json) - assert result == {"response": "test"} - - # Verify calls - mock_cosmos_client.create_message.assert_awaited_once() - - @pytest.mark.asyncio - async def test_add_conversation_cosmos_not_configured(self, history_service): - """Test adding conversation when cosmos is not configured""" - user_id = "test-user-id" - request_json = { - "conversation_id": "existing-id", - "messages": [{"role": "user", "content": "Hello"}] - } - - with patch.object(history_service, "init_cosmosdb_client", return_value=None): - with pytest.raises(ValueError, match="CosmosDB is not configured or unavailable"): - await history_service.add_conversation(user_id, request_json) - - @pytest.mark.asyncio - async def test_add_conversation_no_user_message(self, history_service): - """Test adding conversation with no user message""" - user_id = "test-user-id" - request_json = { - "conversation_id": "existing-id", - "messages": [{"role": "assistant", "content": "Hello"}] - } - - mock_cosmos_client = AsyncMock() - - with patch.object(history_service, "init_cosmosdb_client", return_value=mock_cosmos_client): - with pytest.raises(ValueError, match="No user message found"): - await history_service.add_conversation(user_id, request_json) - - @pytest.mark.asyncio - async def test_add_conversation_conversation_not_found(self, history_service): - """Test adding to a non-existent conversation""" - user_id = "test-user-id" - request_json = { - "conversation_id": "non-existent-id", - "messages": [{"role": "user", "content": "Hello"}] - } - - mock_cosmos_client = AsyncMock() - mock_cosmos_client.create_message = AsyncMock(return_value="Conversation not found") - - with patch.object(history_service, "init_cosmosdb_client", return_value=mock_cosmos_client): - with pytest.raises(ValueError, match="Conversation not found"): - await history_service.add_conversation(user_id, request_json) - @pytest.mark.asyncio async def test_update_conversation(self, history_service): """Test updating an existing conversation"""