o
    uii                     @   sZ   d dl Z d dlZd dlmZmZmZmZ d dlmZm	Z	m
Z
mZmZmZ G dd dZdS )    N)DictListAnyOptional)SCENARIO_GENERATION_PROMPTCHARACTER_ROLEPLAY_PROMPTCHARACTER_CONCLUSION_PROMPT CHARACTER_GUIDED_ROLEPLAY_PROMPTSKILL_ANALYSIS_PROMPTget_skill_analysis_templatec                   @   s  e Zd Zd6defddZdedefddZd	ed
eeef fddZ	deeef fddZ
dd Zd7dededededeee eeeef  f f
ddZdeeef deeeef  fddZdeeeef  deeeef  defdd Zd8d#eeef deeeef  d$ed%edeeeef  dee fd&d'Zd9d#eeef d(eeeef  d)edeeeef  fd*d+Zdedee fd,d-Zdedefd.d/Zdedeeeef  fd0d1Zdeeef fd2d3Zdefd4d5Zd"S ):OllamaServicehttp://127.0.0.1:11434base_urlc                 C   s6   || _ d| _ddddddddddddd| _d S )Nz
gemma3:12br   inputoutputtotal)previewconversation
assessment)r   modeltoken_counts)selfr    r   F/var/www/eduai.edurigo.com/roleplay/testing/services/ollama_service.py__init__   s   


zOllamaService.__init__textreturnc                 C   s   t dt|d S )uM   Estimate token count for text (rough approximation: 1 token ≈ 4 characters)      )maxlen)r   r   r   r   r   _estimate_tokens   s   zOllamaService._estimate_tokensoperation_typeusage_statsc              	   C   s   |rQ| dd}| dd}| d|| }|dkr|| }| j| d  |7  < | j| d  |7  < | j| d  |7  < td	| d
| d| d|  dS td| d dS )zDUpdate token counts for specific operation using actual Ollama statsprompt_eval_countr   
eval_counttotal_durationi r   r   r   zOLLAMA Token count for z: Input=z	, Output=z, Total=z%OLLAMA: No usage stats available for z, skipping token countN)getr   print)r   r#   r$   input_tokensoutput_tokenstotal_tokensr   r   r   _update_token_count   s   $z!OllamaService._update_token_countc                 C   s
   | j  S )z+Get current token counts for all operations)r   copyr   r   r   r   get_token_counts0   s   
zOllamaService.get_token_countsc                 C   s"   | j D ]}dddd| j |< qdS )zReset all token countersr   r   N)r   )r   	operationr   r   r   reset_token_counts4   s   
z OllamaService.reset_token_counts ffffff?   promptsystem_prompttemperature
max_tokensc              
      sx  z| j  d}td|  td| j  |}|r#d| d| }| j|d||dd}td	|d
   tjdd4 I dH }|j||dI dH }	W d  I dH  n1 I dH s\w   Y  td|	j  |	jdkrztd| j d W dS |	  |	 }
|
	dd
 }|
	dd|
	dd|
	dd|
	dd|
	ddd}tdt|  td|  ||fW S  tjy } ztd|  td | j   W Y d}~dS d}~w tjy } ztd!|  W Y d}~dS d}~w tjy! } ztd!|  |jdurtd"|jj  W Y d}~dS d}~w ty; } ztd#|  W Y d}~dS d}~ww )$zEMake a request to Ollama API and return response text and usage statsz/api/generatez!DEBUG: Making Ollama request to: zDEBUG: Using model: zSystem: z

User: F)r8   num_predict)r   r6   streamoptionszDEBUG: Payload model: r   g      ^@timeoutN)jsonzDEBUG: Response status: i  zERROR: 404 - Model 'z>' not found. Available models can be checked with: ollama list)NNresponser3   r%   r   r&   r'   prompt_eval_durationeval_duration)r%   r&   r'   rA   rB   zDEBUG: Ollama response length: zDEBUG: Ollama usage stats: z#Error connecting to Ollama server: zMake sure Ollama is running on zError making Ollama request: zResponse content: z$Unexpected error in Ollama request: )r   r)   r   httpxAsyncClientpoststatus_coderaise_for_statusr?   r(   stripr!   ConnectErrorRequestErrorHTTPStatusErrorr@   r   	Exception)r   r6   r7   r8   r9   urlfull_promptpayloadclientr@   resultresponse_textr$   er   r   r   _make_request9   sn   
(






zOllamaService._make_requestadmin_inputc              
      s  zd}|d   D ]\}}|| d| d7 }q
