
    5iJ                       S r SSKJr  SSKrSSKrSSKJr  SSKJrJr   SSK	J
r
  \
" 5         SSKJrJrJrJr  SSKJr  SS	KJrJrJr  SS
KJr  SSKJr  SSKJr  SSKJr  SSKJ r J!r!J"r"J#r#  SSK$J%r%  Sr&Sr'Sr(Sr)Sr*Sr+Sr,Sr-\." 1 Sk5      r/\" SS/S9r0 " S S\5      r1          S3S jr2S4S jr3S r4S5S  jr5S6S! jr6S7S" jr7S8S# jr8S9S$ jr9S:S% jr:\0Rw                  S&S'S(S)9\" S*S+S,S-9\" \5      \" \5      4     S;S. jj5       r<\0R{                  S/S0S1S)9\" \5      \" \5      4     S<S2 jj5       r>g! \ a     GNf = f)=z
Evaluator chat: senior evaluator persona (Chile, NEE/PIE) using `knowledge_documents` as context.
Documented for OpenAPI / Swagger (`/docs`).
    )annotationsN)datetime)ListOptional)load_dotenv)	APIRouterDependsQuerystatus)JSONResponse)	BaseModelFieldmodel_validator)IntegrityError)Session)get_current_active_user)get_db)AIConversationModelEvaluatorChatAuditModel%EvaluatorChatPsychopedSectionUseModelKnowledgeDocumentModel)	UserLoginip  i   zgpt-4o-minii i  z
gpt-5-mini   >   
conclusionmotorAnalysismotorSynthesisotherSuggestionspersonalAnalysiscognitiveAnalysispersonalSynthesiscognitiveSynthesissuggestionsToFamilysuggestionsToSchoolsuggestionsToStudentsuggestionsToClassroomTeamz/chatzEvaluator chat)prefixtagsc                      \ rS rSr% Sr\" SSSSS0S9rS	\S
'   \" SS\S\ S3SS0S9r	S	\S'   \" SSSS9r
S\S'   \" SSSS9rS\S'   \" SSSS9rS\S'   \" SS9S 5       rSrg) EvaluatorChatRequestJ   z<Request body for evaluator chat (appears in OpenAPI schema)..   u   Instrucción o tarea (p. ej. apartado del informe a redactar, pregunta técnica). Se analiza junto con `user_context` y la base de conocimiento.exampleua   Redacta el apartado «IV … — a) Habilidades cognitivas…» para un informe psicopedagógico.)
min_lengthdescriptionjson_schema_extrastrquestionu   Texto libre que escribe el usuario: antecedentes, notas de evaluación, observaciones, etc. El modelo debe integrar todo el contexto relevante con la instrucción y los knowledge_documents. Máximo z caracteres.uH   Estudiante con TEL; observaciones de aula: participa con apoyo visual…)r-   
max_lengthr.   r/   user_contextNu   Opcional. Si se envía junto con `field_key`, bloquea repetir el mismo apartado para este estudiante y usuario (persistente en base de datos).)defaultger.   zOptional[int]
student_idP   zEClave del apartado (p. ej. cognitiveAnalysis). Requiere `student_id`.)r4   r2   r.   Optional[str]	field_keyu   ID del tipo de documento (p. ej. 27 = informe psicopedagógico). Si se omite y se envía informe doc. 27 (`student_id` + `field_key`), se asume 27 para auditoría.document_type_idafter)modec                    U R                   S LnU R                  b#  U R                  =(       d    SR                  5       OSn[        U5      nX:w  a  [	        S5      eU(       a  X l        U $ )N z?student_id y field_key deben enviarse juntos, u omitirse ambos.)r6   r9   stripbool
ValueError)selfhas_sidfkhas_fks       SC:\Users\jesus\Desktop\proyecto_pie360\backend\app\backend\routes\evaluator_chat.py_student_and_field_together0EvaluatorChatRequest._student_and_field_togethery   sZ    //-/3~~/Idnn"))+rb^__N    )r9   )__name__
__module____qualname____firstlineno____doc__r   r1   __annotations__MAX_USER_CONTEXT_CHARSr3   r6   r9   r:   r   rG   __static_attributes__ rI   rF   r)   r)   J   s    FM z

