o
    MIhvu                     @   s  d dl mZ d dlmZ d dlmZ d dlmZ d dlZd dl	Z	d dl
mZmZmZ d dlmZ eG dd	 d	ZG d
d deZG dd deZG dd dZedkred eddZed ed ed dZg dZdZdZed ede  ede  ede  ede  ejeeeed Zed! red" ed# Zed$ ed%ed&   ed'ed(   ed)ed*   ed+ Zed, Zed- ed.ed/   ed0ed1 d2   ed3ed1 d4   ed5ed1 d6   ed7ed8   ed9ed:  d; ed< ed= ed>d?d@dAdBd@dCdDd@dEdFd@dGdHd@dI  ed= e edJ ddK dLD ]+\Z!Z"ee!d?d@e"dM dBd@e"dN dOd@e"dP dFd@e"dQ dHd@e"dR dSd; qCedT edU e edJ ddV dLD ];\Z!Z"edWe! dXe"dM  dYe"dN dSdZ ed[e"dQ   ed\e"d] dSd^e"dR dSd; ed)e"d*   qdS ed_e#d`da  edbe#dcdd  dS )e    )OllamaPromptTemplate)BaseOutputParser)LLMChainN)DictListOptional)	dataclassc                   @   s&   e Zd ZU eed< eed< eed< dS )	SkillDatanamecountlevelN)__name__
__module____qualname__str__annotations__int r   r   N/var/www/eduai.edurigo.com/doc_train/edurigo_ai/TNA/testing/tna_skill_count.pyr   
   s   
 r   c                   @   4   e Zd ZdZdedefddZdedefddZdS )	DepartmentOutputParserz-Parser for Step 1 - Department Identificationtextreturnc              
   C   sl   zt d|t j}|rt| W S | |W S  ty5 } zdddt| dW  Y d }~S d }~ww )N\{.*?\}UnknownlowzParse error: identified_department
confidence	reasoning)	researchDOTALLjsonloadsgroup_extract_department_from_text	Exceptionr   )selfr   
json_matcher   r   r   parse   s   zDepartmentOutputParser.parsec                 C   s   ddddS )Nr   mediumz#Could not parse structured responser   r   )r*   r   r   r   r   r(   "   s   z4DepartmentOutputParser._extract_department_from_textN)r   r   r   __doc__r   r   r-   r(   r   r   r   r   r      s    r   c                   @   r   )	SkillAnalysisOutputParserz+Parser for Step 2 - Skill Priority Analysisr   r   c              
   C   s   z?t d|t j}|rt| }d|dW S t d|t j}|r:t| }dt|tr5|gdW S |dW S | |W S  t	yY } zdt
|g dW  Y d }~S d }~ww )Nz\[.*?\]T)successskill_recommendationsr   Fr1   errorr2   )r"   r#   r$   r%   r&   r'   
isinstancedict_extract_skills_from_textr)   r   )r*   r   r+   skills_data
skill_datar,   r   r   r   r-   ,   s.   zSkillAnalysisOutputParser.parsec           
         s  g }|  d}i }|D ]  rdrqtd}|r8|r.d|v r.|| d|d  i}qtfdddD rc|rPd|v rP|| i }td	d
