backedn chat apis and uplaod apis integrated

This commit is contained in:
OwusuBlessing
2025-02-12 00:12:02 +01:00
parent 7200de4846
commit d1ed8b9e3f
4 changed files with 126 additions and 137 deletions
+35 -14
View File
@@ -78,7 +78,7 @@ async def get_api_key(api_key_header: str = Security(api_key_header)) -> str:
class ChatRequest(BaseModel): class ChatRequest(BaseModel):
resume_url: Optional[str] = None resume_url: Optional[str] = None
query: str=None query: str=None
conversation_id: str chat_id: int
theme_id: Optional[int] = 1 theme_id: Optional[int] = 1
full_history_url: Optional[str] = None full_history_url: Optional[str] = None
form_id:Optional[int] = None form_id:Optional[int] = None
@@ -95,7 +95,7 @@ class ChatResponse(BaseModel):
class GeneratePDFRequest(BaseModel): class GeneratePDFRequest(BaseModel):
resume_url: Optional[str] = None resume_url: Optional[str] = None
conversation_id: str chat_id: int
theme_id: Optional[int] = 1 theme_id: Optional[int] = 1
full_history_url: Optional[str] = None full_history_url: Optional[str] = None
form_id:Optional[int] = None form_id:Optional[int] = None
@@ -215,7 +215,7 @@ async def chat_endpoint(
query = "Let's get started" query = "Let's get started"
response = ai_chat( response = ai_chat(
query=query, query=query,
conversation_id=request.conversation_id, conversation_id=request.chat_id,
theme_id=request.theme_id, theme_id=request.theme_id,
resume=resume_docs, resume=resume_docs,
full_history=full_history_docs, full_history=full_history_docs,
@@ -304,16 +304,16 @@ async def generate_pdf_endpoint(
# Here you would fetch the conversation data using the conversation_id # Here you would fetch the conversation data using the conversation_id
# This is a placeholder - replace with your actual conversation data fetching logic # This is a placeholder - replace with your actual conversation data fetching logic
# Get AI-generated theme content # Get AI-generated theme content
# Get AI-generated theme content
response = ai_chat( response = ai_chat(
query="NOW GENERATE THE STARTPOP FRAMEWORK", query="NOW GENERATE THE STARTPOP FRAMEWORK",
conversation_id=request.conversation_id, conversation_id=request.chat_id,
theme_id=request.theme_id, theme_id=request.theme_id,
resume=resume_docs, resume=resume_docs,
full_history=full_history_docs, full_history=full_history_docs,
form_response=form_response_docs, form_response=form_response_docs,
generate_theme="YES" generate_theme="YES"
) )
print(f"AI Response for theme: {response}") print(f"AI Response for theme: {response}")
# Ensure AI response is valid # Ensure AI response is valid
@@ -321,19 +321,40 @@ async def generate_pdf_endpoint(
raise HTTPException(status_code=500, detail="Invalid AI response format") raise HTTPException(status_code=500, detail="Invalid AI response format")
# Generate PDF # Generate PDF
response = json.loads(response) response_data = json.loads(response)
pdf_content = create_pdf(response) pdf_content = create_pdf(response_data)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"theme_{timestamp}.pdf" file_path = f"theme_{timestamp}.pdf"
print(f"Returning PDF with filename: {filename}") # Save the PDF locally temporarily
with open(file_path, "wb") as file:
file.write(pdf_content)
return Response( # Upload the PDF to S3 using the API
content=pdf_content, upload_url = f"{os.getenv('BACKEND_BASE_URL')}/v3/api/custom/theme/doc-upload?x-project={x_api_key}"
media_type="application/pdf", with open(file_path, 'rb') as file:
headers={"Content-Disposition": f'attachment; filename="{filename}"'} files = {'file': file}
) upload_response = requests.post(upload_url, files=files)
# Check if the upload was successful
if upload_response.status_code != 200:
raise HTTPException(status_code=upload_response.status_code, detail="File upload to S3 failed: " + upload_response.text)
upload_data = upload_response.json() # Get the response in JSON format
# Extract the uploaded file URL
theme_url = upload_data.get("url") # Adjust this key based on the actual API response structure
if not theme_url:
raise HTTPException(status_code=500, detail="Failed to retrieve theme URL from upload response")
# Clean up the temporary file
os.remove(file_path)
# Return JSON response with theme URL and text
return {
"theme_url": theme_url,
"theme_text": response_data
}
except Exception as e: except Exception as e:
print(f"Error generating theme: {str(e)}") print(f"Error generating theme: {str(e)}")
raise HTTPException(status_code=500, detail=f"Error: {str(e)}") raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
+52 -102
View File
@@ -7,16 +7,28 @@ from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.messages import HumanMessage, AIMessage, BaseMessage from langchain_core.messages import HumanMessage, AIMessage, BaseMessage
from langgraph.checkpoint.memory import MemorySaver from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import START, MessagesState, StateGraph from langgraph.graph import START, MessagesState, StateGraph
from utils.utils import format_questions_text
from src.prompts import chat_prompt from src.prompts import chat_prompt
from langchain_openai import ChatOpenAI from langchain_openai import ChatOpenAI
from src.models import Phase2Generation,Phase1Generation from src.models import Phase2Generation,Phase1Generation
from config import TEMPERATURE from config import TEMPERATURE
import os
import requests
import json
from dotenv import load_dotenv
from typing import List, Dict, Optional
from dataclasses import dataclass
from datetime import datetime
from langchain_core.messages import HumanMessage, AIMessage
# Load environment variables
load_dotenv()
@dataclass @dataclass
class Message: class Message:
role: str # 'human' or 'ai' role: str # 'human' or 'ai'
content: str content: str
timestamp: str timestamp: str=None
QUESTIONS_PATH = "./data/config_files/questions.json" QUESTIONS_PATH = "./data/config_files/questions.json"
with open(QUESTIONS_PATH, "r") as f: with open(QUESTIONS_PATH, "r") as f:
@@ -46,63 +58,31 @@ def setup_prompt_template(theme: int, resume: str,full_history=None,form_respons
("system", chat_prompt(theme, resume,full_history,form_response,generate_theme)), ("system", chat_prompt(theme, resume,full_history,form_response,generate_theme)),
MessagesPlaceholder(variable_name="messages") MessagesPlaceholder(variable_name="messages")
]) ])
def fetch_conversation_history(conversation_id: str) -> List[Message]:
"""
Fetch conversation history from the API using the conversation ID.
"""
x_api_key = os.getenv("BACKEND_XAPI_KEY")
base_url = os.getenv("BACKEND_BASE_URL")
url = f"{base_url}/v3/api/custom/jordan/ai-chat/get-messages/{conversation_id}?x-project={x_api_key}"
def parse_ai_response(content: str) -> Dict:
"""Parse AI response content into expected format"""
try: try:
response = json.loads(content) response = requests.get(url)
return { response.raise_for_status() # Raise an error for bad responses
"message": response.get("message", ""), data = response.json()["data"] # First JSON parse
"end": response.get("end", "no") == "yes" data = json.loads(json.loads(data))
} # Parse the API response into Message objects
except json.JSONDecodeError: messages = []
return { for item in data:
"message": content, role = item.get("role", "unknown")
"end": False content = item.get("content", "")
} timestamp = datetime.now().isoformat() # Use current timestamp if not provided
messages.append(Message(role=role, content=content))
return messages
except requests.RequestException as e:
print(f"Error fetching conversation history: {e}")
return []
def add_message(storage_path: Path, conversation_id: str, role: str, content: str) -> None:
"""Add a message to the conversation history"""
message_data = {
"role": role,
"content": content,
"timestamp": datetime.now().isoformat()
}
conversations = load_conversations(storage_path)
if conversation_id not in conversations:
conversations[conversation_id] = {"messages": []}
conversations[conversation_id]["messages"].append(message_data)
save_conversations(storage_path, conversations)
def get_conversation_history(conversation_id: str, storage_path: Path) -> List[Message]:
"""Get the conversation history"""
conversations = load_conversations(storage_path)
if conversation_id not in conversations:
return None
return [
Message(
role=msg["role"],
content=msg["content"],
timestamp=msg["timestamp"]
)
for msg in conversations[conversation_id]["messages"]
]
def load_conversations(storage_path: Path) -> Dict:
"""Load conversations from storage file"""
try:
with open(storage_path, 'r') as f:
return json.load(f)
except FileNotFoundError:
return {}
def save_conversations(storage_path: Path, conversations: Dict) -> None:
"""Save conversations to storage file"""
with open(storage_path, 'w') as f:
json.dump(conversations, f, indent=2)
def convert_to_langchain_messages(messages: List[Message]) -> List[HumanMessage | AIMessage]: def convert_to_langchain_messages(messages: List[Message]) -> List[HumanMessage | AIMessage]:
"""Convert our Message objects to LangChain message objects""" """Convert our Message objects to LangChain message objects"""
@@ -114,20 +94,19 @@ def convert_to_langchain_messages(messages: List[Message]) -> List[HumanMessage
converted_messages.append(AIMessage(content=msg.content)) converted_messages.append(AIMessage(content=msg.content))
return converted_messages return converted_messages
def ai_chat(query: str, conversation_id: str, theme_id: int, resume: str, full_history=None, form_response=None, generate_theme="NO") -> str: def ai_chat(query: str, conversation_id: str, theme_id: int, resume: str, full_history=None, form_response=None, generate_theme="NO") -> str:
"""Main chat function that processes queries and manages conversation""" """Main chat function that processes queries and manages conversation"""
storage_path = Path("conversations.json")
class State(TypedDict): class State(TypedDict):
messages: Annotated[Sequence[BaseMessage], "The messages in the conversation"] messages: List[HumanMessage | AIMessage]
language: str language: str
# Initialize model and workflow # Initialize model and workflow
model = ChatOpenAI(model=MODEL, temperature=TEMPERATURE) model = ChatOpenAI(model=MODEL, temperature=TEMPERATURE)
if generate_theme == "YES": if generate_theme == "YES":
model = model.with_structured_output(Phase2Generation) model = model.with_structured_output(Phase2Generation)
else: else:
model = model.with_structured_output(Phase1Generation) model = model.with_structured_output(Phase1Generation)
workflow = StateGraph(state_schema=State) workflow = StateGraph(state_schema=State)
def call_model(state: State): def call_model(state: State):
@@ -142,12 +121,11 @@ def ai_chat(query: str, conversation_id: str, theme_id: int, resume: str,full_hi
workflow.add_edge(START, "model") workflow.add_edge(START, "model")
workflow.add_node("model", call_model) workflow.add_node("model", call_model)
memory = MemorySaver() memory = MemorySaver()
app = workflow.compile(checkpointer=memory) app = workflow.compile(checkpointer=memory)
# Get conversation history # Fetch conversation history from the API
history = get_conversation_history(conversation_id, storage_path) history = fetch_conversation_history(conversation_id)
config = {"configurable": {"thread_id": conversation_id}} config = {"configurable": {"thread_id": conversation_id}}
language = "English" language = "English"
@@ -155,65 +133,37 @@ def ai_chat(query: str, conversation_id: str, theme_id: int, resume: str,full_hi
if not history: if not history:
# New conversation # New conversation
input_messages = [HumanMessage(content=query)] if query else [HumanMessage(content="Let's get started")] input_messages = [HumanMessage(content=query)] if query else [HumanMessage(content="Let's get started")]
output = app.invoke(
{"messages": input_messages, "language": language},
config
)
else: else:
# Existing conversation # Existing conversation
history = convert_to_langchain_messages(history) history = convert_to_langchain_messages(history)
input_messages = history + [HumanMessage(content=query)] if query else history input_messages = history + [HumanMessage(content=query)] if query else history
output = app.invoke( output = app.invoke(
{"messages": input_messages, "language": language}, {"messages": input_messages, "language": language},
config config
) )
if generate_theme == "YES": if generate_theme == "YES":
structured_message = output["messages"][0] structured_message = output["messages"][0]
output = structured_message.json(by_alias=True) # This returns a JSON string. output = structured_message.json(by_alias=True) # This returns a JSON string.
print(f"Output: {output}")
if query:
add_message(storage_path, conversation_id, "human", query)
add_message(storage_path, conversation_id, "ai", output)
else: else:
structured_message = output["messages"][0] structured_message = output["messages"][0]
output = structured_message.json() # This returns a JSON string. output = structured_message.json() # This returns a JSON string.
output = json.loads(output) output = json.loads(output)
print(output)
message = output.get("message") message = output.get("message")
print(output) print(output)
if query:
add_message(storage_path, conversation_id, "human", query)
add_message(storage_path, conversation_id, "ai", message)
print(output)
return output return output
# Example usage: # Example usage:
if __name__ == "__main__": if __name__ == "__main__":
# Sample resume #conversation_id = "12345" # Replace with the actual conversation ID
sample_resume = """ query = "Hello let us continue"
John Doe theme_id = 1
EMT-B Certified resume = "Emergency Response Specialist"
5 years experience as volunteer firefighter conversation_id = 1
Bachelor's in Fire Science response = ai_chat(query, conversation_id, theme_id, resume)
""" print(response)
# Sample conversation
conversation_id = "12345"
theme_id = 1 # Customer Service theme
# Start conversation
# Continue conversation
follow_up = ai_chat(
query="What was my last questions?",
conversation_id=conversation_id,
theme_id=theme_id,
resume=sample_resume
)
print("AI:", follow_up)
+4 -1
View File
@@ -329,7 +329,7 @@ def chat_prompt(theme,resume,full_history=None, form_response=None,generate_them
NOTE: DO NOT KEEP THE CONVERSATION excessively long , CAREFULL ANALYZE USER RESUME AND THE PROVIDED EXAMPLES QUESTIONS AND ALL CONTEXT , ASK RELEVANT QUESTION BASED ON THE THEME AND THAT IS ALL NOTE: DO NOT KEEP THE CONVERSATION excessively long , CAREFULL ANALYZE USER RESUME AND THE PROVIDED EXAMPLES QUESTIONS AND ALL CONTEXT , ASK RELEVANT QUESTION BASED ON THE THEME AND THAT IS ALL
Follow these instructions exactly: FOLLOW THESE INSTRUCTIONS STTRICTLY:
You may receive chat history that includes a previously generated STARTPOP framework, and the user may provide feedback on it. Use this feedback to engage with the user and ask clarifying questions as needed. You may receive chat history that includes a previously generated STARTPOP framework, and the user may provide feedback on it. Use this feedback to engage with the user and ask clarifying questions as needed.
@@ -346,6 +346,9 @@ def chat_prompt(theme,resume,full_history=None, form_response=None,generate_them
If the user says, "I just want to make the actions a little bit clearer," a good response is: "Okay noted, would you like to generate your theme now?" (with the appropriate "end" and "pop_theme_generation" values). If the user says, "I just want to make the actions a little bit clearer," a good response is: "Okay noted, would you like to generate your theme now?" (with the appropriate "end" and "pop_theme_generation" values).
If the user responds "yes," a good response is: "Go ahead and click on the theme generation button, thanks." If the user responds "yes," a good response is: "Go ahead and click on the theme generation button, thanks."
"NEVER RETURN THE STARTPOP FRAME WORK FORMAT PLEASE" , SEE EXAMPLE RESPONSES I GAVE
WHENE THERE IS THERE IS NEED TO GENERATE THE STARTPOP THEME FRAME , JUST TELL THEM TO GO AHEAD AND CLICK ON button
Strictly adhere to these guidelines Strictly adhere to these guidelines
""" """
return prompt return prompt
+21 -6
View File
@@ -1,13 +1,28 @@
import os import os
import requests import requests
import json import json
from typing import List
from dotenv import load_dotenv from dotenv import load_dotenv
load_dotenv() load_dotenv()
doc_id = 2
x_api_key = os.getenv("BACKEND_XAPI_KEY")
url = f"{os.getenv('BACKEND_BASE_URL')}/v3/api/custom/theme-document/answer/{doc_id}?x-project={x_api_key}"
result = requests.get(url) from src.llm import ai_chat
response_json = result.json() # Return response in JSON format
print(response_json)
#conversation_id = "12345" # Replace with the actual conversation ID
query = "Hello let us continue"
theme_id = 1
resume = "Emergency Response Specialist"
conversation_id = 1
response = ai_chat(query, conversation_id, theme_id, resume)
print(response)
"""
with open(file_path, 'rb') as file:
files = {'file': file}
response = requests.post(upload_url, files=files)
response.raise_for_status() # Ensure we raise an error for bad responses
response_data = response.json() # Get the response in JSON format
print(response_data)
"""