o
    l6jš  ã                   @  sä   d Z ddlmZ ddlZddlZddlZddlZe ¡ Z	e	 
ej¡ e d¡Ze d¡Zejd Zejd Zejd Zd	d
„ ejd  d¡D ƒZdd
„ ejd  d¡D ƒZejd Zej dd¡Zd!dd„Zd"dd„Zd#dd „ZdS )$aá  S3-upload trigger that launches one Fargate task per PDF.

An ``s3:ObjectCreated`` event on the input bucket invokes this handler. For
each PDF record it mirrors the inbound key into an output prefix, skips
duplicates and non-PDFs, and calls ``ecs.run_task`` with a per-document command
override. Every decision is logged to CloudWatch so a dropped or duplicate file
leaves an audit trail -- the reason this is a Lambda rather than EventBridge
wired straight to RunTask.

Path mirroring:
    inbound/<company>/<sub>/<file>.pdf  ->  <company>/<sub>/<file>/

Required environment (set by Terraform):
    ECS_CLUSTER, TASK_DEFINITION, CONTAINER_NAME, SUBNETS, SECURITY_GROUPS,
    OUTPUT_BUCKET; optional INPUT_PREFIX (default "inbound/").
é    )ÚannotationsNÚecsÚs3ÚECS_CLUSTERÚTASK_DEFINITIONÚCONTAINER_NAMEc                 C  ó   g | ]}|r|‘qS © r	   ©Ú.0Úsr	   r	   údeploy/lambda/handler.pyÚ
<listcomp>#   ó    r   ÚSUBNETSú,c                 C  r   r	   r	   r
   r	   r	   r   r   $   r   ÚSECURITY_GROUPSÚOUTPUT_BUCKETÚINPUT_PREFIXzinbound/ÚkeyÚstrÚreturnc                 C  s8   |   t¡r| ttƒd… n| }| dd¡d }|› d�S )zKMap an inbound key to its output prefix (path-mirrored, extension dropped).NÚ.é   r   ú/)Ú
startswithr   ÚlenÚrsplit)r   ÚrelÚstemr	   r	   r   Úoutput_prefix)   s   
r    ÚprefixÚboolc                 C  s    t jt| dd�}| dd¡dkS )zDTrue if the output prefix already holds artifacts (duplicate event).r   )ÚBucketÚPrefixÚMaxKeysÚKeyCountr   )r   Úlist_objects_v2r   Úget)r!   Úrespr	   r	   r   Úalready_processed0   s   r*   ÚeventÚdictÚcontextÚobjectc                 C  s6  g }|   dg ¡D ]Ž}|d d d }tj |d d d ¡}| ¡  d¡s,t d|¡ qt|ƒ}t	|ƒr<t d	||¡ qd
|› d|› �}d
t
› d|› �}t d||¡ tjttdddttddœidtd|d|dddgdœgid�}	dd„ |	  dg ¡D ƒ}
|	  dg ¡}|r‹t d||¡ t d|
¡ | |
¡ qd |iS )!NÚRecordsr   ÚbucketÚnamer.   r   z.pdfzskip non-pdf: %sz+skip duplicate: %s already has output at %szs3://r   zRunTask src=%s dst=%sÚFARGATEr   ÚawsvpcConfigurationÚENABLED)ÚsubnetsÚsecurityGroupsÚassignPublicIpÚcontainerOverridesÚtablez-oz--reviewz--llm-backendÚapi)r1   Úcommand)ÚclusterÚtaskDefinitionÚ
launchTypeÚcountÚnetworkConfigurationÚ	overridesc                 S  s   g | ]}|d  ‘qS )ÚtaskArnr	   )r   Útr	   r	   r   r   ^   r   zhandler.<locals>.<listcomp>ÚtasksÚfailureszRunTask failures for %s: %szstarted task(s): %sÚstarted)r(   ÚurllibÚparseÚunquote_plusÚlowerÚendswithÚlogÚinfor    r*   r   r   Úrun_taskÚCLUSTERr   r   r   r   ÚerrorÚextend)r+   r-   rF   Úrecordr0   r   r!   ÚsrcÚdstr)   ÚarnsrE   r	   r	   r   Úhandler6   sN   ýÿþÿÿôrV   )r   r   r   r   )r!   r   r   r"   )r+   r,   r-   r.   r   r,   )Ú__doc__Ú
__future__r   ÚloggingÚosÚurllib.parserG   Úboto3Ú	getLoggerrL   ÚsetLevelÚINFOÚclientr   r   ÚenvironrO   r   r   Úsplitr   r   r   r(   r   r    r*   rV   r	   r	   r	   r   Ú<module>   s(    







