backedn chat apis and uplaod apis integrated
This commit is contained in:
@@ -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,36 +304,57 @@ 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
|
||||||
if not isinstance(response, str):
|
if not isinstance(response, str):
|
||||||
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:
|
||||||
return Response(
|
file.write(pdf_content)
|
||||||
content=pdf_content,
|
|
||||||
media_type="application/pdf",
|
# Upload the PDF to S3 using the API
|
||||||
headers={"Content-Disposition": f'attachment; filename="{filename}"'}
|
upload_url = f"{os.getenv('BACKEND_BASE_URL')}/v3/api/custom/theme/doc-upload?x-project={x_api_key}"
|
||||||
)
|
with open(file_path, 'rb') as file:
|
||||||
|
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)}")
|
||||||
|
|||||||
+62
-112
@@ -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]:
|
||||||
def parse_ai_response(content: str) -> Dict:
|
"""
|
||||||
"""Parse AI response content into expected format"""
|
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}"
|
||||||
|
|
||||||
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,106 +94,76 @@ 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):
|
||||||
prompt_template = setup_prompt_template(theme_id, resume,full_history,form_response,generate_theme)
|
prompt_template = setup_prompt_template(theme_id, resume, full_history, form_response, generate_theme)
|
||||||
prompt = prompt_template.invoke({
|
prompt = prompt_template.invoke({
|
||||||
"messages": state["messages"],
|
"messages": state["messages"],
|
||||||
"language": state["language"]
|
"language": state["language"]
|
||||||
})
|
})
|
||||||
response = model.invoke(prompt)
|
response = model.invoke(prompt)
|
||||||
return {"messages": [response]}
|
return {"messages": [response]}
|
||||||
|
|
||||||
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"
|
||||||
|
|
||||||
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(
|
|
||||||
{"messages": input_messages, "language": language},
|
|
||||||
config
|
|
||||||
)
|
|
||||||
|
|
||||||
|
output = app.invoke(
|
||||||
|
{"messages": input_messages, "language": language},
|
||||||
|
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
@@ -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
|
||||||
@@ -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)
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user