o
    €Bpj¹4  ã                   @  sž  d Z ddlmZ ddlZddlmZmZmZmZ dZ	dddd	d
dœZ
dZdZdZdZdZdSdd„ZdTdd„ZdUdVd!d"„ZdWd$d%„ZdXd&d'„ZdYd*d+„ZdZd,d-„Ze d.ej¡e d/ej¡e d0ej¡e d1ej¡e d2ej¡e d3ej¡e d4ej¡e d5ej¡e d6ej¡e d7ej¡e d8ej¡e d9ej¡e d:ej¡fZd[d;d<„Zd\d>d?„ZdXd@dA„Zd]dDdE„Zd^dGdH„Zd]dIdJ„Zd_dLdM„Z d`dNdO„Z!dadQdR„Z"dS )bu?   Structured call summary â€” bullet sections for Call Detail UI.é    )ÚannotationsN)ÚAnyÚDictÚListÚOptional)Úpoints_to_discussÚkey_takeawaysÚfeature_tasksÚfollow_up_tasksÚtaskszPoint of DiscusszKey TakeawayzFeature TaskzFollow-up TaskÚTasksuV  
STRUCTURED SUMMARY (structured_summary object â€” REQUIRED in every response; use transcript facts only):
- overview: 1-2 sentence call recap
- points_to_discuss: open topics, unanswered questions, or items to revisit (1-5 concise bullets when applicable)
- key_takeaways: main conclusions or insights from the call (REQUIRED: at least 1 bullet for substantive calls)
- feature_tasks: product, demo, configuration, or internal delivery tasks (1-5 concise bullets when applicable)
- follow_up_tasks: concrete next steps for agent or customer (REQUIRED: at least 1 bullet for substantive calls)
- tasks: other actionable tasks, commitments, or to-dos mentioned on the call (1-5 concise bullets when applicable)
Use empty arrays [] only when a section truly has nothing to list (e.g. wrong-number or silent call).
Do not invent facts not supported by the transcript.
Each point must appear in only one section â€” never repeat the same fact, task, or takeaway across multiple sections.
Within each section, every bullet must be distinct â€” do not include two bullets that say the same thing in different words.
zþ
  "structured_summary": {{
    "overview": "<optional 1-2 sentence recap>",
    "points_to_discuss": ["<bullet>"],
    "key_takeaways": ["<bullet>"],
    "feature_tasks": ["<bullet>"],
    "follow_up_tasks": ["<bullet>"],
    "tasks": ["<bullet>"]
  }},)zdid not sharezdid not mentionzdid not providezdid not discussznot share detailsznot mentionedznot coveredÚ
unansweredzremains unclearzwas not discussedzno details about)Ú	scheduledz	follow upz	follow-upz	call backÚcallbackz	next stepz	will sendz
will sharezwill providezdemo forzdemo onzdemo tomorrowz	demo next)Ú
introducedÚofferedzdiscussed serviceszservices likezfeatures likeÚconfigurationÚintegrationzauto dialerzclick to callÚivrzdemo ofÚvaluer   Úreturnú	List[str]c                 C  sb   t | tƒst | tƒr|  ¡ r|  ¡ gS g S g }| D ]}t|pdƒ ¡ }|r*| |¡ q|d d… S )NÚ é   )Ú
isinstanceÚlistÚstrÚstripÚappend)r   ÚitemsÚitemÚtext© r"   ú=/home/aiteam/pcaa-dev/dashboard-backend/structured_summary.pyÚ_clean_bullets]   s   


