Restructured how BackstoryTextField works to not propogate changes until Enter
This commit is contained in:
parent
bcb1e866cb
commit
6923132655
@ -1,4 +1,4 @@
|
|||||||
import React, { useRef, useEffect, ChangeEvent, KeyboardEvent } from 'react';
|
import React, { useRef, useEffect, ChangeEvent, KeyboardEvent, useState } from 'react';
|
||||||
import { useTheme } from '@mui/material/styles';
|
import { useTheme } from '@mui/material/styles';
|
||||||
import './BackstoryTextField.css';
|
import './BackstoryTextField.css';
|
||||||
|
|
||||||
@ -7,8 +7,7 @@ interface BackstoryTextFieldProps {
|
|||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
multiline?: boolean;
|
multiline?: boolean;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
onChange?: (e: ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => void;
|
onEnter: (value: string) => void;
|
||||||
onKeyDown?: (e: KeyboardEvent<HTMLTextAreaElement | HTMLInputElement>) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const BackstoryTextField: React.FC<BackstoryTextFieldProps> = ({
|
const BackstoryTextField: React.FC<BackstoryTextFieldProps> = ({
|
||||||
@ -16,12 +15,12 @@ const BackstoryTextField: React.FC<BackstoryTextFieldProps> = ({
|
|||||||
disabled = false,
|
disabled = false,
|
||||||
multiline = false,
|
multiline = false,
|
||||||
placeholder,
|
placeholder,
|
||||||
onChange,
|
onEnter
|
||||||
onKeyDown,
|
|
||||||
}) => {
|
}) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||||
const shadowRef = useRef<HTMLTextAreaElement>(null);
|
const shadowRef = useRef<HTMLTextAreaElement>(null);
|
||||||
|
const [editValue, setEditValue] = useState<string>(value);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (multiline && textareaRef.current && shadowRef.current) {
|
if (multiline && textareaRef.current && shadowRef.current) {
|
||||||
@ -43,15 +42,10 @@ const BackstoryTextField: React.FC<BackstoryTextFieldProps> = ({
|
|||||||
}
|
}
|
||||||
}, [value, multiline, textareaRef, shadowRef, placeholder]);
|
}, [value, multiline, textareaRef, shadowRef, placeholder]);
|
||||||
|
|
||||||
const handleChange = (e: ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
|
const handleKeyDown = (event: KeyboardEvent<HTMLTextAreaElement | HTMLInputElement>) => {
|
||||||
if (onChange) {
|
if (event.key === 'Enter' && (!multiline || !event.shiftKey)) {
|
||||||
onChange(e);
|
setEditValue('');
|
||||||
}
|
onEnter(event.currentTarget.value);
|
||||||
};
|
|
||||||
|
|
||||||
const handleKeyDown = (e: KeyboardEvent<HTMLTextAreaElement | HTMLInputElement>) => {
|
|
||||||
if (onKeyDown) {
|
|
||||||
onKeyDown(e);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -74,10 +68,10 @@ const BackstoryTextField: React.FC<BackstoryTextFieldProps> = ({
|
|||||||
<input
|
<input
|
||||||
className="BackstoryTextField"
|
className="BackstoryTextField"
|
||||||
type="text"
|
type="text"
|
||||||
value={value}
|
value={editValue}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
onChange={handleChange}
|
onChange={(e) => { setEditValue(e.target.value); }}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
style={sharedStyle}
|
style={sharedStyle}
|
||||||
/>
|
/>
|
||||||
@ -89,10 +83,10 @@ const BackstoryTextField: React.FC<BackstoryTextFieldProps> = ({
|
|||||||
<textarea
|
<textarea
|
||||||
className="BackstoryTextField"
|
className="BackstoryTextField"
|
||||||
ref={textareaRef}
|
ref={textareaRef}
|
||||||
value={value}
|
value={editValue}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
onChange={handleChange}
|
onChange={(e) => { setEditValue(e.target.value); }}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
style={{
|
style={{
|
||||||
...sharedStyle,
|
...sharedStyle,
|
||||||
|
@ -246,10 +246,8 @@ const Conversation = forwardRef<ConversationHandle, ConversationProps>(({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleKeyPress = (event: any) => {
|
const handleEnter = (value: string) => {
|
||||||
if (event.key === 'Enter' && !event.shiftKey) {
|
sendQuery(value);
|
||||||
sendQuery(query);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
@ -553,8 +551,7 @@ const Conversation = forwardRef<ConversationHandle, ConversationProps>(({
|
|||||||
disabled={processing}
|
disabled={processing}
|
||||||
multiline={multiline ? true : false}
|
multiline={multiline ? true : false}
|
||||||
value={query}
|
value={query}
|
||||||
onChange={(e: any) => setQuery(e.target.value)}
|
onEnter={handleEnter}
|
||||||
onKeyDown={handleKeyPress}
|
|
||||||
placeholder={prompt}
|
placeholder={prompt}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -287,7 +287,7 @@ const Message = (props: MessageProps) => {
|
|||||||
overflow: "auto", /* Handles scrolling for the div */
|
overflow: "auto", /* Handles scrolling for the div */
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<StyledMarkdown {...{ content: formattedContent, submitQuery, sessionId, setSnack }} />
|
<StyledMarkdown streaming={message.role === "streaming"} {...{ content: formattedContent, submitQuery, sessionId, setSnack }} />
|
||||||
</Scrollable>
|
</Scrollable>
|
||||||
:
|
:
|
||||||
<Typography
|
<Typography
|
||||||
|
@ -17,10 +17,11 @@ interface StyledMarkdownProps extends BackstoryElementProps {
|
|||||||
className?: string,
|
className?: string,
|
||||||
content: string,
|
content: string,
|
||||||
sx?: SxProps,
|
sx?: SxProps,
|
||||||
|
streaming?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledMarkdown: React.FC<StyledMarkdownProps> = (props: StyledMarkdownProps) => {
|
const StyledMarkdown: React.FC<StyledMarkdownProps> = (props: StyledMarkdownProps) => {
|
||||||
const { className, content, submitQuery, sx } = props;
|
const { className, content, submitQuery, sx, streaming } = props;
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const overrides: any = {
|
const overrides: any = {
|
||||||
@ -28,16 +29,16 @@ const StyledMarkdown: React.FC<StyledMarkdownProps> = (props: StyledMarkdownProp
|
|||||||
component: (element: any) => {
|
component: (element: any) => {
|
||||||
const { className } = element.children.props;
|
const { className } = element.children.props;
|
||||||
const content = element.children?.props?.children || "";
|
const content = element.children?.props?.children || "";
|
||||||
if (className === "lang-mermaid") {
|
if (className === "lang-mermaid" && !streaming) {
|
||||||
return <Mermaid className="Mermaid" chart={content} />;
|
return <Mermaid className="Mermaid" chart={content} />;
|
||||||
}
|
}
|
||||||
if (className === "lang-markdown") {
|
if (className === "lang-markdown") {
|
||||||
return <MuiMarkdown children={content} />;
|
return <MuiMarkdown children={content} />;
|
||||||
}
|
}
|
||||||
if (className === "lang-json") {
|
if (className === "lang-json" && !streaming) {
|
||||||
try {
|
try {
|
||||||
const fixed = jsonrepair(content);
|
const fixed = jsonrepair(content);
|
||||||
return <Scrollable autoscroll className="JsonViewScrollable">
|
return <Scrollable className="JsonViewScrollable">
|
||||||
<JsonView
|
<JsonView
|
||||||
className="JsonView"
|
className="JsonView"
|
||||||
style={{
|
style={{
|
||||||
|
@ -8,6 +8,7 @@ import json
|
|||||||
import traceback
|
import traceback
|
||||||
import asyncio
|
import asyncio
|
||||||
import time
|
import time
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
from . base import Agent, agent_registry, LLMMessage
|
from . base import Agent, agent_registry, LLMMessage
|
||||||
from .. conversation import Conversation
|
from .. conversation import Conversation
|
||||||
@ -419,32 +420,60 @@ class JobDescription(Agent):
|
|||||||
metadata["error"] = message.response
|
metadata["error"] = message.response
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
def format_rag_context(self, rag_results: List[Dict[str, Any]]) -> str:
|
||||||
|
"""
|
||||||
|
Format RAG results from process_job_requirements into a structured string.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
rag_results: List of dictionaries from process_job_requirements.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A formatted string for inclusion in the prompt.
|
||||||
|
"""
|
||||||
|
if not rag_results:
|
||||||
|
return "No additional context available."
|
||||||
|
|
||||||
|
# Group results by category and subcategory
|
||||||
|
grouped_context = defaultdict(list)
|
||||||
|
for result in rag_results:
|
||||||
|
key = f"{result['category']}/{result['subcategory']}".strip("/")
|
||||||
|
grouped_context[key].append({
|
||||||
|
"query": result["context"],
|
||||||
|
"content": result["content"][:100] + "..." if len(result["content"]) > 100 else result["content"]
|
||||||
|
})
|
||||||
|
|
||||||
|
# Format as a structured string
|
||||||
|
context_lines = ["Additional Context from Document Retrieval:"]
|
||||||
|
for category, items in grouped_context.items():
|
||||||
|
context_lines.append(f"\nCategory: {category}")
|
||||||
|
for item in items:
|
||||||
|
context_lines.append(f"- Query: {item['query']}")
|
||||||
|
context_lines.append(f" Relevant Document: {item['content']}")
|
||||||
|
|
||||||
|
return "\n".join(context_lines)
|
||||||
|
|
||||||
# Stage 1B: Candidate Analysis Implementation
|
# Stage 1B: Candidate Analysis Implementation
|
||||||
def create_candidate_analysis_prompt(self, resume: str, context: str) -> tuple[str, str]:
|
def create_candidate_analysis_prompt(self, resume: str, rag_results: List[Dict[str, Any]]) -> tuple[str, str]:
|
||||||
"""Create the prompt for candidate qualifications analysis."""
|
"""Create the prompt for candidate qualifications analysis."""
|
||||||
|
|
||||||
# system_prompt = """
|
# system_prompt = """
|
||||||
# You are an objective resume analyzer. Your task is to catalog ALL skills, experiences, and qualifications
|
# You are an objective resume analyzer. Create a comprehensive inventory of all skills, experiences, and qualifications present in the candidate's materials.
|
||||||
# present in a candidate's materials WITHOUT any reference to any job description.
|
|
||||||
|
# CORE PRINCIPLES:
|
||||||
# ## INSTRUCTIONS:
|
# - Analyze ONLY the candidate's resume and provided context
|
||||||
|
# - Focus ONLY on the candidate's actual qualifications
|
||||||
# 1. Analyze ONLY the candidate's resume and context provided.
|
# - Do not reference any job requirements
|
||||||
# 2. Create a comprehensive inventory of the candidate's actual qualifications.
|
# - Include only explicitly mentioned information
|
||||||
# 3. DO NOT consider any job requirements - this is a pure candidate analysis task.
|
|
||||||
# 4. For each qualification, cite exactly where in the materials it appears.
|
# OUTPUT FORMAT:
|
||||||
# 5. DO NOT duplicate or repeat time periods or skills once listed.
|
|
||||||
|
|
||||||
# ## OUTPUT FORMAT:
|
|
||||||
|
|
||||||
# ```json
|
# ```json
|
||||||
# {
|
# {
|
||||||
# "candidate_qualifications": {
|
# "candidate_qualifications": {
|
||||||
# "technical_skills": [
|
# "technical_skills": [
|
||||||
# {
|
# {
|
||||||
# "skill": "skill name",
|
# "skill": "skill name",
|
||||||
# "evidence": "exact quote from materials",
|
# "evidence_location": "where in resume this appears",
|
||||||
# "source": "resume or context",
|
# "expertise_level": "stated level or 'unspecified'"
|
||||||
# "expertise_level": "explicit level mentioned or 'unspecified'"
|
|
||||||
# }
|
# }
|
||||||
# ],
|
# ],
|
||||||
# "work_experience": [
|
# "work_experience": [
|
||||||
@ -462,88 +491,81 @@ class JobDescription(Agent):
|
|||||||
# "degree": "degree name",
|
# "degree": "degree name",
|
||||||
# "institution": "institution name",
|
# "institution": "institution name",
|
||||||
# "completed": true/false,
|
# "completed": true/false,
|
||||||
# "evidence": "exact quote from materials"
|
# "graduation_date": "date or 'ongoing'"
|
||||||
# }
|
# }
|
||||||
# ],
|
# ],
|
||||||
# "projects": [
|
# "projects": [
|
||||||
# {
|
# {
|
||||||
# "name": "project name",
|
# "name": "project name",
|
||||||
# "description": "brief description",
|
# "description": "brief description",
|
||||||
# "technologies_used": ["tech1", "tech2"],
|
# "technologies_used": ["tech1", "tech2"]
|
||||||
# "evidence": "exact quote from materials"
|
|
||||||
# }
|
# }
|
||||||
# ],
|
# ],
|
||||||
# "soft_skills": [
|
# "soft_skills": [
|
||||||
# {
|
# {
|
||||||
# "skill": "skill name",
|
# "skill": "skill name",
|
||||||
# "evidence": "exact quote or inference basis",
|
# "context": "brief mention of where this appears"
|
||||||
# "source": "resume or context"
|
|
||||||
# }
|
# }
|
||||||
# ]
|
# ]
|
||||||
# }
|
# }
|
||||||
# }
|
# }
|
||||||
# ```
|
|
||||||
|
|
||||||
# Be thorough and precise. Include ONLY skills and experiences explicitly mentioned in the materials.
|
|
||||||
# For each entry, provide the exact text evidence from the materials that supports its inclusion.
|
|
||||||
# Do not make assumptions about skills based on job titles or project names - only include skills explicitly mentioned.
|
|
||||||
# """
|
# """
|
||||||
|
system_prompt = """\
|
||||||
|
You are an objective resume analyzer. Create a comprehensive inventory of all skills, experiences, and qualifications present in the candidate's materials.
|
||||||
|
|
||||||
system_prompt = """
|
CORE PRINCIPLES:
|
||||||
You are an objective resume analyzer. Create a comprehensive inventory of all skills, experiences, and qualifications present in the candidate's materials.
|
- Analyze ONLY the candidate's resume and provided context.
|
||||||
|
- Focus ONLY on the candidate's actual qualifications explicitly mentioned in the resume.
|
||||||
|
- Use the additional context to clarify or provide background for terms, skills, or experiences mentioned in the resume (e.g., to understand the scope of a skill like 'Python' or a role's responsibilities).
|
||||||
|
- Do NOT treat the context as job requirements or infer qualifications not explicitly stated in the resume.
|
||||||
|
- Include only explicitly mentioned information from the resume, supplemented by context where relevant.
|
||||||
|
|
||||||
CORE PRINCIPLES:
|
OUTPUT FORMAT:
|
||||||
- Analyze ONLY the candidate's resume and provided context
|
```json
|
||||||
- Focus ONLY on the candidate's actual qualifications
|
{
|
||||||
- Do not reference any job requirements
|
"candidate_qualifications": {
|
||||||
- Include only explicitly mentioned information
|
"technical_skills": [
|
||||||
|
|
||||||
OUTPUT FORMAT:
|
|
||||||
```json
|
|
||||||
{
|
{
|
||||||
"candidate_qualifications": {
|
"skill": "skill name",
|
||||||
"technical_skills": [
|
"evidence_location": "where in resume this appears",
|
||||||
{
|
"expertise_level": "stated level or 'unspecified'"
|
||||||
"skill": "skill name",
|
|
||||||
"evidence_location": "where in resume this appears",
|
|
||||||
"expertise_level": "stated level or 'unspecified'"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"work_experience": [
|
|
||||||
{
|
|
||||||
"role": "job title",
|
|
||||||
"company": "company name",
|
|
||||||
"duration": "time period",
|
|
||||||
"responsibilities": ["resp1", "resp2"],
|
|
||||||
"technologies_used": ["tech1", "tech2"],
|
|
||||||
"achievements": ["achievement1", "achievement2"]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"education": [
|
|
||||||
{
|
|
||||||
"degree": "degree name",
|
|
||||||
"institution": "institution name",
|
|
||||||
"completed": true/false,
|
|
||||||
"graduation_date": "date or 'ongoing'"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"projects": [
|
|
||||||
{
|
|
||||||
"name": "project name",
|
|
||||||
"description": "brief description",
|
|
||||||
"technologies_used": ["tech1", "tech2"]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"soft_skills": [
|
|
||||||
{
|
|
||||||
"skill": "skill name",
|
|
||||||
"context": "brief mention of where this appears"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
"""
|
],
|
||||||
|
"work_experience": [
|
||||||
|
{
|
||||||
|
"role": "job title",
|
||||||
|
"company": "company name",
|
||||||
|
"duration": "time period",
|
||||||
|
"responsibilities": ["resp1", "resp2"],
|
||||||
|
"technologies_used": ["tech1", "tech2"],
|
||||||
|
"achievements": ["achievement1", "achievement2"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"education": [
|
||||||
|
{
|
||||||
|
"degree": "degree name",
|
||||||
|
"institution": "institution name",
|
||||||
|
"completed": true/false,
|
||||||
|
"graduation_date": "date or 'ongoing'"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"projects": [
|
||||||
|
{
|
||||||
|
"name": "project name",
|
||||||
|
"description": "brief description",
|
||||||
|
"technologies_used": ["tech1", "tech2"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"soft_skills": [
|
||||||
|
{
|
||||||
|
"skill": "skill name",
|
||||||
|
"context": "brief mention of where this appears"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
context = self.format_rag_context(rag_results)
|
||||||
prompt = f"Resume:\n{resume}\n\nAdditional Context:\n{context}"
|
prompt = f"Resume:\n{resume}\n\nAdditional Context:\n{context}"
|
||||||
return system_prompt, prompt
|
return system_prompt, prompt
|
||||||
|
|
||||||
@ -602,10 +624,10 @@ class JobDescription(Agent):
|
|||||||
message.status = "done"
|
message.status = "done"
|
||||||
yield message
|
yield message
|
||||||
|
|
||||||
async def analyze_candidate_qualifications(self, message: Message, resume: str, additional_context: str, metadata: Dict[str, Any]) -> AsyncGenerator[Message, None]:
|
async def analyze_candidate_qualifications(self, message: Message, resume: str, rag_context: List[Dict[str, Any]], metadata: Dict[str, Any]) -> AsyncGenerator[Message, None]:
|
||||||
"""Analyze candidate qualifications from resume and context."""
|
"""Analyze candidate qualifications from resume and context."""
|
||||||
try:
|
try:
|
||||||
system_prompt, prompt = self.create_candidate_analysis_prompt(resume, additional_context)
|
system_prompt, prompt = self.create_candidate_analysis_prompt(resume, rag_context)
|
||||||
metadata["system_prompt"] = system_prompt
|
metadata["system_prompt"] = system_prompt
|
||||||
metadata["prompt"] = prompt
|
metadata["prompt"] = prompt
|
||||||
async for message in self.call_llm(message, system_prompt, prompt):
|
async for message in self.call_llm(message, system_prompt, prompt):
|
||||||
@ -1048,7 +1070,7 @@ Based on the reference data above, please create a corrected version of the resu
|
|||||||
|
|
||||||
# Call find_similar for the item
|
# Call find_similar for the item
|
||||||
try:
|
try:
|
||||||
rag_results = retriever.find_similar(item, top_k=10, threshold=0.7)
|
rag_results = retriever.find_similar(item, top_k=20, threshold=0.4) # Strict matching
|
||||||
# Process each result
|
# Process each result
|
||||||
for doc_id, content, distance, metadata in zip(
|
for doc_id, content, distance, metadata in zip(
|
||||||
rag_results["ids"],
|
rag_results["ids"],
|
||||||
@ -1089,21 +1111,22 @@ Based on the reference data above, please create a corrected version of the resu
|
|||||||
reverse=True
|
reverse=True
|
||||||
)
|
)
|
||||||
|
|
||||||
# Return top 20 results
|
# Return top 10 results
|
||||||
return sorted_results[:20]
|
return sorted_results[:10]
|
||||||
|
|
||||||
|
|
||||||
async def generate_rag_content(self, message: Message, job_requirements: Dict[str, Any]) -> AsyncGenerator[Message, None]:
|
async def generate_rag_content(self, message: Message, job_requirements: Dict[str, Any]) -> AsyncGenerator[Message, None]:
|
||||||
results = self.process_job_requirements(job_requirements = job_requirements)
|
results = self.process_job_requirements(job_requirements = job_requirements)
|
||||||
message.response = f"Retrieved {len(results)} documents:\n"
|
message.response = f"Retrieved {len(results)} documents."
|
||||||
for result in results:
|
message.metadata["rag_context"] = results
|
||||||
message.response += f"""
|
# for result in results:
|
||||||
ID: {result['id']}, Context: {result['context']}, \
|
# message.response += f"""
|
||||||
Category: {result['category']}/{result['subcategory']}, \
|
# ID: {result['id']}, Context: {result['context']}, \
|
||||||
Similarity Score: {result['score']:.3f}, \
|
# Category: {result['category']}/{result['subcategory']}, \
|
||||||
Combined Score: {result['weight'] * result['score']:.3f}, \
|
# Similarity Score: {result['score']:.3f}, \
|
||||||
Content: {result['content']}
|
# Combined Score: {result['weight'] * result['score']:.3f}, \
|
||||||
"""
|
# Content: {result['content']}
|
||||||
|
# """
|
||||||
message.status = "done"
|
message.status = "done"
|
||||||
yield message
|
yield message
|
||||||
return
|
return
|
||||||
@ -1153,17 +1176,14 @@ Content: {result['content']}
|
|||||||
if message.status == "error":
|
if message.status == "error":
|
||||||
return
|
return
|
||||||
|
|
||||||
yield message
|
rag_context = message.metadata["rag_context"]
|
||||||
return
|
|
||||||
|
|
||||||
additional_context = message.preamble["context"]
|
|
||||||
metadata["analyze_candidate_qualifications"] = {
|
metadata["analyze_candidate_qualifications"] = {
|
||||||
"additional_context": additional_context
|
"rag_context": rag_context
|
||||||
}
|
}
|
||||||
async for message in self.analyze_candidate_qualifications(
|
async for message in self.analyze_candidate_qualifications(
|
||||||
message=message,
|
message=message,
|
||||||
resume=resume,
|
resume=resume,
|
||||||
additional_context=additional_context,
|
rag_context=rag_context,
|
||||||
metadata=metadata["analyze_candidate_qualifications"]):
|
metadata=metadata["analyze_candidate_qualifications"]):
|
||||||
if message.status != "done":
|
if message.status != "done":
|
||||||
yield message
|
yield message
|
||||||
|
Loading…
x
Reference in New Issue
Block a user