d}|d}|r<d}t|dD ]\}}|| d|d	d d7 }q)tj|d
 |d |d |d || d|d d}	d}
| j|	|
dddI dH \}}|skW dS | d| z+|dr~|dd }n|dr|dd }|	dr|dd }t
| W W S  t
jy } ztd|  td|  W Y d}~W dS d}~ww  ty } ztd|  W Y d}~dS d}~ww )z-Generate structured scenario from admin inputr3   details: 
roleplay_questionszO
GUIDED CONVERSATION QUESTIONS (The scenario should accommodate THESE topics):
r   . questioncategory	objectivelearner_roleai_role, skills_to_assess)r\   r]   r^   r_   rV   skillszHYou are a training scenario expert. Always respond with valid JSON only.r4   r5   r6   r7   r8   r9   Nr   ```json   ```   zJSON parsing error: Response text: z'Error generating scenario with Ollama: )itemsr(   	enumerater   formatjoinrT   r-   
startswithendswithr?   loadsrH   JSONDecodeErrorr)   rL   )r   rU   details_textkeyvaluequestions_textrY   idxqr6   r7   rR   r$   rS   r   r   r   generate_scenarioz   s`   
	


zOllamaService.generate_scenariorY   conversation_historyc           
   
      s  |sdS d dd |D   g }t|dD ]7\}}|dd }| dd	 }t fd
d|D }|r:dnd}	|d| d|dd d|	  qt|D ]4\}}|dd }| dd	 }t fdd|D }|s|d|d  d|dd   nqSd |S )z
        Analyze conversation history to determine which guided questions have been addressed.
        Returns a formatted string explaining the progression status.
        z&No guided questions for this roleplay. c                 S      g | ]}| d dqS )messager3   r(   ).0turnr   r   r   
<listcomp>       z?OllamaService._analyze_question_progression.<locals>.<listcomp>r   r[   r3   Nrg   c                 3   $    | ]}t |d kr| v V  qdS rg   Nr!   r~   keywordconversation_textr   r   	<genexpr>      " z>OllamaService._analyze_question_progression.<locals>.<genexpr>u   ✓ ADDRESSEDu   ⚠ NOT YET ADDRESSEDz	Question rW    - c                 3   r   r   r   r   r   r   r   r      r   z
**NEXT FOCUS**: Question rX   )rm   lowerrk   r(   splitanyappend)
r   rY   ry   progression_linesrv   rw   question_textkeywords	addressedstatusr   r   r   _analyze_question_progression   s&   &"
z+OllamaService._analyze_question_progressionFNscenariouser_messageis_conclusionc                    s|  zd}|D ]}|d dkrdn|d d }|| d|d  d	7 }qt d
d |D d }	|dd}
|dd}|dd}|dd}|dd}|rt}|d d |d d |d d |d d |d dd|d d |d d |d d ||d
}n|rt}d}t|dD ]\}}|| d |d!d d	7 }q| ||}i d"|d d d|d d d|d d d|d d d|d ddd|d d d|d d d#|d#dd$|d%i ddd|d d d&|d'|d(|d)|d|
d*|d||||	d+}nit}i d"|d d d|d d d|d d d|d d d|d ddd|d d d|d d d#|d#dd$|d%i ddd|d d d(|d)|d|
d*|d|d|d|d,|	i}|jd5i |}d-}| j||d.d/d0I d1H \}}|r| 	d2| |r|W S d3W S  t
y } ztd4|  W Y d1}~d3S d1}~ww )6z&AI character responses during roleplayr3   speakerlearnerLearnerai_characternamerW   r|   rX   c                 S   s   g | ]}| d dkr|qS )r   r   r}   )r~   tr   r   r   r      s    z0OllamaService.play_character.<locals>.<listcomp>r   roleplay_namezRoleplay Sessionr_   	Characterr^   
difficultymediumdurationi,  personalitygoals
backgroundemotional_stateneutralscenario_setupcontextenvironmentconstraints)
character_namer   r   r   r   r   r   r   ry   r   rZ   r[   r   r]   additional_inforV   guided_questionsquestion_progress_analysisry   r   character_role)r   r   current_turnr   a"  You are a world-class roleplay AI. You MUST stay strictly in character as a human-like counterpart (client, customer, etc.). You NEVER break character. You MUST follow the provided 'MANDATORY GUIDED QUESTIONS' if they exist. Respond professionally, naturally, and concisely (1-3 sentences).g?i   rc   Nr   z:I'm having trouble responding right now. Please try again.z)Error in character roleplay with Ollama: r   )r!   r(   r   r	   rk   r   r   rl   rT   r-   rL   r)   )r   r   ry   r   r   rY   history_textr   r   r   r   r   r^   r   r   prompt_templateprompt_kwargsru   rv   rw   question_progressr6   r7   r@   r$   rS   r   r   r   play_character   s   






	

	
zOllamaService.play_characterconversation_turnsengagement_metadatac                    s  zd}|D ]}|d dkrdnd}|| d|d  d7 }qd	|d
  d|d  d|d d  d|d  d|d d  d|d d  d}|d }t |}	tjd|||||	d}
d}| j|
|dddI dH \}}|spW dS | d| zG|d r|d!d }n|d"r|d#d }|d"r|dd$ }| }| 	|}t
| t fd%d&d'D std( W W dS  W W S  t
jy } ztd)|  td*|  | |W  Y d}~W S d}~ww  ty } ztd+|  W Y d}~dS d}~ww ),z:Comprehensive skill analysis with engagement-aware scoringr3   r   r   r   zAI CharacterrW   r|   rX   z
Category: r\   z
Objective: r]   z

Context: r   r   z
Success Criteria: success_criteriaz
AI Character: r   r   r   r   ra   r`   )rb   scenario_contextr   r   skill_analysis_templatezYou are an expert skill assessor. Provide STRICT, ACCURATE analysis in valid JSON format only. Do NOT inflate scores. Follow the engagement-based score ceilings exactly.g333333?i  rc   Nr   rd   re   rf   rg   rh   c                 3   s    | ]}| v V  qd S )Nr   )r~   rs   analysisr   r   r     s    z/OllamaService.analyze_skills.<locals>.<genexpr>skill_analysisoverall_performanceconversation_analysisrecommendationszAnalysis missing required keysz JSON parsing error in analysis: ri   z$Error analyzing skills with Ollama: )r   r
   rl   rm   rT   r-   rn   ro   rH   _sanitize_json_textr?   rp   allr)   rq   _try_json_fallbacksrL   )r   r   r   r   r   r   r   r   rb   skill_templater6   r7   rR   r$   rS   r   r   r   analyze_skillsC  s~   







