o
    2hR                     @   sR   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 G dd dZdS )    N)DictListAnyOptional)SCENARIO_GENERATION_PROMPTCHARACTER_ROLEPLAY_PROMPTSKILL_ANALYSIS_PROMPTget_skill_analysis_templatec                   @   s  e Zd Zd0d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d1d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f deeeef  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-d.Zd/S )2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   [/var/www/eduai.edurigo.com/doc_train/edurigo_ai/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_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_counts2   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)r6   num_predict)r   r4   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responser1   r#   r   r$   r%   prompt_eval_durationeval_duration)r#   r$   r%   r?   r@   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   r4   r5   r6   r7   urlfull_promptpayloadr>   resultresponse_textr"   er   r   r   _make_request7   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)rS   rT   rU   skillszHYou are a training scenario expert. Always respond with valid JSON only.r2   r3   r4   r5   r6   r7   Nr   ```json   ```   zJSON parsing error: Response text: z'Error generating scenario with Ollama: )r   formatjoinrQ   r+   
startswithendswithr<   loadsrE   JSONDecodeErrorr'   rJ   )r   rR   r4   r5   rO   r"   rP   r   r   r   generate_scenarior   sH   



zOllamaService.generate_scenarioscenarioconversation_historyuser_messagec                 C   s  zod}|D ]}|d dkrdn|d d }|| d|d  d	7 }qt j|d d |d d
 |d d |d d |d dd|d d |d d |d d ||d
}d}| j||ddd\}	}
|	rh| d|
 |	rm|	W S dW S  ty } ztd|  W Y d}~dS d}~ww )z&AI character responses during roleplayr1   speakerlearnerLearnerai_charactername: message
personalitygoals
backgroundemotional_stateneutralscenario_setupcontextenvironmentconstraints)
character_namerr   rs   rt   ru   rx   ry   rz   rh   ri   zbYou are a roleplay character. Stay in character at all times. Respond naturally and realistically.g?i   rY   r   z:I'm having trouble responding right now. Please try again.z)Error in character roleplay with Ollama: N)r   r`   r&   rQ   r+   rJ   r'   )r   rg   rh   ri   history_textturnrj   r4   r5   r>   r"   rP   r   r   r   play_character   s@   







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 analysisr1   rj   rk   rl   zAI Characterro   rp   rq   z
Category: rS   z
Objective: rT   z

Context: rw   rx   z
Success Criteria: success_criteriaz
AI Character: rm   rn   z - rt   rW   rV   )rX   scenario_contextr   skill_analysis_templatez`You are an expert skill assessor. Provide detailed, accurate analysis in valid JSON format only.g333333?i  rY   Nr   rZ   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   r`   ra   rQ   r+   rb   rc   rE   _sanitize_json_textr<   rd   allr'   re   _try_json_fallbacksrJ   )r   rg   r   conversation_textr}   rj   r   rX   skill_templater4   r5   rO   r"   rP   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.r2   i  rY   r   z&Error getting completion from Ollama: N)rQ   r+   rJ   r'   )r   r4   r5   r>   r"   rP   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],[rq   :r      z+(?<!^")(?<!: ")(?<![\[\{] ")"(?!"[\s,\]\}])z\"ro   zError sanitizing JSON: )resubreplacesplit	enumeraterE   rc   r   countra   rJ   r'   )
r   r   	sanitizedr   linesilinekey_val
value_partrP   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<   rd   r'   rJ   r   _create_fallback_assessment)r   r   r   
json_matchextracted_jsonsanitized_jsonr   rP   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              
   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 )rn   r1   )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'   rA   r&   rC   r<   r   rJ   )r   rK   r>   r   model_namesrP   r   r   r   health_check  s(   

zOllamaService.health_checkN)r   )r1   r2   r3   )__name__
__module____qualname__strr   intr    r   r   r+   r.   r0   floattupler   rQ   rf   r   r~   r   r   r   r   r   boolr   r   r   r   r   r
      s    8&;2-6)N0/r
   )r<   rA   typingr   r   r   r   config.promptsr   r   r   r	   r
   r   r   r   r   <module>   s
    