fixed llm issues with response
This commit is contained in:
+2
-13
@@ -42,24 +42,13 @@ async def chat_ai(
|
||||
|
||||
# Get response from DroneBot
|
||||
logger.info("Calling DroneBot.chat()...")
|
||||
result = bot.chat(request.query)
|
||||
result = await bot.chat(request.query)
|
||||
logger.info(f"DroneBot response received: {result}")
|
||||
|
||||
final_message_json = json.loads(result["final_message"])
|
||||
# If JSON parsing fails, create a fallback structure
|
||||
# message = {
|
||||
# "message": final_message_json,
|
||||
# "options": None,
|
||||
# "requires_selection": False,
|
||||
# "end": "in_progress",
|
||||
# "form": {}
|
||||
# }
|
||||
logger.info("Created fallback message structure")
|
||||
|
||||
logger.info(f"Final message to return: {final_message_json}")
|
||||
return ChatResponse(
|
||||
status="success",
|
||||
message=final_message_json
|
||||
message=result
|
||||
)
|
||||
except HTTPException:
|
||||
logger.error("Re-raising HTTPException")
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
import os
|
||||
import json
|
||||
import asyncio
|
||||
from langchain_openai import ChatOpenAI
|
||||
from langchain_core.messages import HumanMessage, SystemMessage
|
||||
from typing import List, Optional, Dict, Any
|
||||
from typing_extensions import TypedDict, Annotated
|
||||
from src.config.llm_config import LlmConfig
|
||||
from config import Config
|
||||
from logger import logger
|
||||
|
||||
|
||||
class DroneAssessmentResponse(TypedDict):
|
||||
"""Structured response for drone assessment that matches the chat template format"""
|
||||
message: Annotated[str, "The main response message about the drone assessment from ai"]
|
||||
options: Annotated[Optional[List[str]], None, "List of options for user selection from ai"]
|
||||
requires_selection: Annotated[bool, False, "Whether the user needs to make a selection from ai"]
|
||||
end: Annotated[str, "in_progress", "The current state: 'in_progress', 'complete', 'cancelled', or other status"]
|
||||
form: Annotated[Dict[str, str], {}, "Form data with assessment results as string key-value pairs"]
|
||||
|
||||
|
||||
class structureOuputTool:
|
||||
def __init__(self):
|
||||
self.llm = ChatOpenAI(
|
||||
api_key=Config.OPENAI_API_KEY,
|
||||
model=LlmConfig.openai.models.gpt_4o,
|
||||
temperature=0.3
|
||||
)
|
||||
# Create structured output LLM
|
||||
self.structured_llm = self.llm.with_structured_output(DroneAssessmentResponse)
|
||||
|
||||
def create_assessment_prompt(self, response: dict) -> str:
|
||||
"""Create a prompt to convert AI response to structured output"""
|
||||
prompt = f"""
|
||||
Your task is to analyze the response from AI and return the structured output in the format provided.
|
||||
|
||||
RESPONSE FROM AI:
|
||||
{response}
|
||||
|
||||
IMPORTANT: Copy the exact values from the response. Do not fabricate or add anything.
|
||||
- If message is empty, return empty string
|
||||
- If options is empty, return empty list []
|
||||
- If form is empty, return empty dict {{}}
|
||||
- If requires_selection is empty, return False
|
||||
- If end is empty, return "in_progress"
|
||||
|
||||
Just convert to structured format - no additional content.
|
||||
"""
|
||||
return prompt
|
||||
|
||||
def run(self, booking_form: dict) -> dict:
|
||||
"""
|
||||
Convert booking form data to structured assessment response.
|
||||
|
||||
Args:
|
||||
booking_form (dict): Structured booking form input
|
||||
|
||||
Returns:
|
||||
dict: AI-generated structured output
|
||||
"""
|
||||
logger.info("Starting DroneAssessmentAgent run...")
|
||||
|
||||
try:
|
||||
# Generate assessment prompt
|
||||
prompt = self.create_assessment_prompt(booking_form)
|
||||
messages = [
|
||||
SystemMessage(content=prompt),
|
||||
HumanMessage(content="Please convert this booking form data into a structured assessment response.")
|
||||
]
|
||||
|
||||
# Invoke structured LLM
|
||||
logger.debug("Sending prompt to structured LLM...")
|
||||
response = self.structured_llm.invoke(messages)
|
||||
|
||||
# TypedDict response is already a dict, no need to convert
|
||||
result = response
|
||||
logger.info("Received structured LLM response")
|
||||
logger.debug(f"Structured output: {json.dumps(result, indent=2)}")
|
||||
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.exception("Error in DroneAssessmentAgent")
|
||||
return {
|
||||
"message": "Error occurred during assessment",
|
||||
"options": ["Try again", "Contact support"],
|
||||
"requires_selection": True,
|
||||
"end": "error",
|
||||
"form": {
|
||||
"error": str(e),
|
||||
"assessment_status": "failed"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async def main():
|
||||
"""Async main function to test the drone assessment agent."""
|
||||
from test2 import booking_form_input
|
||||
|
||||
logger.info("Launching DroneAssessmentAgent from main()...")
|
||||
agent = DroneAssessmentAgent()
|
||||
result = await agent.run(booking_form_input)
|
||||
|
||||
logger.info("Drone assessment completed")
|
||||
logger.debug("Final structured output:")
|
||||
logger.debug(json.dumps(result, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
+66
-18
@@ -16,7 +16,7 @@ from src.prompts.setup_prompt import setup_prompt_manager
|
||||
from src.config.llm_config import LlmConfig
|
||||
from src.llm.tools import AgenTools
|
||||
from config import Config
|
||||
|
||||
from src.llm.agent.structured_output import structureOuputTool
|
||||
prompt_manager = setup_prompt_manager()
|
||||
|
||||
|
||||
@@ -152,12 +152,14 @@ class DroneBot:
|
||||
# Initialize output variables
|
||||
self.final_message = ""
|
||||
self.final_model_used = ""
|
||||
self.structured_result = None
|
||||
|
||||
# Create tools
|
||||
self.tools = self._create_tools()
|
||||
|
||||
# Initialize prompt manager with customer metadata
|
||||
self.prompt_manager = setup_prompt_manager(customer_metadata)
|
||||
self.structure_agent = structureOuputTool()
|
||||
|
||||
print(f"DroneBot initialized")
|
||||
print(f"OpenAI fallback: {'Enabled' if use_openai_as_fallback else 'Disabled'}")
|
||||
@@ -206,7 +208,7 @@ class DroneBot:
|
||||
|
||||
# Case 3: Response only has tool calls, no text content
|
||||
elif hasattr(response, 'tool_calls') and response.tool_calls:
|
||||
return "Generating your visualization..."
|
||||
return "Calling tool..."
|
||||
|
||||
# Case 4: Empty or None content
|
||||
else:
|
||||
@@ -265,6 +267,25 @@ class DroneBot:
|
||||
print(f"Extracted final message: {final_message_content}")
|
||||
self.final_message = final_message_content
|
||||
|
||||
# Try to parse as JSON first, if it fails, use structured agent
|
||||
try:
|
||||
json.loads(final_message_content)
|
||||
print("Final message is valid JSON, no need for structured agent")
|
||||
except json.JSONDecodeError:
|
||||
print("Final message is not valid JSON, calling structured agent")
|
||||
# Since we can't await here, we'll store the raw message
|
||||
# The structured processing can happen later if needed
|
||||
try:
|
||||
# Try to run synchronously first
|
||||
structured_result = self.structure_agent.run(self.final_message)
|
||||
print(f"Structured agent result: {structured_result}")
|
||||
# Store the structured result separately, keep final_message as string
|
||||
self.structured_result = structured_result
|
||||
except Exception as e:
|
||||
print(f"Error in structured agent (sync): {str(e)}")
|
||||
# Keep the original message if structured agent fails
|
||||
pass
|
||||
|
||||
# Update state
|
||||
updated_state = {"messages": state["messages"] + [response]}
|
||||
updated_state["current_model"] = current_model_config["name"]
|
||||
@@ -392,7 +413,7 @@ class DroneBot:
|
||||
langchain_messages.append(AIMessage(content=msg.content))
|
||||
return langchain_messages
|
||||
|
||||
def chat(self, user_query: str) -> Dict[str, Any]:
|
||||
async def chat(self, user_query: str) -> Dict[str, Any]:
|
||||
"""Main method to interact with DroneBot"""
|
||||
print(f"DroneBot processing query: {user_query}")
|
||||
|
||||
@@ -431,29 +452,55 @@ class DroneBot:
|
||||
break
|
||||
|
||||
if not self.final_message:
|
||||
self.final_message = "I've processed your visualization request."
|
||||
self.final_message = "...."
|
||||
|
||||
final_response = {
|
||||
"messages": output.get("messages", []),
|
||||
"final_message": self.final_message,
|
||||
"final_model_used": self.final_model_used or output.get("current_model", "unknown"),
|
||||
"user_question": user_query
|
||||
# Check if we already have a structured result from the workflow
|
||||
if self.structured_result is not None:
|
||||
print("Using pre-computed structured result from workflow")
|
||||
structured_message = self.structured_result
|
||||
else:
|
||||
# Try to parse final_message as JSON, if it fails, use structured agent
|
||||
try:
|
||||
structured_message = json.loads(self.final_message)
|
||||
print("Final message is valid JSON, no structured agent needed")
|
||||
except json.JSONDecodeError:
|
||||
print("Final message is not valid JSON, calling structured agent")
|
||||
try:
|
||||
# Now we can properly await the async function
|
||||
structured_result = await self.structure_agent.run(self.final_message)
|
||||
print(f"Structured agent result: {structured_result}")
|
||||
# Assign the structured result to structured_message
|
||||
structured_message = structured_result
|
||||
except Exception as e:
|
||||
print(f"Error in structured agent: {str(e)}")
|
||||
structured_message = {
|
||||
"message": "Sorry i encountered an error while processing your request. Please try again.",
|
||||
"options": [],
|
||||
"requires_selection": False,
|
||||
"end": "error",
|
||||
"form": {}
|
||||
}
|
||||
|
||||
print(f"Final message: {self.final_message[:100]}...")
|
||||
return final_response
|
||||
# final_response = {
|
||||
# "messages": output.get("messages", []),
|
||||
# "final_message": structured_message,
|
||||
# "final_model_used": self.final_model_used or output.get("current_model", "unknown"),
|
||||
# "user_question": user_query
|
||||
# }
|
||||
|
||||
#print(f"Final message: {self.final_message[:100]}...")
|
||||
return structured_message
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error in DroneBot workflow execution: {str(e)}")
|
||||
return {
|
||||
"messages": [],
|
||||
"final_message": "Sorry, I encountered an error while processing your visualization request. Please try again.",
|
||||
"final_model_used": "error",
|
||||
"user_question": user_query,
|
||||
"error": str(e)
|
||||
"message": "Sorry i encountered an error while processing your request. Please try again.",
|
||||
"options": [],
|
||||
"requires_selection": False,
|
||||
"end": "error",
|
||||
"form": {}
|
||||
}
|
||||
|
||||
|
||||
# Example usage
|
||||
if __name__ == "__main__":
|
||||
# Example conversation history
|
||||
@@ -477,7 +524,8 @@ if __name__ == "__main__":
|
||||
|
||||
query = "Can we start"
|
||||
# Chat with DroneBot
|
||||
response = bot.chat(query)
|
||||
import asyncio
|
||||
response = asyncio.run(bot.chat(query))
|
||||
|
||||
print("Response:", response["final_message"])
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# terminal_chat.py
|
||||
from src.llm.orchestrator import DroneBot, Message # Adjust import path as needed
|
||||
import json
|
||||
def terminal_chat():
|
||||
async def terminal_chat():
|
||||
print("🚁 DroneBot Terminal Chat")
|
||||
print("Type 'exit' to quit.\n")
|
||||
|
||||
@@ -31,15 +30,16 @@ def terminal_chat():
|
||||
bot.history = history
|
||||
|
||||
# Get bot response
|
||||
response = bot.chat(user_input)
|
||||
response = await bot.chat(user_input)
|
||||
|
||||
# Add bot response to history
|
||||
history.append(Message(role="ai", content=response["final_message"]))
|
||||
history.append(Message(role="ai", content=json.dumps(response)))
|
||||
|
||||
# Print bot response
|
||||
response_json = json.loads(response["final_message"])
|
||||
print(f"🤖 DroneBot: {response_json}\n")
|
||||
|
||||
print(f"🤖 DroneBot: {response}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
terminal_chat()
|
||||
import asyncio
|
||||
asyncio.run(terminal_chat())
|
||||
@@ -1,7 +1,7 @@
|
||||
# terminal_chat.py
|
||||
from src.llm.orchestrator import DroneBot, Message # Adjust import path as needed
|
||||
|
||||
def terminal_chat():
|
||||
async def terminal_chat():
|
||||
print("🚁 DroneBot Terminal Chat")
|
||||
print("Type 'exit' to quit.\n")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user