dd   }||d< qd	 v r{t
d}|rzt|d|d< qtfdddD rdd    t fdddD r |d< qd	 v rt
d}|rt|d|d< q|rd|v r|| |D ]&}	|	dd |	dd |	dd |	dd |	dd |	dd q|rd nd!|t|d"S )#z,Enhanced text extraction for multiple skills
#z^\d+\.\s*(.+)
skill_name   c                 3       | ]	}|   v V  qd S Nlower.0keywordliner   r   	<genexpr>_       zFSkillAnalysisOutputParser._extract_skills_from_text.<locals>.<genexpr>)zskill:zskill_name:- z^[\-\*\d\.\s]* :priorityz(\d+(?:\.\d+)?)priority_percentagec                 3   r>   r?   r@   rB   rE   r   r   rG   l   rH   )zlevel:requiredc                 3   r>   r?   r@   )rC   r   )
level_textr   r   rG   n   rH   )beginnerintermediateexpertrequired_levelcoveragetarget_coverageg      I@Intermediatecurrent_coverageg        g      N@gap_typezMissing Skillr!   z)Standard skill requirement for departmentTF)r1   r2   extracted_count)stripsplit
startswithr"   matchappendr'   anysubrA   r#   float
setdefaultlen)
r*   r   skillslinescurrent_skillnumbered_matchr<   priority_matchcoverage_matchskillr   )rP   rF   r   r7   I   s^   




z3SkillAnalysisOutputParser._extract_skills_from_textN)r   r   r   r/   r   r   r-   r7   r   r   r   r   r0   )   s    r0   c                
   @   s*  e Zd Zd*dedefddZdefdd	Zdefd
dZdedee de	fddZ
dededee de	fddZdedee dedede	f
ddZdedee dee fddZdee dedefddZdedee fdd Zd!edefd"d#Zd$ee	 dedee	 fd%d&Zd$ee	 de	fd'd(Zd)S )+TwoStepTNASystem
gemma3:12bhttp://localhost:11434
model_namebase_urlc                 C   sf   || _ || _t||ddd| _t | _t | _t| j| 	 | jdd| _
t| j|  | jdd| _dS )z
        Initialize Two-Step TNA System
        
        Args:
            model_name: Ollama model name
            base_url: Ollama server URL
        g?i  )modelrp   temperaturenum_predictF)llmpromptoutput_parserverboseN)ro   rp   r   rt   r   dept_parserr0   skill_parserr   _create_department_prompt
dept_chain_create_skill_analysis_promptskill_chain)r*   ro   rp   r   r   r   __init__   s,   zTwoStepTNASystem.__init__r   c                 C   s   d}t ddg|dS )z=Create prompt template for Step 1 - Department Identificationas  
You are an expert at identifying organizational departments based on user descriptions.

TASK: Analyze the user prompt and identify which specific department they are referring to from the organization's department list.

ORGANIZATION DEPARTMENTS: {organization_departments}

USER PROMPT: "{user_prompt}"

INSTRUCTIONS:
1. Carefully analyze keywords, responsibilities, and context in the user prompt
2. Match the description to the most appropriate department from the provided list
3. Consider typical department functions and responsibilities
4. Choose the best match from the given department list
5. Provide confidence level and clear reasoning

RESPOND IN THIS EXACT JSON FORMAT:
{{
    "identified_department": "exact department name from the list",
    "confidence": "high/medium/low",
    "reasoning": "brief explanation of why this department was chosen"
}}

JSON Response:user_promptorganization_departmentsinput_variablestemplater   r*   r   r   r   r   rz      s
   z*TwoStepTNASystem._create_department_promptc                 C   s   d}t g d|dS )z;Create prompt template for Step 2 - Skill Priority Analysisa	  
You are an expert Training Need Analysis (TNA) consultant specializing in skill gap analysis.

TASK: Analyze the current skills for the {department} department and recommend skill priorities that need to be addressed.

DEPARTMENT: {department}
TOTAL EMPLOYEES: {employee_count}
CURRENT SKILLS: {current_skills}

ANALYSIS REQUIREMENTS:

1. COVERAGE ANALYSIS:
   - Calculate current skill coverage percentages
   - Identify skills with insufficient coverage
   - Standard target: 50-70% coverage for most skills, 70%+ for critical skills

2. LEVEL ANALYSIS:
   - Assess if current skill levels are appropriate for the department
   - Consider if Beginner/Intermediate/Expert levels match job requirements

3. MISSING SKILLS:
   - Identify essential skills that are completely missing
   - Consider both technical and soft skills needed for this department