Hc 
 )-.l< %&pq
L# 
 !&M	!J   %[ I} 
 ',q	'm  '" #rI   r)   c                  UR                   nUc$  UR                  b  UR                  (       a  [        nUR                  =(       d    SR	                  5       =(       d    Sn[        U5      S::  a  UOUSS S-   nU R                  [        UUUR                  U(       a  USS OSU[        R                  " 5       S95        g)u8   Registra un uso exitoso del chat evaluador (auditoría).Nr>       z

[... pregunta truncada ...]r7   )user_idr:   r6   r9   r1   
added_date)
r:   r6   r9   PSYCHOPED_DOCUMENT_TYPE_IDr?   lenaddr   r   now)dbuser_id_intbodyr1   doc_tidrD   qs          rF   _persist_evaluator_chat_auditra      s     ##G4??64>>,
..
B	%	%	'	/4BMU*&51ADe1eAFF$!#b"g||~	
	rI   c                Z   U R                  [        5      R                  [        R                  S:H  5      R	                  [        R
                  R                  5       5      R                  5       n/ nU GH  nUR                  =(       d    SR                  5       =(       d    SUR
                   3nUR                  =(       d    SR                  5       nU(       d  Mi  / nUR                  (       a  UR                  SUR                   35        UR                  (       a  UR                  SUR                   35        SU 3nU(       a  USSR                  U5       S	3-  nUR                  U S
U 35        GM     SR                  U5      n[        U5      [         :  a  US [          S-   nU$ )NTr>   z
Documento ztype: z
category: z## z (z, )
z

---

z

[... context truncated ...])queryr   filter	is_activeorder_byidascalltitler?   contentdocument_typeappendcategoryjoinrY   MAX_KNOWLEDGE_CONTEXT_CHARS)	r\   rowspartsrrl   r^   metaheadjoineds	            rF   _load_knowledge_contextry      sY   
'(	&00D8	9	(++//1	2		 	 EB%%'>Zv+>		R&&(??KK& 123::KK*QZZL12UG}b4)++DvRv&'  &F
6{005569ZZMrI   c                 \     SSK n U S4$ ! [         a  nS[        U5      4s SnA$ SnAff = f)zTImport at call time so the same interpreter as the request handles missing installs.r   N)openaiImportErrorr0   )r{   es     rF   _try_import_openair~      s2    t| SV|s   
 
+&++c                    U =(       d    SR                  5       nU=(       d    SR                  5       nSU SU 3n[        U5      [        :  a  US[        S-
   S-   nU$ )zXArma el mensaje de usuario para el modelo: tarea + contexto escrito, separados y claros.r>   u   ### Instrucción / tarea
z'

### Contexto aportado por el usuario
N(   u(   

[... mensaje truncado por límite ...])r?   rY   MAX_USER_MESSAGE_CHARS)r1   r3   r`   ctxblocks        rF   _build_model_user_inputr      sp    	R A2
$
$
&C$# 1%	 
 5z**3.347ccLrI   c                X   U R                  5       nUR                  S5      (       d  UR                  S5      (       am  UR                  S5      (       a  US S R                  5       nOUS S R                  5       nUR                  S5      (       a  MU  UR                  S5      (       a  Mm  U$ )Nu   …z...)rstripendswith)textss     rF   _strip_trailing_ellipsisr      s    A
**U

qzz%00::e#2A#2A	 **U

qzz%00
 HrI   c                   [        U =(       d    SR                  5       5      nU(       a  [        U5      U::  a  U$ USU nS HF  nUR                  U5      nUS:w  d  M  USUS-    R	                  5       nU(       d  M;  [        U5      s  $    UR                  S5      nUS:  a  USU R	                  5       nO US[        SUS-
  5       R	                  5       nU(       a  US   S;  a  US	-  n[        U5      $ )
u\   Recorta a `max_len` cerrando en oración o palabra; nunca añade puntos suspensivos finales.r>   N)z? z! z.
z. r   r+    r   z.!?.)r   r?   rY   rfindr   max)	r   max_lentchunkneedleiclosedcutouts	            rF   _clamp_response_to_closed_maxr      s     $*"!3!3!56AA'!hwKE+KK77QU^**,Fv/77 , ++c
C
QwDSk  ")c!Wq[)*113
s2we#s
#C((rI   c                b   U =(       d    SR                  5       nUR                  S5      (       dC  UR                  S5      (       d-  UR                  S5      (       d  UR                  S5      (       d  g[        R                  " SS5      R	                  5       R                  5       nS	nX#;   a  U$ S$ )
ud   Razonamiento interno consume el mismo cupo que el texto; bajar esfuerzo deja más para la respuesta.r>   zgpt-5o1o3o4NEVALUATOR_CHAT_REASONING_EFFORTlow)noneminimalr   mediumhighxhigh)lower
startswithosgetenvr?   )
model_namemeffortalloweds       rF   _reasoning_effort_for_modelr      s    		r  "ALL!!Q\\$%7%71<<;M;MQRQ]Q]^bQcQcYY8%@FFHNNPFCG&61E1rI   c           	     B   [        U SS5      nU(       a7  [        U5      R                  5       (       a  [        U5      R                  5       $ / n[        U SS5      =(       d    /  H  n[        USS5      S:X  d  M  [        USS5      =(       d    /  Hj  n[        USS5      nUS:X  a'  UR                  [        USS5      =(       d    S5        M=  US	:X  d  ME  UR                  [        US	S5      =(       d    S5        Ml     M     SR	                  U5      R                  5       $ )
zSoutput_text suele bastar; si el API/SDK usa bloques distintos, recorremos `output`.output_textNoutputtypemessagerm   r   r>   refusal)getattrr0   r?   ro   rq   )responseprimaryrt   itemcctypes         rF   _extract_openai_response_textr      s    ht4G3w<%%''7|!!##E(D17R74&)3T9d39r9640M)LLFB!7!=2>i'LLIr!:!@bA : 8 775>!!rI   c                    U R                  5       =(       d    SnS[         S[         SU S[         S[         S[         S[         S	3$ )
Nu   (No hay documentos activos en knowledge_documents; responde con criterio profesional general sobre NEE/PIE en Chile, sin inventar normas específicas.)u  You are a senior educational evaluator in Chile with deep, practical experience in the PIE (Plan Individual de Apoyo) process, school inclusion, special educational needs (NEE), curricular and organizational adjustments, and written evaluation for families, teaching teams, and coordination. You write as a seasoned peer: rigorous, warm toward the team, never condescending, and attentive to the ethical weight of describing a student's learning profile.

Persona and voice (output in Spanish):
- Understand and unpack the question before you write: INSTRUCTION/TASK always defines the main question or the main writing task—that is the spine of your entire answer. Read it slowly (objetivo, alcance, apartado o tipo de síntesis). If it contains several sub-demands that belong to that same main task, address them clearly and in order. Then ground your answer in USER-WRITTEN CONTEXT as evidence—not as a list of separate questions you must all answer.
- Use polished professional Spanish suited to Chilean schools: clear syntax, precise vocabulary (e.g. apoyos, mediación, adecuaciones, participación, autonomía relativa, trayectoria, evidencias en aula). Prefer third person or impersonal forms ("Se observa…", "El perfil muestra…", "En el ámbito comunicativo…") and neutral references to "el estudiante" / "la estudiante" when needed.
- Sound like a written evaluation or technical synthesis destined to inform equipo de aula, familia or UTP: balanced, evidence-led, nuanced. Avoid generic praise ("excelente desempeño en todo") unless the context truly supports it across domains. Avoid alarmist or stigmatizing wording; when describing difficulties, tie them to support needs and observable patterns from the context.
- Show senior judgment: weigh strengths and gaps together; connect domains when the context allows (e.g. how communication patterns relate to participation or emotional regulation); distinguish what is clearly evidenced from what is only suggested. Do not invent classroom facts, scores, or diagnoses not present in the context.
- Length of the redacción: you may produce one continuous, professional written answer (redacción / síntesis) of up to u    characters (including spaces). When the task and the evidence support it, use that full span: develop paragraphs with the depth expected from a senior PIE evaluator in Chile, not a brief note—always staying under or at u	   characters, never above.

Natural, human-like prose (still rigorous and professional):
- Write as a colleague would on a good day: warm, readable, never stiff or "robotic". Vary sentence length and openings; use smooth transitions (e.g. asimismo, en cambio, en este punto, en conjunto) instead of repeating the same scaffold every paragraph.
- Prefer connected paragraphs over mechanical enumeration. Avoid sounding like a template ("En primer lugar… En segundo lugar…") or a generic AI checklist unless INSTRUCTION/TASK explicitly asks for numbered structure.
- Use concrete, lived-school wording where it fits; cut empty fillers ("es importante destacar", "cabe señalar", "en el marco de") unless they genuinely help the reader—do not stack them sentence after sentence.
- A touch of natural empathy for the equipo's effort is fine (e.g. that classroom evidence is partial or uneven)—without syrupy tone, clichés ("cada niño es único"), or breaking the Hard rules below.
- If USER-WRITTEN CONTEXT resembles a transcript with many question/answer pairs, do not reproduce a turn-by-turn "P: … R: …" layout in your output unless INSTRUCTION/TASK explicitly requires it; deliver one coherent evaluative redacción that answers the main instruction.
- Formatting for Word/forms: write with real line breaks. Separate paragraphs with a blank line (two consecutive newlines) between major blocks (e.g. after a list of sub-themes or before a new subsection); use single newlines inside a paragraph only when it genuinely helps readability. Avoid one uninterrupted wall of text with no breaks—the platform keeps these newlines when exporting to Word.

PIE / inclusion mindset (conceptual, not legal invention):
- Frame observations in terms of supports, accessibility, reasonable adjustments, and progression over time when the user context allows—without citing specific laws, decrees, or official documents unless they appear verbatim in the KNOWLEDGE BASE.
- Respect the logic of collaborative work: valoración del equipo, continuidad pedagógica, and clarity for who will read the text (docente de aula, familia, psicopedagogía), still without naming real people unless INSTRUCTION/TASK requires it.

Ground your answer in the KNOWLEDGE BASE below when it is relevant. If the knowledge base does not cover the question, answer with sound professional judgment and explicitly avoid fabricating laws, decrees, or institutional details.

KNOWLEDGE BASE (from knowledge_documents):
u  

The user message has two labeled parts: INSTRUCTION/TASK and USER-WRITTEN CONTEXT. First infer the intent and boundaries of the main question in INSTRUCTION/TASK; then synthesize with the knowledge base. USER-WRITTEN CONTEXT may be long and include several embedded questions and answers (e.g. notes, interview fragments, chat-like transcripts). That material is background and evidence: use what supports the main task; do not treat every internal Q&A as a prompt you must answer one by one, and do not let the redacción drift into answering a side question that is not the principal instruction. The final text must read as a coherent answer to the main INSTRUCTION/TASK. Detail your reasoning implicitly in the prose (what you conclude and why, given the evidence)—without meta-commentary like "la pregunta pide…". Do not ignore substantive details the user provides when they serve the main task. If the context conflicts with the knowledge base, prefer cautious professional wording and do not invent norms.

When USER-WRITTEN CONTEXT is tabular, pasted from spreadsheets, or lists per-item statuses (e.g. LOGRADO, EN PROCESO, REQUIERE APOYO), treat every row and column as data to respect—not filler. Use statuses and indicator content for accuracy, but do not restate identifying metadata in the answer (see Hard rules). If the user pasted more than one evaluation or row, contrast how they differ using patterns and domains—without naming students, evaluators, courses, or dates unless INSTRUCTION/TASK explicitly requires them.

Wide rubric tables (many domains in one paste): USER-WRITTEN CONTEXT often includes a single header row plus one data row, with columns for metadata (e.g. timestamp, specialist, student name, course) and many indicator columns grouped by domain in the headers (e.g. "Habilidades cognitivas y comunicativas: […]", "Habilidades Personales, Socioemocionales…", "Habilidades motoras, de autonomía y sensoriales…"). When INSTRUCTION/TASK names a specific section or domain (e.g. "IV. ANÁLISIS CUALITATIVO… a) Habilidades cognitivas y comunicativas"), your redacción must **only** address that domain: use **only** the indicator columns whose headers or bracketed text clearly belong to cognitive-communicative skills for that synthesis. Do **not** summarize personal-socioemotional or motor/sensory columns in that same answer unless INSTRUCTION/TASK explicitly asks for those domains too (e.g. full IV with a, b, c). Within the instructed domain, integrate **all** relevant indicator-status pairs from the paste for that domain—so the qualitative analysis reflects the full spread of LOGRADO / EN PROCESO / REQUIERE APOYO for those columns, not a cherry-picked subset.

Status labels are NOT interchangeable. Map each item only to the status shown in that column/cell:
- LOGRADO: competence observed as consolidated in context; you may use clear positive wording aligned with achievement.
- EN PROCESO: still developing; describe as emerging, partial, or inconsistent, with support or practice still needed—never as fully achieved.
- REQUIERE APOYO: not consolidated; stress need for explicit support, mediation, or adjustments; do not rewrite as success or "logra" / "demuestra de forma adecuada" for that same item.
Do not narrate the checklist as if every line were LOGRADO. For each indicator that appears with a status, your wording must match LOGRADO vs EN PROCESO vs REQUIERE APOYO. Cover strengths and gaps: do not only list problems and skip LOGRADO items, and do not only praise while hiding REQUIERE APOYO or EN PROCESO. Keep Spanish agreement using impersonal forms ("Presenta…", "Se observa…"), neutral "el estudiante/la estudiante", or "el perfil", not proper names from the context.

Comprehensive coverage within scope: Read the entire USER-WRITTEN CONTEXT to find evidence, but **write only within the scope of INSTRUCTION/TASK**. If the main task is a single domain (e.g. IV a) cognitive-communicative), deepen that domain across all its indicators in the table—do not dilute the answer by also narrating socioemotional or motor columns from the same paste. If INSTRUCTION/TASK explicitly requires several domains or the whole IV, then cover each requested domain with the same rigor. Your redacción may extend up to ul   characters: use most of that space when the data support it—rich, specific content with a readable rhythm (not a wall of keywords); do not stop short with a thin paragraph if you can still add faithful detail under the cap for the **asked** scope.

Every case is different: ground the answer in the statuses and indicators that fall under the instructed scope so profiles do not sound interchangeable. If two or more evaluations appear in one answer, separate them without proper names (e.g. contrasting patterns or domains), unless INSTRUCTION/TASK explicitly asks to identify people.

Hard rules:
- Maximum length: z characters (including spaces), never more. Aim to land near that ceiling when the data support it, using most of the available space up to u    when appropriate.
- Always finish with a complete, closed sentence (final period, etc.). If a clean ending would exceed the cap by a few characters, prefer the shorter clean ending—never exceed uy  . Do not end with "...", "…", suspension points, or trailing commas.
- Do not cite internal system labels; write for teachers and coordinators.
- Never equate EN PROCESO or REQUIERE APOYO with LOGRADO in the narrative for the same indicator.
- With the available length cap, synthesize across all indicators that belong to the scope implied by INSTRUCTION/TASK (e.g. all cognitive-communicative columns when that is the task). Do not omit whole blocks of indicators **within that scope**. When INSTRUCTION/TASK narrows to one domain, you may ignore other domains in the paste for the narrative—only do not ignore indicators inside the requested domain.
- Do not name the student, the evaluator/specialist, the course or grade (e.g. 2° F, 2° A), dates, or the report title (e.g. "Informe cualitativo breve", section letter as a heading). Omit "evaluado por…", "evaluadora:", parenthetical names, and similar. Start directly with qualitative substance—e.g. "Presenta un perfil…", "Fortalezas observadas (LOGRADO):", "En habilidades cognitivas y comunicativas…"—unless INSTRUCTION/TASK explicitly demands those identifiers.
- Do not give recommendations, advice, next steps, or phrases like "se recomienda", "se sugiere", "conviene", "es aconsejable" unless the INSTRUCTION/TASK asks for that kind of content—explicitly (e.g. recomendaciones, orientaciones, sugerencias) or clearly as the goal of the question (e.g. qué hacer, próximos pasos, cómo apoyar). If the task is only synthesis or qualitative description, stay descriptive-evaluative. Describing REQUIERE APOYO in rubric terms is allowed without adding unsolicited prescriptions.
)r?   MAX_RESPONSE_CHARS)knowledge_blockkbs     rF   _build_system_instructionr     s    				   
~  %~B{ |N  {O  Om  n@  mA A"  \ ]o  \p p
 &&  's  tF  sG Gs tF  sG GY2 2rI   z /evaluator/psychoped-used-fieldszCApartados del chat evaluador ya usados (informe 27, por estudiante)u   Lista las claves `field_key` ya generadas con éxito por el usuario actual para el `student_id` indicado. Sirve para deshabilitar opciones en el asistente tras recargar.)summaryr.   .r+   zID del estudiante)r5   r.   c                   [        USS 5      =(       d    [        USS 5      =(       d    SnUb  [        U5      OSnUR                  [        R                  5      R                  [        R                  U:H  [        R                  U :H  5      R                  5       nU Vs/ s H  ofS   (       d  M  US   PM     nn[        [        R                  SSSU0S.S	9$ s  snf )
Nri   rV   r+   r      OK
field_keysr   r   datastatus_coderm   )r   intre   r   r9   rf   rV   r6   rk   r   r   HTTP_200_OK)r6   session_userr\   uid_rawr]   rs   ru   keyss           rF   $list_psychoped_evaluator_used_fieldsr   E  s     lD$/^7<TX3Y^]^G")"5#g,1K
6@@A	199[H1<<
J

 
 	 '$QA$DAaD$D'&&4,9MN  (s   C/	Cz
/evaluatorz-Chat evaluador (knowledge_documents + OpenAI)u  Recibe `question` (instrucción/tarea) y `user_context` (texto libre del usuario; máximo 6000 caracteres). Se prioriza síntesis detallada y personalizada por estudiante, usando los datos aportados. Ambos se envían al modelo junto con el contenido activo de `knowledge_documents`. Hasta 6000 caracteres en la respuesta; GPT-5 usa `max_output_tokens` alto y `EVALUATOR_CHAT_REASONING_EFFORT` (p. ej. low). Ver `EVALUATOR_CHAT_MAX_OUTPUT_TOKENS`. Requiere `OPENAI_API_KEY`. Modelo: `EVALUATOR_CHAT_MODEL`, o `NEE_EVALUATOR_MODEL`, o por defecto GPT-5 mini (`gpt-5-mini`). La interacción se guarda en `ai_conversations`. Si se envían `student_id` y `field_key` (informe psicopedagógico doc. 27), se persiste el uso y no se permite repetir el mismo apartado para ese estudiante (HTTP 409).c                
  ^)^*^+^,^-^.^/ [        5       u  p4Uc'  [        [        R                  SSU(       a  SU0OS S.S9$ [        R
                  " S5      nU(       d  [        [        R                  SSS S.S9$ U R                  =(       d    SR                  5       nU R                  =(       d    SR                  5       nU(       d  [        [        R                  S	S
S S.S9$ U(       d  [        [        R                  S	SS S.S9$ [        USS 5      =(       d    [        USS 5      =(       d    SnUb  [        U5      OSm/U R                  n	U R                  (       a#  U R                  =(       d    SR                  5       OS n
U	b  U
(       a  U
[        ;  a  [        [        R                  S	SSU
0S.S9$ UR                  [         5      R#                  [         R$                  T/:H  [         R                  U	:H  [         R                  U
:H  5      R'                  5       nUb  [        [        R(                  SSU
U	S.S.S9$ [+        Xg5      n[-        U5      n[/        U5      nUR1                  US9n[        R
                  " S[        R
                  " S[2        5      5      m)[4        n[        R
                  " S5      nU(       aB  UR                  5       R7                  5       (       a  [9        S[;        [        U5      S5      5      n T)UUUS.n[=        T)5      nUb  SU0US'   UR>                  R@                  " SG0 UD6m+[        T+S(S 5      nUbK  [        US)S 5      =(       d    [E        U5      n[        [        RJ                  S%S*U 3S+[        T+SS 5      0S.S9$ [        T+S,S 5      nUS-:X  a)  [        [        RJ                  S%S.S+[        T+SS 5      0S.S9$ [M        T+5      m*T*(       dI  S n T)UU[9        S/U5      SS00S1.nUR>                  R@                  " SG0 UD6n[M        U5      nU(       a  Um+Um*T*(       do  [        R
                  " S2[N        5      R                  5       nU(       a?   UR>                  RA                  UUU[9        S3U5      S9n[M        U5      n U (       a  Um+U m*Um)T*(       dL  [        T+S4S 5      n!U!b  [        U!S5S 5      OS n"[        [        RJ                  S%S6S7UU"[        T+SS 5      US8.S.S9$ [Q        T*[R        5      m*[E        [T        RV                  " 5       5      m,S m.[Y        T+S95      (       a(  T+RZ                  (       a  [        T+RZ                  S:S 5      m.S;U S<U 3m-[]        T-5      S=:  a  T-S S> S?-   m-SHU)U*U+U,U-U.U/4S@ jjn#S n$ U#" 5       n%UR_                  U%5        U	bL  U
(       aE  []        U5      SA::  a  UOUS SA n&UR_                  [!        T/U	U
U&[`        Rb                  " 5       SB95        [e        UT/XSC9  URg                  5         URi                  U%5        U%Rj                  n$SDSET*[]        T*5      [R        [q        UR                  5       5      U$T)[]        U5      []        U5      SF.S.n([        [        Rr                  U(S9$ ! [B         a  n[E        U5      nURG                  5       nSU;   d  SU;   a'  SU;   a!  [        [        RH                  S S!S S.S9s S nA$ S nSU;   d  S"U;   d  S#U;   a  S$n[        [        RJ                  S%S&U 3U(       a  S'U0OS S.S9s S nA$ S nAff = f! [B         a    S n GNf = f! [B         a     GNf = f! [l         a    URo                  5          U#" 5       n'UR_                  U'5        [e        UT/XSC9  URg                  5         URi                  U'5        U'Rj                  n$ GN! [B         a    URo                  5         S n$  GNf = f[B         a    URo                  5         S n$ GNf = f)INi  zPython package 'openai' is not available in this environment. Install with the same interpreter as the app: pip install 'openai>=2.0.0' or pip install -r requirements.txt (on Linux/systemd: activate the project venv first, then restart gunicorn/uvicorn).import_errorr   r   OPENAI_API_KEYzOPENAI_API_KEY not configuredr>   i  zquestion is requiredzuser_context is requiredri   rV   r+   uG   field_key no es un apartado permitido para el informe psicopedagógico.r9   i  ur   Ya utilizaste el asistente para este apartado con este estudiante. No se puede volver a generar la misma sección.)r9   r6   )api_keyEVALUATOR_CHAT_MODELNEE_EVALUATOR_MODEL EVALUATOR_CHAT_MAX_OUTPUT_TOKENS   i>  )modelinputinstructionsmax_output_tokensr   	reasoninginsufficient_quota429zexceeded your current quotai  u   El chat no está disponible
rate_limitz	too largeu   Límite de tokens (TPM) o contexto demasiado grande. Acorta `user_context`, desactiva knowledge_documents que no necesites, o aumenta límites en https://platform.openai.com/account/rate-limitsi  zOpenAI error: hinterrorr   zOpenAI response error: response_idr   failedzOpenAI response status failedi    r   )r   r   r   r   r   EVALUATOR_CHAT_FALLBACK_MODELi   incomplete_detailsreasonu%   El modelo no devolvió texto visible.u   Se intentó reintento automático (más tokens, menor reasoning y fallback de modelo) pero siguió vacío. Aumenta EVALUATOR_CHAT_MAX_OUTPUT_TOKENS o usa EVALUATOR_CHAT_MODEL=gpt-4o-mini para este flujo.)r   openai_statusincomplete_reasonr   max_output_tokens_usedusagetotal_tokensz[question]
z

[user_context]
rT   rU   z

[... truncado ...]c                    > [        TT[        TSS 5      TSTT T[        R                  " 5       [        R                  " 5       S9
$ )Nri   evaluator_chat)
rV   
session_idprevious_response_id
input_textinstructionresponse_textr   tokens_usedrW   updated_date)r   r   r   r[   )r   rawr   session_uuidstored_inputr   r]   s   rF   _build_ai_row-evaluator_chat_message.<locals>._build_ai_row\  sC    "#!(4!>#(#||~!
 	
rI   i   )rV   r6   r9   question_label
created_at)r]   r^   r1   r   r   )r   response_lengthr2   knowledge_documents_usedconversation_idr   question_lengthuser_context_lengthrR   )returnr   ):r~   r   r   HTTP_500_INTERNAL_SERVER_ERRORr   r   r1   r?   r3   HTTP_422_UNPROCESSABLE_ENTITYr   r   r6   r9   PSYCHOPED_EVALUATOR_FIELD_KEYSre   r   rf   rV   firstHTTP_409_CONFLICTr   ry   r   OpenAIEVALUATOR_CHAT_DEFAULT_MODEL(EVALUATOR_CHAT_MAX_OUTPUT_TOKENS_DEFAULTisdigitr   minr   	responsescreate	Exceptionr0   r   HTTP_503_SERVICE_UNAVAILABLEHTTP_502_BAD_GATEWAYr   r   r   r   uuiduuid4hasattrr   rY   rZ   r   r[   ra   commitrefreshri   r   rollbackr@   r   )0r^   r   r\   
openai_modopenai_import_errorr   r1   r3   r   psychoped_student_idpsychoped_field_keydupmodel_inputr   r   clientmax_outraw_envcreate_kwargsreffr}   err	err_lowerr   err_objemstretry_responseretry_kwargs	retry_rawfallback_modelfallback_responsefallback_rawincr   r   conv_idrowqlrow_onlypayloadr   r   r   r   r   r   r]   s0                                            @@@@@@@rF   evaluator_chat_messager3  c  s   ( '9&:#J==j
 BU)<=Z^	
 	
 ii()G==:
 	
 #**,H%%+224L<<"/EtT
 	
 <<"/ISWX
 	

 lD$/^7<TX3Y^]^G")"5#g,1K??<@NN4>>/R668PT',?&DD"@@!h(*=>  HH:;V5==L5@@DXX5??CVV
 UW 	 ?"44!J &9&:
  *(AK-b1O,_=Lw/F
		')EFJ
 7Gii:;G7==?**,,c3s7|V45&
 (!(	
 +:6*2D)9M+&##**;];< h.GWi.>#g,334RD9&$(EF
 	
 
8T	*B	X~33:&$(EF
 	
 (
1C	"#$ ,%(w%7&	2L $--44D|DN5nEI) #BDabhhj$*$4$4$;$;(%!-&)$&8	 %< %!  ==NO0H&C!/J h 4d;14h-d33B{ &()/#*8T4#@.5	
 	
$ (-?
@Ctzz|$LKx!!hnnhnnndC!(+?~NL
<5 #FU+.FF
 
 G o
s+0C ]c1x~BFF5'31#%'||~ 	&bk`
		


3&&$ "3x,(,_-B-B-D(E&"8}#&|#4	
G F$6$6HHu  
!fIIK	9,SL:iG"??!<   C<<94y8P_  33+C51*.D
 	
)
J  	"!N	"&  H  
		$HFF8)"+DdIIKJJx kkG 	KKMG	  
s    8Z $A\- ">]  B] 
\*A\%\* ?\%\*%\*-\=<\= 
]]`,A^??_ `_  ``)
r\   r   r]   r   r^   r)   r1   r0   r  None)r\   r   r  r0   )r1   r0   r3   r0   r  r0   )r   r0   r  r0   )r   r0   r   r   r  r0   )r   r0   r  r8   )r   objectr  r0   )r   r0   r  r0   )r6   r   r   r   r\   r   )r^   r)   r   r   r\   r   )?rN   
__future__r   r   r  r   typingr   r   dotenvr   r|   fastapir   r	   r
   r   fastapi.responsesr   pydanticr   r   r   sqlalchemy.excr   sqlalchemy.ormr   app.backend.auth.auth_userr   app.backend.db.databaser   app.backend.db.modelsr   r   r   r   app.backend.schemasr   r   r	  r   rr   r   rP   r  rX   	frozensetr  r   r)   ra   ry   r~   r   r   r   r   r   r   getr   postr3  rR   rI   rF   <module>rE     s  
 # 	   !	"M 6 5 * 6 6 ) " > *  * +/ ( - %     ,   "+" " 
	89 8v  	
  
28),2""4n &Q	a	   CA3FG%&=>&/ 	, ;	T	  " &&=>&/iI
iIiI 	iIiIC  		s   D9 9EE