zOllamaService.analyze_skillsc              
      sn   zd}| j ||dddI dH \}}|r| d| |W S  ty6 } ztd|  W Y d}~dS d}~ww )z(Get a simple text completion from Ollamaz]You are a helpful assistant. Provide clear, natural responses without any formatting symbols.r4   i  rc   Nr   z&Error getting completion from Ollama: )rT   r-   rL   r)   )r   r6   r7   r@   r$   rS   r   r   r   get_completion  s"   zOllamaService.get_completionc           
   
   C   sD  z|}ddl }|dd|}|dd|}|dddd}|dd	}|d
d|}|dd|}|dd|}|d}t|D ];\}}d|v r~| dr~|dd}t|dkr~|d  }|dd dkr~|dd|}|d d | ||< qCd	|}|W S  t
y }	 ztd|	  |W  Y d}	~	S d}	~	ww )z6Sanitize JSON text to fix common issues before parsingr   Nz
(\w+)n's\bz\1n'tz	(\w+)'s\bz\1's"z, "'").replace('z,(\s*[}\]])z\1z}\s*{z},{z]\s*\[z],[rX   :r      z+(?<!^")(?<!: ")(?<![\[\{] ")"(?!"[\s,\]\}])z\"rW   zError sanitizing JSON: )resubreplacer   rk   rH   ro   r!   countrm   rL   r)   )
r   r   	sanitizedr   linesilinekey_val
value_partrS   r   r   r   r     s6   

z!OllamaService._sanitize_json_textc              
   C   s  ddl }z!|d||j}|r$| }| |}t|}td |W S W n ty> } ztd|  W Y d}~nd}~ww zctd |d||j}|d||j}	|d	||j}
