From bab442e955336df6714b4736f95885c8935a82a2 Mon Sep 17 00:00:00 2001 From: OwusuBlessing Date: Tue, 12 Aug 2025 19:32:39 +0100 Subject: [PATCH] fixed issues history --- .../__pycache__/responses.cpython-311.pyc | Bin 863 -> 863 bytes api/routes/__pycache__/chat.cpython-311.pyc | Bin 3126 -> 5836 bytes api/routes/chat.py | 55 +++++++++++++++++- main.py | 2 +- .../__pycache__/orchestrator.cpython-311.pyc | Bin 21728 -> 22586 bytes src/llm/orchestrator.py | 25 +++++++- .../chat_templates.cpython-311.pyc | Bin 16176 -> 16730 bytes src/prompts/templates/chat_templates.py | 20 ++++++- 8 files changed, 94 insertions(+), 8 deletions(-) diff --git a/api/models/__pycache__/responses.cpython-311.pyc b/api/models/__pycache__/responses.cpython-311.pyc index fedde2894bb17c466a352070798b7bcf2494d3ad..ee601844bab3dd88bdb9df621e6299db8ce85e76 100644 GIT binary patch delta 20 acmcc5cAt%VIWI340}$AE%-+Zy!VCa8)&&*- delta 20 acmcc5cAt%VIWI340}w38n6;5Rgc$%kxCK@K diff --git a/api/routes/__pycache__/chat.cpython-311.pyc b/api/routes/__pycache__/chat.cpython-311.pyc index 2aa651f6dfef40505e8c1c2db4e05c10b2d58744..6afc51e968c523173ea1f723ab35458f087e0b38 100644 GIT binary patch literal 5836 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 Q2vWXU*`K~zW>{QX202m_Ll+I zb5&IUjqev9U%M?&x&rXjWN3IN2N2X9x^obglo<|yIXbEl^CIul#>sbH8R&J$S*o8T zdv>Wv)LqDz67+_9;tUne)#M=NU)Q^lvZ`-Uk-Vn&{HJfy~V8`#6XCFF*iJ z0|*`gC(z+NPyj>!nn%Ir&Bi&C|5pSBPUlK|_4rd-3lD~Pdq8O3;#`*uiM4q_(1m6| z>rI`asn%Ek3c?IMz#JXbfWU01%>mHGf>@I5l)UR)%eZ8EH&kTPx4tS!o4b@8n_ayF zb?3NrQ_90U80D{kJPa%LfTP`h?--K;v=45!BhI@pV1IBPv`;F#Cy1I#XA_zDFfxq9 zAR@A1C6ja>=7?mN3DYu&yjENJMNdD=k-91$Pv&$)BpsQFbmmOB4)0+7>lv^wBjR-n z;V~i%8M!PGGr5Fr5OD;jvnFQF5$B1~B$^m8)442W_7UDNaahLe<8dPc){yX5b0(26 zGOUb=cQXbeL#BCWHSku($;oR8GoDD}F1m8lVSJ?UgFd%FrQRbd%UWzvi+u_$?4xN( zib5JgsFnUqJMfs~kEvD|P57eS+zi|icMQ^{ar>_^h>s2BleqPuf3G(wnF%o)F@-)M3 zetMiUI7l(f;TD?keg=CO?6tpX;mhr8%8bAr6vEO5Bf2R;M0!2cyVch^DPc$yHhSR3 zRyJ-}_y!t_SG9+zqRmcvKhq2EXnY%KV|^r?vrL4EFp{H&v%OYL!%RF2@ODNpPyaX$ z59f3%gO1=nx@3Q@k+%={p21~%#TV`JL!|;Jd{F}xp5|p)sfaalweph(+%Z^&M=KoT MyX--KLSX&>0{IvEV*mgE diff --git a/api/routes/chat.py b/api/routes/chat.py index 718b365..39501ef 100644 --- a/api/routes/chat.py +++ b/api/routes/chat.py @@ -7,7 +7,7 @@ from api.dependencies.auth import get_api_key from src.llm.orchestrator import DroneBot, Message # Adjust import as needed from src.llm.agent.flight_assesment import DroneAssessmentAgent import json - +from logger import logger router = APIRouter( prefix="/chat", tags=["chat"] @@ -20,19 +20,68 @@ async def chat_ai( ): """Chat with DroneBot using query and history.""" try: + logger.info(f"Starting chat request with query: {request.query}") + logger.info(f"History length: {len(request.history)}") + # Convert to internal Message format history = [Message(role=msg.role, content=msg.content) for msg in request.history] + logger.info(f"Converted history to internal format: {len(history)} messages") # Initialize DroneBot with history + logger.info("Initializing DroneBot...") bot = DroneBot(history=history, use_openai_as_fallback=True) + logger.info("DroneBot initialized successfully") + + # Get response from DroneBot + logger.info("Calling DroneBot.chat()...") result = bot.chat(request.query) - message = json.loads(result["final_message"]) - print(result) + logger.info(f"DroneBot response received: {result}") + + # Validate result and final_message + if not result or "final_message" not in result: + logger.error(f"Invalid result from DroneBot: {result}") + raise HTTPException( + status_code=500, + detail="Invalid response from DroneBot: missing final_message" + ) + + final_message = result["final_message"] + logger.info(f"Final message extracted: {final_message}") + + if not final_message or not isinstance(final_message, str): + logger.error(f"Final message is not a valid string: {type(final_message)} - {final_message}") + raise HTTPException( + status_code=500, + detail="Invalid response from DroneBot: final_message is not a valid string" + ) + + try: + logger.info("Attempting to parse final_message as JSON...") + message = json.loads(final_message) + logger.info("JSON parsing successful") + except json.JSONDecodeError as json_error: + logger.warning(f"JSON decode error: {json_error}") + logger.warning(f"Raw final_message: {final_message}") + # If JSON parsing fails, create a fallback structure + message = { + "message": final_message, + "options": None, + "requires_selection": False, + "end": "in_progress", + "form": {} + } + logger.info("Created fallback message structure") + + logger.info(f"Final message to return: {message}") return ChatResponse( status="success", message=message ) + except HTTPException: + logger.error("Re-raising HTTPException") + raise except Exception as e: + logger.error(f"Unexpected error in chat_ai: {str(e)}", exc_info=True) raise HTTPException( status_code=500, detail=str(e) diff --git a/main.py b/main.py index 78dade7..81a1a76 100644 --- a/main.py +++ b/main.py @@ -37,4 +37,4 @@ async def root(): if __name__ == "__main__": import uvicorn - uvicorn.run(app, host="0.0.0.0", port=5340) \ No newline at end of file + uvicorn.run(app, host="0.0.0.0", port=5120) \ No newline at end of file diff --git a/src/llm/__pycache__/orchestrator.cpython-311.pyc b/src/llm/__pycache__/orchestrator.cpython-311.pyc index d4fcc8decbdd55c8f25802814ffe8bc6caf49bfd..d09ea4807de9d08ea972f042813e2375ae7e794c 100644 GIT binary patch delta 2676 zcmaJ@du&tJ89(RR*RRBJoY-+}oY;=@sFN6+5Hgaq4On>)H#Q4NQ__thV;h<@v9seF z@|e@KsMV;%Amy|wDs+w3mQpCClZOlXe*k{t{UVnDwX|ED5lPHP0^H1ky*I~i6FB4IrR*e@LB&vM}G!g_H;4W+K{GvUIDK@?U> z;mY$#Aq3l2Kd%rrJ>Y~@g7U#Cp`eq!Rh1(ste#iAs0eGoi9}7~d^WBEddA+RQcgtT z@!+8d{n~yJ7txQu0hl_WRXMLa#^#yUF>-3)?nH*AbJKQUTXL` zarofM@c8ZF@$_(19F9utJC-WZQomyH+_reqmS)k?yr|47)OzQtv+n)&bBEH-R?*qI z*#C{mvDA23o;Gb0P1~{yNM@{Cc72vewGYB`$5wl)=q`h`RnsQX1X*x;=L5LAscJF6 zK3N|4Yin!wG^oUUs(h&}t#^xhcNXa(>H6ip;uHQ`Lb`cKY#x$$cdXT-wK0oSdXEH) zU8|5s^~FL+#4Vb@P-*nge^xav)HR&zc|WmI*LJ(EEnU|k)^(gJ%*y3v&#Kja#7=nHr|JXdQ3-m>dWYhy#|Z0QLUJX@z% z8uFeJ1?#P(o@(kH4kPp=)f|XVqi`8C2k1}v0UVRyrv3GPY?2~=Qg72X<)sLXnQ5lJ z20wGM!HCOgS;HqknltT*#3y6Xc*K7H;@O4S$w<3>uH^fIcDrxp< z5$umiYsxn6sg zF@g*T0Nveg)L#T5qZZ%?-5(@};^aJ?YPZ3*zuEp0b|uR?-sPGT%)rNhHF~nlpo)Q$ zK{Y@|#ZE*3T0eOgmR0m`Peim=fO2t!?F{*Vjy!oq@eU)e(DKf`_|Nnwoeec@>WX)s z&LWupHylo0?Of!nN5MA31xGXLU_1^lmS9wH@r1qG$R#@AJ%(qJ|MvFs_-N9*YX^Sz z0ti4Z)%xIMg_yb|GD4OP4HJ_;bORJ_3F8$AMXV zgMJxs;G1+U@P=U(V!1G}u!$qhEb<@e(rzpNHIO&y)!onI*<}3>OEEq{pBxAktS}As rXM-H09}hf^zoLI1sKehS^?OvD=dc_XWznPYTZ#X#jZ%U34ZZbWkCvtW delta 1894 zcmZ{keN0nV6u|F&?JI4elv1HB0xbiSQc5eJvLQ=Biiola1=K&(RbE9t%WfYCT5w}- z(Zy_-vqha6yKL%|edszDxBW5Ph`J9KtIg2rk|oRb-%L$Lvdm=Ly$_2naWBp9p6~N| z-g)oyv-s!JIQ5i9qek>fX?$_J%6%^N?;P$`4pMUq@md%&o1iH-i!Te}ZVuL2N)*JD z5IGNm&1N%xnMGkW_*a|qdzh0IV^jHYGDWnusMZ!oT1~}y^@Yl4)talGh_gBB zY?f@-+BZkrx6QQoUT^P>wD(8b`z7;DgE?wgJY%q5H`pTvN7UfBmgbmMpmftAW9Z3< z&K}j-<0zTdEWWTITHSP2jFh!TZDp;J@n&{GG`l2@I9-K=iMpQ*d7*+2`>Ndhb zd!a!saHA<>DGxU%C>zJt47g$cP%mxC&Qyz~)$i}Mba;Dv18}F*hE1S!*zpi7bCeYj zT9M>13^K62B0+|3h8~6yIN->^WAKq<0`CHsvnA;YH3z0RIP5If*ffjoA&QBlI9D9L z<$Mamvt=7$s%+L+uc)u$E>)=;s`<-&jlH3gzf#Fl1zum43BQy_^QCTmv>07o7i*UHGBeOy_pD`USio?0@<= zmp05wH8Myq^e2#8lfxgTx&~Y|KFxV5SvkVyLXN}7HJ>RCGVwe-U%LTcgl}tYh09bq z$J*kE9{-PlaMsER4v)j6I}4wIOYZ%+BkZYP#pChtku@%iPr#kES+*0bTZS~9n5?T` zB>PyiDTZwfwG8ZmlQWRlq~9dXOG$pFoCKOd5lb8{wVx2mb|F9_l~< diff --git a/src/llm/orchestrator.py b/src/llm/orchestrator.py index d51b0fe..1987490 100644 --- a/src/llm/orchestrator.py +++ b/src/llm/orchestrator.py @@ -169,13 +169,31 @@ class DroneBot: try: # Case 1: Response has direct string content if hasattr(response, 'content') and isinstance(response.content, str) and response.content.strip(): - return response.content.strip() + content = response.content.strip() + + # Check if the content is valid JSON + try: + json_content = json.loads(content) + # If it's valid JSON, return the message field or the entire JSON + if isinstance(json_content, dict) and "message" in json_content: + return content # Return the full JSON string + else: + return content # Return the JSON string even if no message field + except json.JSONDecodeError: + # Not JSON, return as plain text + return content # Case 2: Response has list content (complex format) elif hasattr(response, 'content') and isinstance(response.content, list): for item in response.content: if isinstance(item, dict) and item.get('type') == 'text' and item.get('text'): - return item['text'].strip() + text_content = item['text'].strip() + # Check if it's JSON + try: + json.loads(text_content) + return text_content # Return JSON string + except json.JSONDecodeError: + return text_content # Return plain text # Case 3: Response only has tool calls, no text content elif hasattr(response, 'tool_calls') and response.tool_calls: @@ -228,11 +246,14 @@ class DroneBot: response = llm_with_tools.invoke(messages) print(f"DroneBot response: {type(response).__name__}") + print(f"Response content type: {type(response.content)}") + print(f"Response content: {response.content}") if hasattr(response, 'tool_calls'): print(f" Tool calls: {len(response.tool_calls) if response.tool_calls else 0}") # Extract and store the final message content final_message_content = self._extract_final_message_content(response) + print(f"Extracted final message: {final_message_content}") self.final_message = final_message_content # Update state diff --git a/src/prompts/templates/__pycache__/chat_templates.cpython-311.pyc b/src/prompts/templates/__pycache__/chat_templates.cpython-311.pyc index 37117f73e47e16bdd1e691b22e52b0aa434d9e27..cbfdb405463843e2837012b97a836c65cb1482ef 100644 GIT binary patch delta 424 zcmZvZze@u#6vwYvy>hg5(78P6c4${Ys5HI4-le8VN#6B(+c^m0prE5*!O2x_bQTwf zihqZTIQdujCz!j6Kfnx0-V1y`pGOu~@7I;)L!;q2cC~Kqj_-T7O*yai9LMw8Z3r|c z9xwr18q#qM= zAcIT0J_7rL>yI#?rNxgjEGLUK?iGob9^^;$^*mgATe0!^qPyAaqs1a94Qxn-XoTee zrF)iTCl_a@rKY`?=Wl=O`INn^=7;r-BJ{cCKJM1gtye!Bbo1B!?pEklZ1MkoUbR2- E31H!Ge*gdg delta 97 zcmccB#JHi3Z#ge77XuL3rO(PJx0}f4$8yh>fnoC$rn9V*9|=fJ&SbnYd4n+PW-rcF e<{YJLJS-m=O4%m=aj;b^XJZ6%u#-hBKqCRhUK@n~ diff --git a/src/prompts/templates/chat_templates.py b/src/prompts/templates/chat_templates.py index 1f52d80..13853d7 100644 --- a/src/prompts/templates/chat_templates.py +++ b/src/prompts/templates/chat_templates.py @@ -2,6 +2,22 @@ def get_booking_prompt(): return f""" +## CRITICAL INSTRUCTION - YOU MUST FOLLOW THIS EXACTLY + +**YOU MUST ALWAYS RESPOND WITH VALID JSON ONLY. NO OTHER TEXT, NO EXPLANATIONS, NO MARKDOWN FORMATTING.** + +**EVERY SINGLE RESPONSE MUST BE A VALID JSON OBJECT WITH THIS EXACT STRUCTURE:** + +{{ + "message": "Your conversational response to the user", + "options": ["Option 1", "Option 2", "Option 3"] or null, + "requires_selection": true/false, + "end": "complete" | "in_progress" | "cancelled", + "form": {{}} +}} + +**DO NOT ADD ANYTHING BEFORE OR AFTER THE JSON. NO ```json, NO ```, NO EXPLANATIONS.** + ## System Instructions & Persona You are DroneBot, a professional and knowledgeable drone survey booking assistant working for a leading renewable energy inspection company. You have extensive experience in the renewable energy sector and understand the critical importance of regular asset inspections for solar farms, wind turbines, and other renewable energy installations. @@ -16,7 +32,7 @@ Your primary function is to guide users through a comprehensive booking process, You MUST ALWAYS respond with a structured JSON object containing exactly these fields: -```json + {{ "message": "Your conversational response to the user", "options": ["Option 1", "Option 2", "Option 3"] or null, @@ -24,7 +40,7 @@ You MUST ALWAYS respond with a structured JSON object containing exactly these f "end": "complete" | "in_progress" | "cancelled", "form": {{}} }} -``` + **Field Explanations:** - **message**: Your conversational response (friendly, professional tone)