€r$   r!   r   Úsetc                 C  s   t t dd|  ¡ ¡ ¡ ƒS )Nz
[^a-z0-9 ]r   )r%   ÚreÚsubÚlowerÚsplit)r!   r"   r"   r#   Ú	_word_setj   s   r*   ç333333ã?ÚaÚbÚ	thresholdÚfloatÚboolc                 C  sD   t | ƒt |ƒ}}|r|sdS t||@ ƒtt|ƒt|ƒƒ }||kS )NF)r*   ÚlenÚmin)r,   r-   r.   ÚwaÚwbÚoverlapr"   r"   r#   Ú_is_similarn   s
   r6   r   c                   sB   g }| D ]‰ ˆ s	qt ‡ fdd„|D ƒƒrq| ˆ ¡ q|d d… S )Nc                 3  s    | ]}t ˆ |ƒV  qd S ©N)r6   )Ú.0Úexisting©r    r"   r#   Ú	<genexpr>{   ó   € z"_dedupe_bullets.<locals>.<genexpr>r   )Úanyr   )r   Úoutr"   r:   r#   Ú_dedupe_bulletsv   s   r?   c                 C  s   t  d|  ¡ ¡}dd„ |D ƒS )Nz(?<=[.!?])\s+c                 S  s   g | ]
}|  ¡ r|  ¡ ‘qS r"   ©r   )r8   Úpartr"   r"   r#   Ú
<listcomp>ƒ   s    z$_split_sentences.<locals>.<listcomp>)r&   r)   r   )r!   Úpartsr"   r"   r#   Ú_split_sentences   s   rD   Ú
structuredúDict[str, Any]c                   s   t ‡ fdd„tD ƒƒS )Nc                 3  s    | ]}ˆ   |¡V  qd S r7   )Úget©r8   Úkey©rE   r"   r#   r;   ‡   r<   z'_has_bullet_sections.<locals>.<genexpr>)r=   ÚSTRUCTURED_SUMMARY_KEYSrJ   r"   rJ   r#   Ú_has_bullet_sections†   s   rL   c                 C  s   |   ¡  ¡ }|dv S )N>   ún/aúnot applicabler   ÚnaÚnone)r   r(   )r   Úcleanedr"   r"   r#   Ú_is_empty_valueŠ   s   rR   z^\s*no follow[- ]?up tasks?\bz&\bno follow[- ]?up tasks? identified\bz^\s*no (feature )?tasks?\bz\bnone identified\bz\bnot applicable\bz\bn/a\bz\bscored\s*\d+\s*/\s*\d+\bz\bquality\s*score\bz(\bparameter\s*(score|check|evaluation)\bz^\s*none\.?\s*$z^\s*n/?a\.?\s*$z^\s*nil\.?\s*$z	^\s*-\s*$c                   s–   d  t| pdƒ ¡ ¡ ¡ }t|ƒdk rdS | ¡ ‰ d}t‡ fdd„|D ƒƒr<t|ƒdk r<t d	ˆ ¡s:d
ˆ v s:dˆ v r<dS t	D ]
}| |¡rH dS q>dS )zNFalse for empty, tiny, or junk analytics bullets that should not show or push.ú r   é   F)	zno follow-upzno follow upzno followupznothing to followzno action requiredzno next stepznot identifiedzno feature taskzno tasks identifiedc                 3  ó    | ]}|ˆ v V  qd S r7   r"   )r8   Úp©r(   r"   r#   r;   ²   ó   € z,is_actionable_task_bullet.<locals>.<genexpr>éP   z^(no|none|n/a|nil)\bÚ
identifiedÚ
applicableT)
Újoinr   r)   r   r1   r(   r=   r&   ÚsearchÚ_ACTIONABLE_TASK_JUNK_PATTERNS)r!   rQ   ÚplaceholdersÚpatternr"   rW   r#   Úis_actionable_task_bullet¡   s   "
ÿra   ú	List[Any]c                 C  sj   g }t ƒ }| pg D ])}d t|pdƒ ¡ ¡ ¡ }|rt|ƒsq	| ¡ }||v r(q	| |¡ | |¡ q	|S )zHDrop junk bullets from feature/follow-up/task lists for UI and CRM push.rS   r   )	r%   r\   r   r)   r   ra   r(   Úaddr   )r   r>   Úseenr    r!   rI   r"   r"   r#   Úfilter_actionable_task_bullets»   s   
re   c                 C  s@   t  d| t j¡}|sg S | d¡ ¡ }t  d|¡}dd„ |D ƒS )NzEservices like (.+?)(?: and scheduled| and agreed| and confirmed|\.|$)é   z	,|\band\bc                 S  s    g | ]}|  d ¡r|  d ¡‘qS )z .r@   )r8   Únamer"   r"   r#   rB   Õ   s     z*_extract_service_names.<locals>.<listcomp>)r&   r]   Ú
IGNORECASEÚgroupr   r)   )r!   ÚmatchÚchunkÚnamesr"   r"   r#   Ú_extract_service_namesË   s   ýrm   Úai_responseúOptional[Dict[str, Any]]c                   s°  t |  d¡p|  d¡pdƒ ¡ }|sdS g }g }g }g }g }t |  d¡p%dƒ ¡ }t |  d¡p0dƒ ¡ }|rBt|ƒsB| | d¡¡ |rPt|ƒsP| | d¡¡ t|ƒD ]{}	|	 d¡}
|
 ¡ ‰ t‡ fdd	„t	D ƒƒrp| |