|d
||j}t	||	|
|grd}|| d 7 }||	 d 7 }||
 d 7 }|| 7 }|d7 }| |}t|}td |W S W n ty } ztd|  W Y d}~nd}~ww td | 
 S )z3Try multiple fallback methods to extract valid JSONr   Nz\{.*\}zBSuccessfully extracted JSON using regex fallback with sanitizationz)Regex fallback with sanitization failed: z+Attempting to reconstruct JSON structure...z$"skill_analysis":\s*\{.*?\}(?=,\s*")z)"overall_performance":\s*\{.*?\}(?=,\s*")z+"conversation_analysis":\s*\{.*?\}(?=,\s*")z"recommendations":\s*\{.*?\}{,}z-Successfully reconstructed JSON from sectionszJSON reconstruction failed: zDAll JSON parsing attempts failed, creating minimal fallback response)r   searchDOTALLgroupr   r?   rp   r)   rL   r   _create_fallback_assessment)r   r   r   
json_matchextracted_jsonsanitized_jsonr   rS   skill_analysis_matchoverall_performance_matchconversation_analysis_matchrecommendations_matchreconstructedr   r   r   r     sL   



z!OllamaService._try_json_fallbacksc                 C   sH   dddgdgdgdiddddd	d
gdgdgddgdgdgddS )z<Create a basic assessment when JSON parsing completely failszOverall Performance   z9Assessment could not be completed due to technical issueszSession completed successfullyz;Unable to provide detailed feedback due to technical issues)scoreevidence	strengthsimprovement_areaszAssessment Unavailabler   )weighted_scoreperformance_leveltotal_turnsconversation_qualityzSession completedzAssessment unavailablez)Unable to analyze due to technical issues)r   critical_momentsmissed_opportunitiesz.Please retry the session for detailed feedbackz,Technical issues prevented detailed analysisz'Retry recommended for proper assessment)immediate_focuspractice_suggestionsadvanced_skillsr   r   r/   r   r   r   r     s*   	z)OllamaService._create_fallback_assessmentc              
      s$  zv| j  d}td|  tjdd4 I dH }||I dH }W d  I dH  n1 I dH s2w   Y  td|j  |jdkru| dg }d	d
 |D }td|  | j|vrrtd| j d td|  W dS W dS W dS  ty } ztd|  W Y d}~dS d}~ww )z$Check if Ollama service is availablez	/api/tagsz"DEBUG: Checking Ollama health at: g      @r=   Nz%DEBUG: Health check response status:    modelsc                 S   r{   )r   r3   r}   )r~   r   r   r   r   r   2  r   z.OllamaService.health_check.<locals>.<listcomp>zDEBUG: Available models: zWARNING: Model 'z' not found in available modelszAvailable models: FTzDEBUG: Health check failed: )	r   r)   rC   rD   r(   rF   r?   r   rL   )r   rM   rP   r@   r   model_namesrS   r   r   r   health_check$  s.   (

zOllamaService.health_check)r   )r3   r4   r5   )FN)r3   )__name__
__module____qualname__strr   intr"   r   r   r-   r0   r2   floattupler   rT   rx   r   r   boolr   r   r   r   r   r   r   r   r   r   r   r      s     8&A.<H <mO0/r   )r?   rC   typingr   r   r   r   config.promptsr   r   r   r	   r
   r   r   r   r   r   r   <module>   s
     	