4. PRIORITY CALCULATION:
   - Base priority on: coverage gaps, level gaps, business impact, urgency
   - Priority scale: 90-100% (Critical), 70-89% (Important), 50-69% (Moderate), 30-49% (Nice-to-have), 10-29% (Low)

5. DEPARTMENT-SPECIFIC FOCUS:
   - Consider the core functions and responsibilities of {department}
   - Include industry-standard skills for this department type
   - Think about both current needs and future requirements

IMPORTANT: You MUST provide EXACTLY 10-15 skill recommendations. Include:
- Analysis of existing skills that need improvement
- Essential missing skills for this department
- Both technical and soft skills
- Skills at different priority levels

RESPOND WITH JSON ARRAY OF EXACTLY 10-15 SKILL RECOMMENDATIONS:

[
  {{
    "skill_name": "exact skill name",
    "required_level": "Beginner/Intermediate/Expert",
    "priority_percentage": 85.5,
    "current_coverage": 25.0,
    "target_coverage": 70.0,
    "gap_type": "Coverage Gap/Level Gap/Missing Skill/Enhancement",
    "reasoning": "detailed explanation of why this skill needs attention and the assigned priority"
  }},
  {{
    "skill_name": "another skill name",
    "required_level": "Intermediate",
    "priority_percentage": 78.0,
    "current_coverage": 0.0,
    "target_coverage": 60.0,
    "gap_type": "Missing Skill",
    "reasoning": "explanation for this skill priority"
  }}
]

CRITICAL: Provide MINIMUM 10 skills, MAXIMUM 15 skills. Do not provide fewer than 10 recommendations.
Sort by priority percentage (highest first).
Include a variety of skills: existing skills needing improvement + completely missing essential skills.

JSON Response:
departmentemployee_countcurrent_skillsr   r   r   r   r   r   r|      s
   Dz.TwoStepTNASystem._create_skill_analysis_promptr   r   c              
   C   s   zct d t d|  t d|  |d|d}| j|}t|tr]|dd}| ||}|rN||d< d|d	< t d
| d|dd d |W S d|d	< d| d|d< |W S ddddW S  ty} } zdt	|ddW  Y d}~S d}~ww )a[  
        STEP 1: Use LLM to identify department from user prompt
        
        Args:
            user_prompt: User's description/prompt about the department
            organization_departments: List of departments in the organization
            
        Returns:
            Dict with identified department, confidence, and reasoning
        u2   🔄 STEP 1: Identifying department from prompt...z   Prompt: z   Available Departments: z, )r   r   r   rJ   Tr1   u!   ✅ STEP 1 Complete: Identified 'z' with r    unknownz confidenceFzDepartment 'z ' not found in organization listr4   z2Failed to parse department identification responseN)r1   r4   r   )
printjoinr{   runr5   r6   get_match_department_to_listr)   r   )r*   r   r   chain_inputresultidentified_deptmatched_deptr,   r   r   r   step1_identify_department  s<   
z*TwoStepTNASystem.step1_identify_departmentr   r   r   c                 C   s  zt d| d t d|  t dt| d | ||}|||d}| j|}t dt|  t dt|d	d
  d t|tr|	drt|	dg }t d| d | 
|d |}d||t||| |t|j|t|dd}	t dt| d |	W S t d t|}
| j|
}|	dr|	dr| 
|d |}t dt| d d||t||| |d|	ddddW S ddg dtt|dkrt|d	d d nt|idW S  ty } zdt|g d W  Y d	}~S d	}~ww )!a  
        STEP 2: Use LLM to analyze skills for the identified department
        
        Args:
            department: Department identified from Step 1
            employee_count: Total number of employees in department
            current_skills: List of current skills with counts and levels
            
        Returns:
            Dict with skill priority recommendations
        u"   🔄 STEP 2: Analyzing skills for z department...   Employee Count:    Current Skills: z skills providedr   u    🔍 Debug - LLM Response type: u   🔍 Debug - Response preview: N   z...r1   r2   u   🔍 Debug - Parsed z skills from JSON responseT)raw_response_typeraw_skill_countprocessed_skill_count)r1   r   r   total_recommendationsr2   analysis_summary