¡ qTd
ˆ v rt
jd|
dt
jd\}}| ¡ rŽ| | ¡  d¡¡ | ¡ rœ| d| ¡ › ¡ qTt‡ fdd	„tD ƒƒr®| |
¡ qTt‡ fdd	„tD ƒƒr¿| |
¡ qTt‡ fdd	„dD ƒƒrÏ| |
¡ qTt|ƒD ]}| |¡ qÔ|  d¡pâi }t|tƒr| ¡ D ])}t|tƒsõqít | d¡püdƒ ¡ }|rd| ¡ v r| |dd…  d¡¡ qí|t|ƒt|ƒtt|ƒƒtt|ƒƒtt|ƒƒdœ}t|ƒsOt|ƒ}t|ƒdkrOtdd„ |dd… D ƒƒ|d< t|ƒrV|S dS )zTBuild bullet sections from legacy flat analytics when structured_summary is missing.Úoverall_summaryÚsummaryr   NÚcall_purposeÚobjections_concernsÚ.c                 3  rU   r7   r"   ©r8   r`   rW   r"   r#   r;   ñ   rX   z8derive_structured_summary_from_legacy.<locals>.<genexpr>z and scheduled z\band scheduled\brf   )ÚmaxsplitÚflagsz
Scheduled c                 3  rU   r7   r"   ru   rW   r"   r#   r;   ý   rX   c                 3  rU   r7   r"   ru   rW   r"   r#   r;     rX   c                 3  rU   r7   r"   )r8   ÚphraserW   r"   r#   r;     s
   € ÿ
ÿ)zexpressed interestzmentioned havingÚ	confirmedÚagreedzcustomer fromz
looking toz	team sizeÚsalespersonÚ
parametersÚ	reasoningÚfollowéÈ   ©Úoverviewr   r   r	   r
   r   c                 S  s   g | ]}|  d ¡‘qS )rt   )Úrstrip)r8   Úsentencer"   r"   r#   rB   -  s    z9derive_structured_summary_from_legacy.<locals>.<listcomp>é   r   )r   rG   r   rR   r   r‚   rD   r(   r=   Ú_POINTS_PATTERNSr&   r)   rh   Ú_FOLLOWUP_PATTERNSÚ_FEATURE_PATTERNSrm   r   ÚdictÚvaluesr?   re   rL   r1   )rn   ÚoverallÚpointsÚ	takeawaysÚfeaturesÚ	followupsr   rr   Ú
objectionsrƒ   Ú
normalizedÚbeforeÚafterrg   ÚparamsÚpdatar}   rE   Ú	sentencesr"   rW   r#   Ú%derive_structured_summary_from_legacyØ   s~    



