o
    r\(ihQ                     @   sV   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 G dd dZdS )    N)DictListAnyOptional)SCENARIO_GENERATION_PROMPTCHARACTER_ROLEPLAY_PROMPTCHARACTER_CONCLUSION_PROMPTSKILL_ANALYSIS_PROMPTget_skill_analysis_templatec                   @   s  e Zd Zd2d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d3d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d4deeef deeeef  d ed!edee f
d"d#Zdeeef d$eeeef  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d+d,Zdeeef fd-d.Zdefd/d0Zd1S )5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   I/var/www/eduai.edurigo.com/roleplay/production/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_counts/   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_counts3   s   
z OllamaService.reset_token_counts ffffff?   promptsystem_prompttemperature
max_tokensc              
   C   s  z| j  d}td|  td| j  |}|r"d| d| }| j|d||dd}td	|d
   tj||dd}td|j  |jdkrWt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jy } ztd|  td| j   W Y d}~dS d}~w tjjy } z!td |  t|dr|jdurt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)r7   num_predict)r   r5   streamoptionszDEBUG: Payload model: r   x   )jsontimeoutzDEBUG: Response status: i  zERROR: 404 - Model 'z>' not found. Available models can be checked with: ollama list)NNresponser2   r$   r   r%   r&   prompt_eval_durationeval_duration)r$   r%   r&   r@   rA   zDEBUG: Ollama response length: zDEBUG: Ollama usage stats: z#Error connecting to Ollama server: zMake sure Ollama is running on NzError making Ollama request: zResponse content: z$Unexpected error in Ollama request: )r   r(   r   requestspoststatus_coderaise_for_statusr=   r'   stripr    
exceptionsConnectionErrorRequestExceptionhasattrr?   r   	Exception)r   r5   r6   r7   r8   urlfull_promptpayloadr?   resultresponse_textr#   er   r   r   _make_request8   s`   







zOllamaService._make_requestadmin_inputc              
   C   s,  z{t j|d |d |d d|d d}d}| j||dd	d
\}}|s'W dS | d| z+|dr:|dd }n|drE|dd }|drP|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 inputcategory	objectivedetails, skills_to_assess)rT   rU   rV   skillszHYou are a training scenario expert. Always respond with valid JSON only.r3   r4   r5   r6   r7   r8   Nr   ```json   ```   zJSON parsing error: Response text: z'Error generating scenario with Ollama: )r   formatjoinrR   r,   
startswithendswithr=   loadsrF   JSONDecodeErrorr(   rK   )r   rS   r5   r6   rP   r#   rQ   r   r   r   generate_scenarios   sH   



zOllamaService.generate_scenarioFscenarioconversation_historyuser_messageis_conclusionc                 C   s,  z{d}|D ]}|d dkrdn|d d }|| d|d  d	7 }q|r't }nt}|j|d d |d d
 |d d |d d |d dd|d d |d d |dd|d d ||d}	d}
| j|	|
ddd\}}|rt| d| |ry|W S dW S  ty } ztd|  W Y d}~dS d}~ww )z&AI character responses during roleplayr2   speakerlearnerLearnerai_charactername: message
personalitygoals
backgroundemotional_stateneutralscenario_setupcontextenvironmentrU   constraints)character_namert   ru   rv   rw   rz   r{   rU   r|   ri   rj   zYou are a roleplay character. Stay in character at all times. Respond naturally, realistically, and politely. Maintain a courteous and professional tone throughout the conversation. Guide conversations toward professional conclusions when appropriate.g?i   rZ   r   z:I'm having trouble responding right now. Please try again.z)Error in character roleplay with Ollama: N)r   r   ra   r'   rR   r,   rK   r(   )r   rh   ri   rj   rk   history_textturnrl   prompt_templater5   r6   r?   r#   rQ   r   r   r   play_character   sH   








zOllamaService.play_characterconversation_turnsc                    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\}}|skW 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 analysisr2   rl   rm   rn   zAI Characterrq   rr   rs   z
Category: rT   z
Objective: rU   z

Context: ry   rz   z
Success Criteria: success_criteriaz
AI Character: ro   rp   z - rv   rX   rW   )rY   scenario_contextr   skill_analysis_templatez`You are an expert skill assessor. Provide detailed, accurate analysis in valid JSON format only.g333333?i  rZ   Nr   r[   r\   r]   r^   r_   c                 3   s    | ]}| v V  qd S )Nr   ).0keyanalysisr   r   	<genexpr>  s    z/OllamaService.analyze_skills.<locals>.<genexpr>skill_analysisoverall_performanceconversation_analysisrecommendationszAnalysis missing required keysz JSON parsing error in analysis: r`   z$Error analyzing skills with Ollama: )r
   r	   ra   rb   rR   r,   rc   rd   rF   _sanitize_json_textr=   re   allr(   rf   _try_json_fallbacksrK   )r   rh   r   conversation_textr   rl   r   rY   skill_templater5   r6   rP   r#   rQ   r   r   r   analyze_skills   sz   








zOllamaService.analyze_skillsc              
   C   sf   zd}| j ||ddd\}}|r| d| |W S  ty2 } 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.r3   i  rZ   r   z&Error getting completion from Ollama: N)rR   r,   rK   r(   )r   r5   r6   r?   r#   rQ   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],[rs   :r      z+(?<!^")(?<!: ")(?<![\[\{] ")"(?!"[\s,\]\}])z\"rq   zError sanitizing JSON: )resubreplacesplit	enumeraterF   rd   r    countrb   rK   r(   )
r   r   	sanitizedr   linesilinekey_val
value_partrQ   r   r   r   r   3  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=   re   r(   rK   r   _create_fallback_assessment)r   r   r   
json_matchextracted_jsonsanitized_jsonr   rQ   skill_analysis_matchoverall_performance_matchconversation_analysis_matchrecommendations_matchreconstructedr   r   r   r   c  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              
   C   s   zU| j  d}td|  tj|dd}td|j  |jdkrS| dg }dd	 |D }td
|  | j|vrPtd| j d td|  W dS W dS W dS  tyo } 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: r   )r>   z%DEBUG: Health check response status:    modelsc                 S   s   g | ]}| d dqS )rp   r2   )r'   )r   r   r   r   r   
<listcomp>  s    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: N)r   r(   rB   r'   rD   r=   r   rK   )r   rL   r?   r   model_namesrQ   r   r   r   health_check  s(   

zOllamaService.health_checkN)r   )r2   r3   r4   )F)__name__
__module____qualname__strr   intr!   r   r   r,   r/   r1   floattupler   rR   rg   r   boolr   r   r   r   r   r   r   r   r   r   r   r      s    8&;8-6/N0/r   )r=   rB   typingr   r   r   r   config.promptsr   r   r   r	   r
   r   r   r   r   r   <module>   s
    