debug_infou   ✅ STEP 2 Complete: Generated z skill recommendationsuY   ⚠️  Warning: Received non-dict or unsuccessful response, attempting manual parsing...u0   ✅ STEP 2 Complete (manual parsing): Generated manual_extractionrZ   r   )parsing_methodrZ   FzIFailed to parse skill analysis response with both JSON and manual methodsraw_responsei  )r1   r4   r2   r   r3   )r   rd   _format_skills_for_llmr}   r   typer   r5   r6   r   _post_process_recommendations_generate_summaryr   ry   r7   r)   )r*   r   r   r   skills_descriptionr   r   skill_countprocessed_recsfinal_resultraw_textmanual_resultr,   r   r   r   step2_analyze_skillsS  s|   
,	z%TwoStepTNASystem.step2_analyze_skillsskills_inputc           	      C   s   t d t d | ||}|dsdd|dd|dS |d	 }| |}| |||}|ds?dd
|dd||dS d|||d |d dS )a  
        Complete two-step TNA analysis
        
        Args:
            user_prompt: User's description of department/team
            organization_departments: List of organization's departments
            employee_count: Total employees in the department
            skills_input: Current skills data (formatted string)
            
        Returns:
            Complete analysis results from both steps
        u&   🚀 Starting Complete TNA Analysis...zP================================================================================r1   Fr=   r4   z Department identification failed)r1   stepr4   step1_resultr      zSkill analysis failed)r1   r   r4   r   step2_resultTr2   r   )r1   r   r   final_recommendationsr   )r   r   r   _parse_skill_inputr   )	r*   r   r   r   r   r   r   r   r   r   r   r   complete_tna_analysis  s2   





z&TwoStepTNASystem.complete_tna_analysisr   	dept_listc                 C   s`   |sdS |   }|D ]}|  |kr|  S q|D ]}||  v s)|  |v r-|  S qdS )z?Match identified department with organization's department listN)rA   r[   )r*   r   r   identified_lowerdeptr   r   r   r     s   z*TwoStepTNASystem._match_department_to_listre   c                 C   s`   |sdS g }|D ]"}|j | d }|d|j d|j  d| d|dd|j d	 qd
|S )z#Format current skills for LLM inputz No current skills data provided.d   rI   z: /z employees (.1fz% coverage) at z levelr:   )r   r_   r   r   r   )r*   re   r   	formattedrk   rU   r   r   r   r     s   *
z'TwoStepTNASystem._format_skills_for_llmc           	      C   s   g }|  s|S td|  }|D ]-}td|  }|r?|d  }t|d}| |d  }|t||| q|S )z/Parse skill input string into SkillData objectsz,\s*z(.+?)-(\d+)\s*\((.+?)\)r=   r      )	r[   r"   r\   r^   r'   r   _normalize_skill_levelr_   r   )	r*   r   re   skill_partspartr^   r<   r   r   r   r   r   r     s   z#TwoStepTNASystem._parse_skill_inputr   c                 C   sH   |  }|drdS |dsd|v rdS |ds |dr"dS dS )	z(Normalize skill level to standard formatbeginBeginnerinterr.   rW   rS   advExpert)rA   r]   )r*   r   level_lowerr   r   r   r   #  s   
z'TwoStepTNASystem._normalize_skill_levelrecommendationsc                 C   s   g }|D ]g}zZ| dd | | ddtdtdt| ddtdtdt| d	dtdtdt| d
d| dd | dd d}t|d
 d | |d< || W q tt	fyk   Y qw |j