þ
€
€


ú
	ÿr–   Úrawc                   s²   t | tƒsd S t|  d¡pdƒ ¡ }|tt|  d¡ƒƒtt|  d¡ƒƒttt|  d¡ƒƒƒttt|  d¡ƒƒƒttt|  d¡ƒƒƒdœ‰ t|ƒpRt	‡ fd	d
„t
D ƒƒ}|rWˆ S d S )Nr   r   r   r   r	   r
   r   r€   c                 3  s    | ]}ˆ | V  qd S r7   r"   rH   ©r   r"   r#   r;   G  rX   z/normalize_structured_summary.<locals>.<genexpr>)r   rˆ   r   rG   r   r?   r$   re   r0   r=   rK   )r—   r   Úhas_contentr"   r˜   r#   Únormalize_structured_summary3  s$   
ÿÿÿörš   c                 C  sÊ   t | tƒsdS t|  d¡p|  d¡pdƒ ¡ }t|  d¡ƒ}|r4t|ƒr4|d s.|r.||d< || d< |S t| ƒ}|r@|| d< |S |rR|d sL|rL||d< || d< |S |rc|g g g g g dœ}|| d< |S dS )z=Normalize LLM output or derive legacy bullets for UI display.Nrp   rq   r   Ústructured_summaryr   r€   )r   rˆ   r   rG   r   rš   rL   r–   )rn   rŠ   rE   ÚderivedÚfallbackr"   r"   r#   Úensure_structured_summaryK  s:   
 úrž   Úraw_responsec                 C  s   t | tƒs| S t| ƒ | S )z[Attach structured_summary to a parsed raw_response dict (non-destructive for other fields).)r   rˆ   rž   )rŸ   r"   r"   r#   Úenrich_raw_responses  s   
r    c                 C  s€   g }t |  d¡p	dƒ ¡ }|r| |¡ tD ]$}|  |¡pg }|s"qt ||¡}| |› dd dd„ |D ƒ¡ ¡ qd |¡S )Nr   r   z: z; c                 s  s    | ]}t |ƒV  qd S r7   )r   )r8   r-   r"   r"   r#   r;   †  rX   z-flatten_structured_summary.<locals>.<genexpr>z

)r   rG   r   r   rK   ÚSTRUCTURED_SUMMARY_LABELSr\   )rE   rC   r   rI   ÚbulletsÚlabelr"   r"   r#   Úflatten_structured_summary{  s   
&
r¤   Úcall_summary_onc                C  sJ   |sdS t | ƒ t|  d¡pdƒ ¡ }|s#|  d¡}t|tƒr#t|ƒS |S )zPNormalize structured_summary on ai_response and return flat summary text for DB.r   rp   r›   )rž   r   rG   r   r   rˆ   r¤   )rn   r¥   r!   Ú	persistedr"   r"   r#   Úresolve_call_summary_text‹  s   

r§   )r   r   r   r   )r!   r   r   r%   )r+   )r,   r   r-   r   r.   r/   r   r0   )r   r   r   r   )r!   r   r   r   )rE   rF   r   r0   )r   r   r   r0   )r!   r   r   r0   )r   rb   r   r   )rn   rF   r   ro   )r—   r   r   ro   )rŸ   r   r   r   )rE   rF   r   r   )rn   rF   r¥   r0   r   r   )#Ú__doc__Ú
__future__r   r&   Útypingr   r   r   r   rK   r¡   ÚSTRUCTURED_SUMMARY_INSTRUCTIONSÚ STRUCTURED_SUMMARY_JSON_TEMPLATEr…   r†   r‡   r$   r*   r6   r?   rD   rL   rR   ÚcompileÚIr^   ra   re   rm   r–   rš   rž   r    r¤   r§   r"   r"   r"   r#   Ú<module>   s\    	û






ó




[

(
