- RAG Specialist fully implemented new style
- Selection Specialist - VA version - fully implemented - Correction of TRAICIE_ROLE_DEFINITION_SPECIALIST - adaptation to new style - Removal of 'debug' statements
This commit is contained in:
@@ -71,11 +71,6 @@ class SpecialistExecutor(CrewAIBaseSpecialistExecutor):
|
||||
def execute(self, arguments: SpecialistArguments, formatted_context, citations) -> SpecialistResult:
|
||||
self.log_tuning("Traicie KO Criteria Interview Definition Specialist execution started", {})
|
||||
|
||||
current_app.logger.debug(f"Arguments: {arguments.model_dump()}")
|
||||
current_app.logger.debug(f"Formatted Context: {formatted_context}")
|
||||
current_app.logger.debug(f"Formatted History: {self._formatted_history}")
|
||||
current_app.logger.debug(f"Cached Chat Session: {self._cached_session}")
|
||||
|
||||
if not self._cached_session.interactions:
|
||||
specialist_phase = "initial"
|
||||
else:
|
||||
@@ -104,13 +99,9 @@ class SpecialistExecutor(CrewAIBaseSpecialistExecutor):
|
||||
raise EveAISpecialistExecutionError(self.tenant_id, self.specialist_id, self.session_id,
|
||||
"Specialist is no Selection Specialist")
|
||||
|
||||
current_app.logger.debug(f"Specialist Competencies:\n"
|
||||
f"{selection_specialist.configuration.get("competencies", [])}")
|
||||
|
||||
ko_competencies = []
|
||||
for competency in selection_specialist.configuration.get("competencies", []):
|
||||
if competency["is_knockout"] is True and competency["assess"] is True:
|
||||
current_app.logger.debug(f"Assessable Knockout competency: {competency}")
|
||||
ko_competencies.append({"title": competency["title"], "description": competency["description"]})
|
||||
|
||||
tone_of_voice = selection_specialist.configuration.get('tone_of_voice', 'Professional & Neutral')
|
||||
@@ -118,7 +109,6 @@ class SpecialistExecutor(CrewAIBaseSpecialistExecutor):
|
||||
(item for item in TONE_OF_VOICE if item["name"] == tone_of_voice),
|
||||
None # fallback indien niet gevonden
|
||||
)
|
||||
current_app.logger.debug(f"Selected tone of voice: {selected_tone_of_voice}")
|
||||
tone_of_voice_context = f"{selected_tone_of_voice["description"]}"
|
||||
|
||||
language_level = selection_specialist.configuration.get('language_level', 'Standard')
|
||||
@@ -126,7 +116,6 @@ class SpecialistExecutor(CrewAIBaseSpecialistExecutor):
|
||||
(item for item in LANGUAGE_LEVEL if item["name"] == language_level),
|
||||
None
|
||||
)
|
||||
current_app.logger.debug(f"Selected language level: {selected_language_level}")
|
||||
language_level_context = (f"{selected_language_level['description']}, "
|
||||
f"corresponding to CEFR level {selected_language_level['cefr_level']}")
|
||||
|
||||
@@ -140,12 +129,9 @@ class SpecialistExecutor(CrewAIBaseSpecialistExecutor):
|
||||
}
|
||||
|
||||
flow_results = self.flow.kickoff(inputs=flow_inputs)
|
||||
current_app.logger.debug(f"Flow results: {flow_results}")
|
||||
current_app.logger.debug(f"Flow state: {self.flow.state}")
|
||||
|
||||
new_type = "TRAICIE_KO_CRITERIA_QUESTIONS"
|
||||
|
||||
current_app.logger.debug(f"KO Criteria Questions:\n {self.flow.state.ko_questions}")
|
||||
# Controleer of we een KOQuestions object hebben of een lijst van KOQuestion objecten
|
||||
if hasattr(self.flow.state.ko_questions, 'to_json'):
|
||||
# Het is een KOQuestions object
|
||||
@@ -161,8 +147,6 @@ class SpecialistExecutor(CrewAIBaseSpecialistExecutor):
|
||||
ko_questions_data = [q.model_dump() for q in self.flow.state.ko_questions]
|
||||
json_str = json.dumps(ko_questions_data, ensure_ascii=False, indent=2)
|
||||
|
||||
current_app.logger.debug(f"KO Criteria Questions json style:\n {json_str}")
|
||||
|
||||
try:
|
||||
asset = db.session.query(EveAIAsset).filter(
|
||||
EveAIAsset.type == new_type,
|
||||
@@ -281,21 +265,17 @@ class KOFlow(EveAICrewAIFlow[KOFlowState]):
|
||||
async def execute_ko_def_definition(self):
|
||||
inputs = self.state.input.model_dump()
|
||||
try:
|
||||
current_app.logger.debug("Run execute_ko_interview_definition")
|
||||
crew_output = await self.ko_def_crew.kickoff_async(inputs=inputs)
|
||||
# Unfortunately, crew_output will only contain the output of the latest task.
|
||||
# As we will only take into account the flow state, we need to ensure both competencies and criteria
|
||||
# are copies to the flow state.
|
||||
update = {}
|
||||
for task in self.ko_def_crew.tasks:
|
||||
current_app.logger.debug(f"Task {task.name} output:\n{task.output}")
|
||||
if task.name == "traicie_ko_criteria_interview_definition_task":
|
||||
# update["competencies"] = task.output.pydantic.competencies
|
||||
self.state.ko_questions = task.output.pydantic.ko_questions
|
||||
# crew_output.pydantic = crew_output.pydantic.model_copy(update=update)
|
||||
self.state.phase = "personal_contact_data"
|
||||
current_app.logger.debug(f"State after execute_ko_def_definition: {self.state}")
|
||||
current_app.logger.debug(f"State dump after execute_ko_def_definition: {self.state.model_dump()}")
|
||||
return crew_output
|
||||
except Exception as e:
|
||||
current_app.logger.error(f"CREW execute_ko_def Kickoff Error: {str(e)}")
|
||||
@@ -303,9 +283,6 @@ class KOFlow(EveAICrewAIFlow[KOFlowState]):
|
||||
raise e
|
||||
|
||||
async def kickoff_async(self, inputs=None):
|
||||
current_app.logger.debug(f"Async kickoff {self.name}")
|
||||
current_app.logger.debug(f"Inputs: {inputs}")
|
||||
self.state.input = KODefInput.model_validate(inputs)
|
||||
current_app.logger.debug(f"State: {self.state}")
|
||||
result = await super().kickoff_async(inputs)
|
||||
return self.state
|
||||
|
||||
@@ -143,8 +143,6 @@ class VacancyDefinitionFlow(EveAICrewAIFlow[VacancyDefFlowState]):
|
||||
# update["criteria"] = task.output.pydantic.criteria
|
||||
self.state.criteria = task.output.pydantic.criteria
|
||||
# crew_output.pydantic = crew_output.pydantic.model_copy(update=update)
|
||||
current_app.logger.debug(f"State after execute_vac_def: {self.state}")
|
||||
current_app.logger.debug(f"State dump after execute_vac_def: {self.state.model_dump()}")
|
||||
return crew_output
|
||||
except Exception as e:
|
||||
current_app.logger.error(f"CREW execute_vac_def Kickoff Error: {str(e)}")
|
||||
@@ -152,7 +150,6 @@ class VacancyDefinitionFlow(EveAICrewAIFlow[VacancyDefFlowState]):
|
||||
raise e
|
||||
|
||||
async def kickoff_async(self, inputs=None):
|
||||
current_app.logger.debug(f"Async kickoff {self.name}")
|
||||
self.state.input = VacancyDefinitionSpecialistInput.model_validate(inputs)
|
||||
result = await super().kickoff_async(inputs)
|
||||
return self.state
|
||||
|
||||
@@ -168,20 +168,16 @@ class RoleDefinitionFlow(EveAICrewAIFlow[RoleDefFlowState]):
|
||||
async def execute_role_definition (self):
|
||||
inputs = self.state.input.model_dump()
|
||||
try:
|
||||
current_app.logger.debug("In execute_role_definition")
|
||||
crew_output = await self.role_definition_crew.kickoff_async(inputs=inputs)
|
||||
# Unfortunately, crew_output will only contain the output of the latest task.
|
||||
# As we will only take into account the flow state, we need to ensure both competencies and criteria
|
||||
# are copies to the flow state.
|
||||
update = {}
|
||||
for task in self.role_definition_crew.tasks:
|
||||
current_app.logger.debug(f"Task {task.name} output:\n{task.output}")
|
||||
if task.name == "traicie_get_competencies_task":
|
||||
# update["competencies"] = task.output.pydantic.competencies
|
||||
self.state.competencies = task.output.pydantic.competencies
|
||||
# crew_output.pydantic = crew_output.pydantic.model_copy(update=update)
|
||||
current_app.logger.debug(f"State after execute_role_definition: {self.state}")
|
||||
current_app.logger.debug(f"State dump after execute_role_definition: {self.state.model_dump()}")
|
||||
return crew_output
|
||||
except Exception as e:
|
||||
current_app.logger.error(f"CREW execute_role_definition Kickoff Error: {str(e)}")
|
||||
@@ -189,9 +185,6 @@ class RoleDefinitionFlow(EveAICrewAIFlow[RoleDefFlowState]):
|
||||
raise e
|
||||
|
||||
async def kickoff_async(self, inputs=None):
|
||||
current_app.logger.debug(f"Async kickoff {self.name}")
|
||||
current_app.logger.debug(f"Inputs: {inputs}")
|
||||
self.state.input = RoleDefinitionSpecialistInput.model_validate(inputs)
|
||||
current_app.logger.debug(f"State: {self.state}")
|
||||
result = await super().kickoff_async(inputs)
|
||||
return self.state
|
||||
|
||||
@@ -174,20 +174,16 @@ class RoleDefinitionFlow(EveAICrewAIFlow[RoleDefFlowState]):
|
||||
async def execute_role_definition (self):
|
||||
inputs = self.state.input.model_dump()
|
||||
try:
|
||||
current_app.logger.debug("In execute_role_definition")
|
||||
crew_output = await self.role_definition_crew.kickoff_async(inputs=inputs)
|
||||
# Unfortunately, crew_output will only contain the output of the latest task.
|
||||
# As we will only take into account the flow state, we need to ensure both competencies and criteria
|
||||
# are copies to the flow state.
|
||||
update = {}
|
||||
for task in self.role_definition_crew.tasks:
|
||||
current_app.logger.debug(f"Task {task.name} output:\n{task.output}")
|
||||
if task.name == "traicie_get_competencies_task":
|
||||
# update["competencies"] = task.output.pydantic.competencies
|
||||
self.state.competencies = task.output.pydantic.competencies
|
||||
# crew_output.pydantic = crew_output.pydantic.model_copy(update=update)
|
||||
current_app.logger.debug(f"State after execute_role_definition: {self.state}")
|
||||
current_app.logger.debug(f"State dump after execute_role_definition: {self.state.model_dump()}")
|
||||
return crew_output
|
||||
except Exception as e:
|
||||
current_app.logger.error(f"CREW execute_role_definition Kickoff Error: {str(e)}")
|
||||
@@ -195,9 +191,6 @@ class RoleDefinitionFlow(EveAICrewAIFlow[RoleDefFlowState]):
|
||||
raise e
|
||||
|
||||
async def kickoff_async(self, inputs=None):
|
||||
current_app.logger.debug(f"Async kickoff {self.name}")
|
||||
current_app.logger.debug(f"Inputs: {inputs}")
|
||||
self.state.input = RoleDefinitionSpecialistInput.model_validate(inputs)
|
||||
current_app.logger.debug(f"State: {self.state}")
|
||||
result = await super().kickoff_async(inputs)
|
||||
return self.state
|
||||
|
||||
@@ -61,6 +61,7 @@ class SpecialistExecutor(CrewAIBaseSpecialistExecutor):
|
||||
|
||||
# Load the Tenant & set language
|
||||
self.tenant = Tenant.query.get_or_404(tenant_id)
|
||||
self.specialist_phase = "initial"
|
||||
|
||||
@property
|
||||
def type(self) -> str:
|
||||
@@ -106,19 +107,13 @@ class SpecialistExecutor(CrewAIBaseSpecialistExecutor):
|
||||
def execute(self, arguments: SpecialistArguments, formatted_context, citations) -> SpecialistResult:
|
||||
self.log_tuning("Traicie Selection Specialist execution started", {})
|
||||
|
||||
current_app.logger.debug(f"Arguments: {arguments.model_dump()}")
|
||||
current_app.logger.debug(f"Formatted Context: {formatted_context}")
|
||||
current_app.logger.debug(f"Formatted History: {self._formatted_history}")
|
||||
current_app.logger.debug(f"Cached Chat Session: {self._cached_session}")
|
||||
|
||||
if not self._cached_session.interactions:
|
||||
specialist_phase = "initial"
|
||||
self.specialist_phase = "initial"
|
||||
else:
|
||||
specialist_phase = self._cached_session.interactions[-1].specialist_results.get('phase', 'initial')
|
||||
self.specialist_phase = self._cached_session.interactions[-1].specialist_results.get('phase', 'initial')
|
||||
|
||||
results = None
|
||||
current_app.logger.debug(f"Specialist phase: {specialist_phase}")
|
||||
match specialist_phase:
|
||||
match self.specialist_phase:
|
||||
case "initial":
|
||||
results = self.execute_initial_state(arguments, formatted_context, citations)
|
||||
case "start_selection_procedure":
|
||||
@@ -149,16 +144,21 @@ class SpecialistExecutor(CrewAIBaseSpecialistExecutor):
|
||||
interaction_mode = arguments.interaction_mode
|
||||
if not interaction_mode:
|
||||
interaction_mode = "selection"
|
||||
current_app.logger.debug(f"Interaction mode: {interaction_mode}")
|
||||
|
||||
welcome_message = self.specialist.configuration.get("welcome_message", "Welcome to our selection process.")
|
||||
welcome_message = TranslationServices.translate(self.tenant_id, welcome_message, arguments.language)
|
||||
|
||||
if interaction_mode == "selection":
|
||||
return self.execute_start_selection_procedure_state(arguments, formatted_context, citations,
|
||||
welcome_message)
|
||||
else: # We are in orientation mode, so we perform standard rag
|
||||
return self.execute_rag_state(arguments, formatted_context, citations, welcome_message)
|
||||
# We are in orientation mode, so we give a standard message, and move to rag state
|
||||
start_selection_question = TranslationServices.translate(self.tenant_id, START_SELECTION_QUESTION,
|
||||
arguments.language)
|
||||
self.flow.state.answer = f"{welcome_message}\n\n{start_selection_question}"
|
||||
self.flow.state.phase = "rag"
|
||||
|
||||
results = SelectionResult.create_for_type(self.type, self.type_version)
|
||||
|
||||
return results
|
||||
|
||||
def execute_start_selection_procedure_state(self, arguments: SpecialistArguments, formatted_context, citations,
|
||||
start_message=None) -> SpecialistResult:
|
||||
@@ -172,7 +172,6 @@ class SpecialistExecutor(CrewAIBaseSpecialistExecutor):
|
||||
ko_questions = self._get_ko_questions()
|
||||
fields = {}
|
||||
for ko_question in ko_questions.ko_questions:
|
||||
current_app.logger.debug(f"KO Question: {ko_question}")
|
||||
fields[ko_question.title] = {
|
||||
"name": ko_question.title,
|
||||
"description": ko_question.title,
|
||||
@@ -213,11 +212,9 @@ class SpecialistExecutor(CrewAIBaseSpecialistExecutor):
|
||||
if not arguments.form_values:
|
||||
raise EveAISpecialistExecutionError(self.tenant_id, self.specialist_id, self.session_id,
|
||||
"No form values returned")
|
||||
current_app.logger.debug(f"Form values: {arguments.form_values}")
|
||||
|
||||
# Load the previous KO Questions
|
||||
previous_ko_questions = self._get_ko_questions().ko_questions
|
||||
current_app.logger.debug(f"Previous KO Questions: {previous_ko_questions}")
|
||||
|
||||
# Evaluate KO Criteria
|
||||
evaluation = "positive"
|
||||
@@ -355,39 +352,37 @@ class SpecialistExecutor(CrewAIBaseSpecialistExecutor):
|
||||
results = SelectionResult.create_for_type(self.type, self.type_version,)
|
||||
return results
|
||||
|
||||
def execute_rag_state(self, arguments: SpecialistArguments, formatted_context, citations, welcome_message=None) \
|
||||
def execute_rag_state(self, arguments: SpecialistArguments, formatted_context, citations) \
|
||||
-> SpecialistResult:
|
||||
self.log_tuning("Traicie Selection Specialist rag_state started", {})
|
||||
|
||||
start_selection_question = TranslationServices.translate(self.tenant_id, START_SELECTION_QUESTION,
|
||||
arguments.language)
|
||||
if welcome_message:
|
||||
answer = f"{welcome_message}\n\n{start_selection_question}"
|
||||
else:
|
||||
answer = ""
|
||||
|
||||
rag_results = None
|
||||
if arguments.question:
|
||||
if HumanAnswerServices.check_additional_information(self.tenant_id,
|
||||
START_SELECTION_QUESTION,
|
||||
arguments.question,
|
||||
arguments.language):
|
||||
rag_results = self.execute_rag(arguments, formatted_context, citations)
|
||||
self.flow.state.rag_output = rag_results.rag_output
|
||||
answer = f"{answer}\n{rag_results.answer}"
|
||||
rag_output = None
|
||||
|
||||
if HumanAnswerServices.check_affirmative_answer(self.tenant_id,
|
||||
if HumanAnswerServices.check_additional_information(self.tenant_id,
|
||||
START_SELECTION_QUESTION,
|
||||
arguments.question,
|
||||
arguments.language):
|
||||
return self.execute_start_selection_procedure_state(arguments, formatted_context, citations, answer)
|
||||
rag_output = self.execute_rag(arguments, formatted_context, citations)
|
||||
self.flow.state.rag_output = rag_output
|
||||
answer = rag_output.answer
|
||||
else:
|
||||
answer = ""
|
||||
|
||||
self.flow.state.answer = answer
|
||||
self.flow.state.phase = "rag"
|
||||
self.flow.state.form_request = None
|
||||
if HumanAnswerServices.check_affirmative_answer(self.tenant_id,
|
||||
START_SELECTION_QUESTION,
|
||||
arguments.question,
|
||||
arguments.language):
|
||||
return self.execute_start_selection_procedure_state(arguments, formatted_context, citations, answer)
|
||||
else:
|
||||
self.flow.state.answer = f"{answer}\n\n{start_selection_question}"
|
||||
self.flow.state.phase = "rag"
|
||||
self.flow.state.form_request = None
|
||||
|
||||
results = SelectionResult.create_for_type(self.type, self.type_version,)
|
||||
return results
|
||||
results = SelectionResult.create_for_type(self.type, self.type_version,)
|
||||
return results
|
||||
|
||||
def execute_rag(self, arguments: SpecialistArguments, formatted_context, citations) -> RAGOutput:
|
||||
self.log_tuning("RAG Specialist execution started", {})
|
||||
@@ -395,6 +390,9 @@ class SpecialistExecutor(CrewAIBaseSpecialistExecutor):
|
||||
insufficient_info_message = TranslationServices.translate(self.tenant_id,
|
||||
INSUFFICIENT_INFORMATION_MESSAGE,
|
||||
arguments.language)
|
||||
|
||||
formatted_context, citations = self._retrieve_context(arguments)
|
||||
self.flow.state.citations = citations
|
||||
if formatted_context:
|
||||
flow_inputs = {
|
||||
"language": arguments.language,
|
||||
@@ -403,9 +401,11 @@ class SpecialistExecutor(CrewAIBaseSpecialistExecutor):
|
||||
"history": self.formatted_history,
|
||||
"name": self.specialist.configuration.get('name', ''),
|
||||
}
|
||||
rag_output = self.flow.kickoff(inputs=flow_inputs)
|
||||
if rag_output.rag_output.insufficient_info:
|
||||
rag_output.rag_output.answer = insufficient_info_message
|
||||
flow_results = self.flow.kickoff(inputs=flow_inputs)
|
||||
if flow_results.rag_output.insufficient_info:
|
||||
flow_results.rag_output.answer = insufficient_info_message
|
||||
|
||||
rag_output = flow_results.rag_output
|
||||
else:
|
||||
rag_output = RAGOutput(answer=insufficient_info_message, insufficient_info=True)
|
||||
|
||||
@@ -418,8 +418,11 @@ class SpecialistExecutor(CrewAIBaseSpecialistExecutor):
|
||||
START_SELECTION_QUESTION,
|
||||
arguments.question,
|
||||
arguments.language):
|
||||
results = self.execute_rag(arguments, formatted_context, citations)
|
||||
return results
|
||||
rag_output = self.execute_rag(arguments, formatted_context, citations)
|
||||
|
||||
self.flow.state.rag_output = rag_output
|
||||
|
||||
return rag_output
|
||||
else:
|
||||
return None
|
||||
|
||||
@@ -439,7 +442,6 @@ class SpecialistExecutor(CrewAIBaseSpecialistExecutor):
|
||||
ko_questions_data = minio_client.download_asset_file(self.tenant_id, ko_questions_asset.bucket_name,
|
||||
ko_questions_asset.object_name)
|
||||
ko_questions = KOQuestions.from_json(ko_questions_data)
|
||||
current_app.logger.debug(f"KO Questions: {ko_questions}")
|
||||
|
||||
return ko_questions
|
||||
|
||||
@@ -470,8 +472,8 @@ class SelectionInput(BaseModel):
|
||||
history: Optional[str] = Field(None, alias="history")
|
||||
name: Optional[str] = Field(None, alias="name")
|
||||
# Selection elements
|
||||
region: str = Field(..., alias="region")
|
||||
working_schedule: Optional[str] = Field(..., alias="working_schedule")
|
||||
region: Optional[str] = Field(None, alias="region")
|
||||
working_schedule: Optional[str] = Field(None, alias="working_schedule")
|
||||
start_date: Optional[date] = Field(None, alias="vacancy_text")
|
||||
interaction_mode: Optional[str] = Field(None, alias="interaction_mode")
|
||||
tone_of_voice: Optional[str] = Field(None, alias="tone_of_voice")
|
||||
@@ -489,6 +491,7 @@ class SelectionFlowState(EveAIFlowState):
|
||||
ko_criteria_answers: Optional[Dict[str, str]] = None
|
||||
personal_contact_data: Optional[PersonalContactData] = None
|
||||
contact_time: Optional[str] = None
|
||||
citations: Optional[List[Dict[str, Any]]] = None
|
||||
|
||||
|
||||
class SelectionResult(SpecialistResult):
|
||||
@@ -530,7 +533,6 @@ class SelectionFlow(EveAICrewAIFlow[SelectionFlowState]):
|
||||
raise e
|
||||
|
||||
async def kickoff_async(self, inputs=None):
|
||||
current_app.logger.debug(f"Async kickoff {self.name}")
|
||||
self.state.input = SelectionInput.model_validate(inputs)
|
||||
result = await super().kickoff_async(inputs)
|
||||
return self.state
|
||||
|
||||
Reference in New Issue
Block a user