dd dd |S )z)Post-process and validate recommendationsr<   r   rT   rW   r   r   rN   2   rX   rV   rY   r!   zNo reasoning provided)r<   rT   rN   rX   rV   rY   r!   target_employee_countc                 S   s   | d S )NrN   r   )xr   r   r   <lambda>I  s    z@TwoStepTNASystem._post_process_recommendations.<locals>.<lambda>T)keyreverse)r   r[   r   maxminrb   r   r_   
ValueError	TypeErrorsort)r*   r   r   	processedrecprocessed_recr   r   r   r   .  s(   z.TwoStepTNASystem._post_process_recommendationsc                 C   s   |sddiS t dd |D }t dd |D }t dd |D }tdd |D t | }t ||||d	t|d
|rD|d d dS ddS )zGenerate analysis summarymessagezNo recommendations generatedc                 S   s   g | ]
}|d  dkr|qS )rN   P   r   rC   rr   r   r   
<listcomp>Q      z6TwoStepTNASystem._generate_summary.<locals>.<listcomp>c                 S   s,   g | ]}d |d   krdk rn n|qS )r   rN   r   r   r   r   r   r   r   R  s   , c                 S   s   g | ]
}|d  dk r|qS )rN   r   r   r   r   r   r   r   S  r   c                 s   s    | ]}|d  V  qdS )rN   Nr   r   r   r   r   rG   U  s    z5TwoStepTNASystem._generate_summary.<locals>.<genexpr>)high_prioritymedium_prioritylow_priorityr=   r   r<   N)total_skillspriority_distributionaverage_prioritytop_priority_skill)rd   sumround)r*   r   r   r   r   avg_priorityr   r   r   r   L  s    z"TwoStepTNASystem._generate_summaryN)rm   rn   )r   r   r   r   r~   r   rz   r|   r   r   r   r   r   r   r   r	   r   r   r   r   r   r   r   r   r   r   rl      s.    '!L
5
f
6rl   __main__z#Initializing Two-Step TNA System...rm   )ro   ze
====================================================================================================z'EXAMPLE: COMPLETE TWO-STEP TNA ANALYSISzd====================================================================================================zZWe need training for our team that handles customer complaints and manages support tickets)Sales	MarketingzHuman ResourceszInformation TechnologyFinancezCustomer Service
Operations   zxCommunication-12(Intermediate), Active Listening-6(Intermediate), CRM Tools-5(Beginner), Problem Solving-8(Intermediate)u   📝 Input Data:z   User Prompt: z   Organization Departments: r   r   )r   r   r   r   r1   u#   
🎉 COMPLETE ANALYSIS SUCCESSFUL!r   u   
📍 STEP 1 RESULTS:z   Identified Department: r   z   Confidence: r    z   Reasoning: r!   r   r   u   
📊 STEP 2 RESULTS SUMMARY:z   Total Recommendations: r   z   High Priority Skills: r   r   z   Medium Priority Skills: r   z   Low Priority Skills: r   z   Top Priority Skill: r   z   Average Priority: r   %u#   
🎯 TOP 10 SKILL RECOMMENDATIONS:zx------------------------------------------------------------------------------------------------------------------------Rankz<4 z
Skill Namez<25z
Priority %z<10zRequired Levelz<15zGap Typez<20zTarget Coverager   
   r=   r<   rN   z<10.1frT   rY   rV   r   u#   
💡 DETAILED REASONING FOR TOP 3:zP--------------------------------------------------------------------------------r   r:   z. z (z% priority)z   Gap Type: z   Current Coverage: rX   u   % → Target: u   ❌ Analysis Failed at Step r   r   z
   Error: r4   zUnknown error)$langchain_community.llmsr   langchain.promptsr   langchain.schemar   langchain.chainsr   r%   r"   typingr   r   r	   dataclassesr
   r   r   r0   rl   r   r   
tna_systemr   r   r   r   r   r   step1step2summary	enumerateir   r   r   r   r   r   <module>   s    `   
]

4"&"