form id added for generating theme

This commit is contained in:
2025-02-06 21:01:22 +00:00
parent 142640325a
commit ed55e5e48e
5 changed files with 148 additions and 351 deletions
View File
+16 -1
View File
@@ -95,6 +95,7 @@ class GeneratePDFRequest(BaseModel):
previous_results: Optional[Dict[str, Any]] = None
resume_url: Optional[str] = None
full_history_url: Optional[str] = None
form_id:Optional[int] = None
class QuizRequest(BaseModel):
pdf_url: str
@@ -200,7 +201,6 @@ async def chat_endpoint(
)
@app.post("/rescue-career/generate-theme")
async def generate_pdf_endpoint(
request: GeneratePDFRequest,
@@ -238,12 +238,27 @@ async def generate_pdf_endpoint(
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 onborading 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
)
View File
+122 -158
View File
@@ -22,23 +22,38 @@ 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,feedback=None, previous_result=None) -> dict:
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.
You will be provided with the current theme, user interaction with AI (alongside user resume), and data.
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.
Your responsibility is to carefully analyze user interaction with AI, the theme, and the user RESUME to generate a STARTPOP format for the theme.
NOTE: A SINGLE QUESTION IS GENERATED WITH DETAILED STARTPOP FORMAT
NOTE: For more Context, user full work history may also be provided
TO KNOW MORE ABOUT THE PROJECT READ BELOW
---START------
Throughout most Probationary Firefighter Interviews, they will be evaluating a ton of things. Typically, they want to see how you align with the **7 Main Concepts of Firefighting**. They are also watching how nervous you are, your communication skills, and your overall general competence for the role. At the end of the day, you want them to like you.
### 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.
### 7 Main Concepts:
### 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**
@@ -47,16 +62,10 @@ Throughout most Probationary Firefighter Interviews, they will be evaluating a t
- **Emergency Medicine Experience**
- **Mental and Physical Health**
Your crew of four firefighters is usually comprised of a Driver, a Captain, and two firefighters in the back. That is a High-Performance Team.
Additionally, they evaluate communication skills, competence, and likability.
We are frequently dispatched to calls that require using our understanding of Building Construction Concepts, Mechanical Aptitude, and Emergency Medical Experience. When you respond to an emergency event that is inherently dangerous (like a vehicle fire, a car accident in a slanted ditch, a person trapped under a machine, a house fire, or a chemical suicide), you need to use your Situational Awareness to keep that crew safe.
Sometimes the tools, training, and tactics that you have been taught work perfectly. Sometimes they dont. Can you be a Good Problem Solver to quickly come up with something to make the situation better for the people, places, and environments that we protect?
Ultimately, your crew will be serving the public, and the chiefs need to know that you can be trained to be above their desired standard so that you give the public great Customer Service.
### 20 Important Themes
Consider the 7 concepts to be the soil. All of your stories grow out of that soil. But not every story works for every question. You need to handpick the right one at the right times to give them. Sort of like how you handpick flowers out of the soil. You NEED to have **20 different flowers** so that you are fully prepared for whatever behavioral question they throw at you. These are the **20 Themes** that you would use for behavioral questions:
### 20 Important Themes:
These themes are used for behavioral questions:
- Customer Service
- Conflict
- Challenge
@@ -65,7 +74,7 @@ Consider the 7 concepts to be the soil. All of your stories grow out of that soi
- Successful Team
- Diversity
- Mistake
- Unsuccessful Team
- Unsuccessful Team
- Disagreement
- Bent a Rule
- Delivered a Difficult Message
@@ -78,162 +87,117 @@ Consider the 7 concepts to be the soil. All of your stories grow out of that soi
- Continuous Improvement
- Handled Sensitive Information
### Behavioral Question Starters
Behavioral questions usually start 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"
### 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 STAR Format is what most people tell you to do in order to answer a firefighter interview question. Its a great framework. I highly recommend it. I just advise that you pump it up even further. I call it **STARTPOP**.
### 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.
Try and pull from different parts of your life. My Chief Training Officer told me that he enjoys candidates that are able to use different experiences to answer the questions. Listening to someone drone on and on about a singular time or type of event in their life is a massive turn-off to the interview panel. Thats a bad thing. Just like most things, variety is the spice of life.
### Example STARTPOP:
**Question**: Tell me a time when you made a mistake and how you fixed it?
#### Components of STARTPOP:
1. **Situation**:
- Set up the answer in the mind of the question asker.
- Your storytelling skills matter here. It has to be concise and impactful (no more than 25 seconds long).
- Include dates, ages, places, and circumstances.
- **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.
2. **Task**:
- Explain what you needed to do and why you needed to do it.
- Recap the situation quickly from a different angle.
- **Task**:
- Defuse the situation and clean up the mess quickly.
- Protect my company's reputation and ensure good customer experiences.
3. **Actions**:
- Outline both the negative and the positive way of doing things.
- Show high moral character in every question.
- **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.
4. **Results**:
- Explain what happened as a result of your actions.
- Share results in a time-specific manner (e.g., “5 months later X happened”).
- **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.
5. **Transitions**:
- Speak in a way that aligns with professional expectations.
- Ensure coherence in your responses.
- **Personal Lessons**:
- I learned to own up to mistakes, stay empathetic, and de-escalate tense situations.
6. **Personal Lessons**:
- Discuss what you learned about yourself.
- Address any concerns the interviewers might have about hiring you.
- **Observations of Others**:
- People are entitled to their emotions, and following SOPs prevents mistakes.
7. **Other People Observations**:
- Share insights about others in the situation.
- Keep it short and to the point.
- **Professional Connection**:
- Mistakes happen, but learning from them is crucial.
- I align with Markham Fire's values of transparency and accountability.
8. **Professional Connection**:
- Relate your experience directly to the fire service.
- Conclude strongly, avoiding phrases like “and so yeah…”.
### 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`
EXAMPLE STARTPOP
### 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.
question: Tell me a time when you made a MISTAKE how did you fix it? (Eaves Cleaning Mistake)
Situation:
• In the Fall my business, Tiger Building Services, does a lot of eavestrough cleaning.
• Back in 2019 I was working with an employee in my truck. We were working nicely to hit my daily revenue target.
• We got to the last job of the day; we were tired and running out of sunlight. But I really wanted to squeeze it in.
• We have procedures to follow in order to work safely and effectively. My goal is to be as low impact as possible.
• I made a mistake when we used the handheld blowers on their eaves to blow out the debris without checking how
wet the debris was or the ground around the back of the house. It made a muddy mess all over their white deck.
• They were livid. Swearing and completely unhappy with how we were doing the work. I take ownership of my
mistakes and realized I screwed up by using blowers instead of hand bombing it.
Task:
• My task was to defuse the situation and clean up the mess as quickly as possible.
• I had to do it because as the owner of the company it was my reputation on the line. We got the job through one
of the apps that we use to fill out our schedule and it is imperative that I make sure their customers have good
experiences with us so that we keep our top position on the app.
• I am also a man of integrity and try to be always empathetic, so I felt obligated to correct the mistake.
Action:
• The wrong approach would have been to match the customers energy and just as belligerent and abrasive. It
would have escalated the situation to a point where things could have gotten ugly and pretty physical.
• It would have also been wrong to just ignore or make fun of the customer and the problem we created, or to just
pack our ladders and tools and run away as quickly as possible.
• The correct approach was to get off the roof safely and speak with the customer on the ground eye to eye.
• I made sure to do that and then apologized for the mess that we made. I empathized with them and the way they
were feeling. I told them that it was our mistake, and we will work to correct it immediately.
• I switched our strategy. Told the employee to clean use their hand for the gutters while I cleaned the deck.
Results and Transitions:
• It was a losing situation for me in the short run. The job ended up taking a bit longer than expected and I actually
told them that we would waive the fees due to the inconveniences we created.
• After we finished up, I gave her a plan of action. She would get the eaves cleaning for free, and we would return
the following day with our soft wash system to make sure that she had a sparkling clean deck also free of charge.
• The next morning when we finished the free soft wash, she was happy with the resolution plan and Jiffy was
impressed with our ability to correct the mistake and alleviate the situation.
Personal Lessons:
• What I learned about myself was that I do make mistakes, but I am the type of person that owns up to it.
• I am also honest and empathetic, and I can perform in stressful situations and that I could de-escalate tense
situations, to be adaptable and think quickly on the fly.
• I used the LAST tactic for good customer service: Listened, Apologized, Solved the problem, then thanked them.
• I took the full brunt of their anger, made an action plan that instantly calmed the situation and then acted on it to
make them happy with the service.
Observations of Others:
• What I learned about other people is that people are entitled to their reactions, emotions, and feelings.
• I respect those emotions and have learned that following actionable game plans will help avoid or resolve issues.
• I know the term proper planning prevents poor performance is applicable here.
• There is a reason organizations have SOPs and SOGs. They are there to be followed in order to avoid mistakes.
Professional Connection:
• My biggest takeaway was it is okay to make mistakes, but it is not okay to not learn from them.
• I know that the team on Markham Fire sometimes makes mistakes on the firegrounds, but they are also the type
of people that own up to their mistake and learn from them.
• I also know that Chief Grant promotes having an open and transparent organization that is not afraid from
admitting an error or correcting it.
### 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.
---END------
JSON Output Requirements: Generate a list of well-structured JSON output STARTPOP with question and correcpoding STARTPPOP with the following fields:
- theme_title: The title the theme provided
- question: The question
- Situation: A bulleted list of texts as seen in examples
- Task: A bulleted list of texts as seen in examples
- Action: A bulleted list of texts as seen in examples
- Personal Lessons: A bulleted list of texts as seen in examples
- Results and Transitions: A bulleted list of texts as seen in examples
- Observations of Others: A bulleted list of texts as seen in examples
- Professional Connection: A bulleted list of texts as seen in examples
Review Process:
- Carefully review all news items to confirm they align with the specified theme and meet relevance criteria.
- Ensure the JSON format is flawless, comprehensive, and well-structured, with all fields included and correctly formatted.
NOTE: 1. you MAY BE PROVIDED WITH FEEDBACK AND PREVIOUS RESULT, MEANING AI HAS GENERATED STARTPOP BEFORE AND MAYBE USER IS NOT SATISFIED WITH THE RESULT THEN YOU GENERATE A NEW ONE BASED ON THE FEEDBACK
NOTE: Each question will have a correpoding STARTPOP feilds
<|eot_id|><|start_header_id|>user<|end_header_id|>
Rules for Generating Each Component:
1. Situation: 100 - 120 words.
2. Task: 100 words.
3. Actions: 2 negative actions and 2 positive actions.
4. Results: 50-70 words.
5. Personal Lessons: 50-70 words.
6. Observations of Others: 40 words.
7. Professional Connection: 50-70 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!
CONVERSATION DATA :{conversation_data}
FEEDBACK: {feedback}
PREVIOUS RESULT: {previous_result}
USER RESUME : {resume}
FULL WORK HISTORY : {full_history}
<|eot_id|><|start_header_id|>user<|end_header_id|>
RULES FOR GENERATING EACH COMPONENT - FOLLOW THESE RULES THOROUGHLY MAKE SURE YOUR OUTPUT IS WELL DETAILED
THE FRAME WORK MUST BE DETAILED WITH THE FOLLWWING RULES
1. Situation : 75 - 100 words
2. Task: 50 words
3. Actions: 2 Negative actions and 2 positive actions
4. Results: 25 - 5o words
5. Personal Lessons : 25 - 50 words
6. Observation of others: 25 words
7. Professional connections: 25 - 50 words and in addition to the 25-50 words:
- Connect to the theme of questions (Be creative here)
- Ask to be part of their team(be creattive here)
""",
input_variables=["resume","conversation_data", "feedback", "previous_result","full_history"],
""",
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})
print(f"Output : {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 {}
print(f"Error: {e}")
return {}
+10 -192
View File
@@ -1,195 +1,13 @@
import os
import requests
import json
from typing import List, Dict, Optional, TypedDict, Sequence, Annotated
from dataclasses import dataclass
from pathlib import Path
from datetime import datetime
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.messages import HumanMessage, AIMessage, BaseMessage
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import START, MessagesState, StateGraph
from utils.utils import format_questions_text
from src.prompts import chat_prompt
from langchain_openai import ChatOpenAI
@dataclass
class Message:
role: str # 'human' or 'ai'
content: str
timestamp: str
from dotenv import load_dotenv
load_dotenv()
doc_id = 2
QUESTIONS_PATH = "./data/config_files/questions.json"
with open(QUESTIONS_PATH, "r") as f:
questions = json.load(f)
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}"
prompt_template = None
MODEL = "gpt-4o"
def initialize_workflow(model) -> StateGraph:
"""Initialize LangGraph workflow"""
workflow = StateGraph(state_schema=MessagesState)
memory = MemorySaver()
def call_model(state: MessagesState):
prompt = prompt_template.invoke({"messages": state["messages"], "language": state["language"]})
response = model.invoke(prompt)
return {"messages": [response]}
workflow.add_edge(START, "model")
workflow.add_node("model", call_model)
return workflow.compile(checkpointer=memory)
def setup_prompt_template(theme: int, resume: str) -> ChatPromptTemplate:
"""Set up the prompt template"""
return ChatPromptTemplate.from_messages([
("system", chat_prompt(theme, resume)),
MessagesPlaceholder(variable_name="messages")
])
def parse_ai_response(content: str) -> Dict:
"""Parse AI response content into expected format"""
try:
response = json.loads(content)
return {
"message": response.get("message", ""),
"end": response.get("end", "no") == "yes"
}
except json.JSONDecodeError:
return {
"message": content,
"end": False
}
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]:
"""Convert our Message objects to LangChain message objects"""
converted_messages = []
for msg in messages:
if msg.role == "human":
converted_messages.append(HumanMessage(content=msg.content))
else:
converted_messages.append(AIMessage(content=msg.content))
return converted_messages
def ai_chat(query: str, conversation_id: str, theme_id: int, resume: str) -> str:
"""Main chat function that processes queries and manages conversation"""
storage_path = Path("conversations.json")
class State(TypedDict):
messages: Annotated[Sequence[BaseMessage], "The messages in the conversation"]
language: str
# Initialize model and workflow
model = ChatOpenAI(model=MODEL)
workflow = StateGraph(state_schema=State)
def call_model(state: State):
prompt_template = setup_prompt_template(theme_id, resume)
prompt = prompt_template.invoke({
"messages": state["messages"],
"language": state["language"]
})
response = model.invoke(prompt)
return {"messages": [response]}
workflow.add_edge(START, "model")
workflow.add_node("model", call_model)
memory = MemorySaver()
app = workflow.compile(checkpointer=memory)
# Get conversation history
history = get_conversation_history(conversation_id, storage_path)
config = {"configurable": {"thread_id": conversation_id}}
language = "English"
if not history:
# New conversation
input_messages = [HumanMessage(content=query)] if query else [HumanMessage(content="Let's get started")]
output = app.invoke(
{"messages": input_messages, "language": language},
config
)
else:
# Existing conversation
history = convert_to_langchain_messages(history)
input_messages = history + [HumanMessage(content=query)] if query else history
output = app.invoke(
{"messages": input_messages, "language": language},
config
)
# Store messages
if query:
add_message(storage_path, conversation_id, "human", query)
add_message(storage_path, conversation_id, "ai", output["messages"][-1].content)
return output["messages"][-1].content
# Example usage:
if __name__ == "__main__":
# Sample resume
sample_resume = """
John Doe
EMT-B Certified
5 years experience as volunteer firefighter
Bachelor's in Fire Science
"""
# 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)
result = requests.get(url)
response_json = result.json() # Return response in JSON format
print(response_json)