304 lines
11 KiB
Python
304 lines
11 KiB
Python
import os
|
||
import requests
|
||
from dotenv import load_dotenv
|
||
from langchain_openai import ChatOpenAI
|
||
from langchain_core.prompts.prompt import PromptTemplate
|
||
from langchain_core.output_parsers import StrOutputParser, JsonOutputParser
|
||
from langchain_openai import OpenAIEmbeddings
|
||
from langchain_core.documents import Document
|
||
from uuid import uuid4
|
||
import json
|
||
import getpass
|
||
import numpy as np
|
||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||
from sklearn.metrics.pairwise import cosine_similarity
|
||
from typing import List
|
||
import time
|
||
from datetime import datetime
|
||
import pytz
|
||
import logging
|
||
load_dotenv()
|
||
|
||
os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY")
|
||
|
||
llm_temp = ChatOpenAI(model="gpt-4o-mini", temperature=0.7)
|
||
def generate_theme(conversation_data, resume, full_history, form_response=None, feedback=None, previous_result=None) -> dict:
|
||
try:
|
||
# Define prompt for summarizing and extracting the required fields
|
||
theme_prompt = PromptTemplate(
|
||
template="""
|
||
<|begin_of_text|><|start_header_id|>system<|end_header_id|>
|
||
You are a Fire Fighter Interview preparation assistant that generates STARTPOP FORMAT based on user interaction with AI.
|
||
Your responsibilities include carefully analyzing user interactions, themes, resumes,Onboarding questions and answers and work history to generate detailed STARTPOP formats for specific themes.
|
||
|
||
### Context and Guidelines:
|
||
1. **Purpose**: Generate a single behavioral question with a detailed STARTPOP format.
|
||
2. **Input Sources**:
|
||
- Current theme
|
||
- User interaction with AI
|
||
- User resume
|
||
- Full work history
|
||
- Onboarding questions and answers for additional context
|
||
3. **Output Format**: JSON object with the following fields:
|
||
- `theme_title`: Title of the theme provided.
|
||
- `question`: Behavioral question aligned with the theme.
|
||
- `Situation`: A bulleted list (75-100 words).
|
||
- `Task`: A bulleted list (50 words).
|
||
- `Action`: A bulleted list (2 negative actions and 2 positive actions).
|
||
- `Results and Transitions`: A bulleted list (25-50 words).
|
||
- `Personal Lessons`: A bulleted list (25-50 words).
|
||
- `Observations of Others`: A bulleted list (25 words).
|
||
- `Professional Connection`: A bulleted list (25-50 words). Additionally:
|
||
- Connect to the theme of the question.
|
||
- Creatively express why you should be part of their team.
|
||
|
||
### Key Concepts in Firefighting:
|
||
Throughout most Probationary Firefighter Interviews, evaluators assess alignment with the **7 Main Concepts of Firefighting**:
|
||
- **High Performance Teams**
|
||
- **Situational Awareness**
|
||
- **Being a Great Problem Solver**
|
||
- **Customer Service**
|
||
- **Building Construction, Mechanical Aptitude**
|
||
- **Emergency Medicine Experience**
|
||
- **Mental and Physical Health**
|
||
|
||
Additionally, they evaluate communication skills, competence, and likability.
|
||
|
||
### 20 Important Themes:
|
||
These themes are used for behavioral questions:
|
||
- Customer Service
|
||
- Conflict
|
||
- Challenge
|
||
- Leadership
|
||
- Stress
|
||
- Successful Team
|
||
- Diversity
|
||
- Mistake
|
||
- Unsuccessful Team
|
||
- Disagreement
|
||
- Bent a Rule
|
||
- Delivered a Difficult Message
|
||
- Displayed Integrity
|
||
- Took a Shortcut
|
||
- Didn’t Follow the Rules
|
||
- Emergency Response
|
||
- Dealt with Disabilities
|
||
- Solved a Big Problem
|
||
- Continuous Improvement
|
||
- Handled Sensitive Information
|
||
|
||
### Behavioral Question Starters:
|
||
Behavioral questions often begin with phrases like:
|
||
- "Tell me a time when..."
|
||
- "Can you tell me about a time when you..."
|
||
- "Describe a situation where you had to..."
|
||
- "Give me an example of how you..."
|
||
- "Have you ever been in a position where you needed to..."
|
||
- "Walk me through a time when you..."
|
||
|
||
### STARTPOP Framework:
|
||
The STARTPOP framework enhances the traditional STAR method. It includes:
|
||
1. **Situation**: Set up the scenario concisely (include dates, ages, places, and circumstances).
|
||
2. **Task**: Explain what needed to be done and why.
|
||
3. **Actions**: Outline both negative and positive approaches.
|
||
4. **Results and Transitions**: Share outcomes and ensure coherence.
|
||
5. **Personal Lessons**: Reflect on what you learned.
|
||
6. **Observations of Others**: Share insights about others involved.
|
||
7. **Professional Connection**: Relate the experience to firefighting and express your desire to join the team.
|
||
|
||
### Example STARTPOP:
|
||
**Question**: Tell me a time when you made a mistake and how you fixed it?
|
||
|
||
- **Situation**:
|
||
- In the Fall, my business, Tiger Building Services, does eavestrough cleaning.
|
||
- In 2019, we were working on a job late in the day, tired and running out of sunlight.
|
||
- I used handheld blowers without checking the wetness of debris, creating a muddy mess on the customer's deck.
|
||
- The customer was upset, and I realized my mistake.
|
||
|
||
- **Task**:
|
||
- Defuse the situation and clean up the mess quickly.
|
||
- Protect my company's reputation and ensure good customer experiences.
|
||
|
||
- **Actions**:
|
||
- Negative: Matching the customer's anger or ignoring the problem.
|
||
- Positive: Getting off the roof safely, apologizing, and switching strategies.
|
||
- Positive: Cleaning the gutters by hand and offering a free soft wash service.
|
||
|
||
- **Results and Transitions**:
|
||
- The job took longer than expected, but we waived fees due to the inconvenience.
|
||
- The customer was satisfied after our resolution plan.
|
||
|
||
- **Personal Lessons**:
|
||
- I learned to own up to mistakes, stay empathetic, and de-escalate tense situations.
|
||
|
||
- **Observations of Others**:
|
||
- People are entitled to their emotions, and following SOPs prevents mistakes.
|
||
|
||
- **Professional Connection**:
|
||
- Mistakes happen, but learning from them is crucial.
|
||
- I align with Markham Fire's values of transparency and accountability.
|
||
|
||
### JSON Output Requirements:
|
||
Generate a well-structured JSON output with the following fields:
|
||
- `theme_title`
|
||
- `question`
|
||
- `Situation`
|
||
- `Task`
|
||
- `Action`
|
||
- `Results and Transitions`
|
||
- `Personal Lessons`
|
||
- `Observations of Others`
|
||
- `Professional Connection`
|
||
|
||
### Review Process:
|
||
1. Ensure all news items align with the specified theme and meet relevance criteria.
|
||
2. Verify the JSON format is flawless, comprehensive, and well-structured.
|
||
|
||
### Additional Notes:
|
||
- You may be provided with feedback and previous results if the user is dissatisfied.
|
||
- Use this feedback to refine and regenerate the STARTPOP.
|
||
|
||
<|eot_id|><|start_header_id|>user<|end_header_id|>
|
||
Rules for Generating Each Component:
|
||
1. Situation: 75-100 words.
|
||
2. Task: 50 words.
|
||
3. Actions: 2 negative actions and 2 positive actions.
|
||
4. Results: 25-50 words.
|
||
5. Personal Lessons: 25-50 words.
|
||
6. Observations of Others: 25 words.
|
||
7. Professional Connection: 25-50 words + creative connection to the theme and team invitation.
|
||
NOTE: MAKE SURE THE OUT IS WELL DETAILED
|
||
CONVERSATION DATA: {conversation_data}
|
||
FEEDBACK: {feedback}
|
||
PREVIOUS RESULT: {previous_result}
|
||
USER RESUME: {resume}
|
||
FULL WORK HISTORY: {full_history}
|
||
Onboarding questions and answers for additional context: {form_response}
|
||
<|start_header_id|>assistant<|end_header_id|>
|
||
Return just the JSON output without any other explanation or comments.
|
||
Thank you for your thorough and precise processing!
|
||
|
||
""",
|
||
input_variables=["resume", "conversation_data", "feedback","form_response" "previous_result", "full_history"],
|
||
)
|
||
|
||
# Pipeline to process the prompt and parse output
|
||
theme_router = theme_prompt | llm_temp | JsonOutputParser()
|
||
|
||
# Call the pipeline and generate the cohesive output
|
||
output = theme_router.invoke({
|
||
"conversation_data": conversation_data,
|
||
"feedback": feedback,
|
||
"previous_result": previous_result,
|
||
"resume": resume,
|
||
"full_history": full_history,
|
||
"form_response":form_response
|
||
})
|
||
|
||
print(f"Output: {output}")
|
||
return output
|
||
|
||
except Exception as e:
|
||
print(f"Error: {e}")
|
||
return {}
|
||
|
||
|
||
|
||
|
||
from fastapi import Response, HTTPException, Depends
|
||
from typing import Optional
|
||
import os
|
||
import requests
|
||
import datetime
|
||
import base64 # For encoding the PDF content in Base64
|
||
|
||
@app.post("/rescue-career/generate-theme")
|
||
async def generate_pdf_endpoint(
|
||
request: GeneratePDFRequest,
|
||
api_key: str = Depends(get_api_key)
|
||
):
|
||
|
||
try:
|
||
# Fetch conversation data using the conversation_id
|
||
conversation_data = await get_conversation_data(request.conversation_id)
|
||
|
||
if not conversation_data:
|
||
raise HTTPException(
|
||
status_code=404,
|
||
detail=f"No conversation found with ID {request.conversation_id}"
|
||
)
|
||
|
||
resume_docs = ""
|
||
if request.resume_url:
|
||
docs = load_document(request.resume_url)
|
||
if not docs:
|
||
raise HTTPException(
|
||
status_code=400,
|
||
detail="Invalid resume URL: Unable to fetch document"
|
||
)
|
||
resume_docs = "\n".join(f"- {doc.page_content}" for doc in docs)
|
||
|
||
full_history_docs = ""
|
||
if request.full_history_url:
|
||
docs = load_document(request.full_history_url)
|
||
if not docs:
|
||
raise HTTPException(
|
||
status_code=400,
|
||
detail="Invalid full_history URL: Unable to fetch document"
|
||
)
|
||
full_history_docs = "\n".join(f"- {doc.page_content}" for doc in docs)
|
||
|
||
form_response_docs = ""
|
||
if request.form_id:
|
||
try:
|
||
x_api_key = os.getenv("BACKEND_XAPI_KEY")
|
||
url = f"{os.getenv('BACKEND_BASE_URL')}/v3/api/custom/theme-document/answer/{request.form_id}?x-project={x_api_key}"
|
||
result = requests.get(url)
|
||
form_response = result.json() # Return response in JSON format
|
||
form_response_docs = "\n".join(f"- {form_response}")
|
||
except:
|
||
raise HTTPException(
|
||
status_code=400,
|
||
detail="Unable to fetch onboarding data"
|
||
)
|
||
|
||
# Generate theme data using the generate_theme function
|
||
theme_data = generate_theme(
|
||
conversation_data=conversation_data,
|
||
feedback=request.feedback,
|
||
previous_result=request.previous_results,
|
||
resume=resume_docs,
|
||
form_response=form_response_docs,
|
||
full_history=full_history_docs
|
||
)
|
||
|
||
if not theme_data:
|
||
raise HTTPException(
|
||
status_code=500,
|
||
detail="Failed to generate theme data"
|
||
)
|
||
|
||
# Generate the PDF using the create_pdf function
|
||
pdf_content = create_pdf(theme_data)
|
||
|
||
# Encode the PDF content in Base64
|
||
pdf_base64 = base64.b64encode(pdf_content).decode("utf-8")
|
||
|
||
# Create filename with timestamp
|
||
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
||
filename = f"theme_{timestamp}.pdf"
|
||
|
||
# Return both the PDF (as Base64) and the theme data in a JSON response
|
||
return {
|
||
"theme_data": theme_data,
|
||
"pdf": {
|
||
"filename": filename,
|
||
"content": pdf_base64
|
||
}
|
||
}
|
||
|
||
except Exception as e:
|
||
raise HTTPException(
|
||
status_code=500,
|
||
detail=f"Error generating PDF: {str(e)}"
|
||
) |