
    jis/                    F   U d Z ddlmZ ddlZddlZddlZddlZddlmZmZm	Z	 ddl
mZ ddlZddlmZ ddlmZ ddlmZ dd	lmZ dd
lmZ ddlmZ ddlmZ  ej6                  d      Zg dZg dZdZddZ ddZ!	 	 	 	 	 	 	 	 ddZ" e#       Z$de%d<   ddZ&	 	 d	 	 	 ddZ'y)u.  任務認領偵測(階段 9-B)。

情境:
  Robert 在 LINE 群組:「阿綠 週五前把客戶 A 的提案 v3 寄出去」
  → task_extractor 自動建任務 `<X>`,assignee=阿綠,寫 task_create_auto activity
  阿綠回:「OK 收到」
  → 這支服務:看到阿綠在「同 channel」「24 hr 內」「對應自己被派的任務」回了確認語,
     寫 `task_accepted` activity 標記認領

判斷邏輯:
  1. 訊息進來,只看 user_id != null(庫柏自己跳過)
  2. 字數 < 30 才有可能是「短確認」(回長段討論不算)
  3. 關鍵字 prefilter:含「OK / 好 / 收到 / 沒問題 / 了解 / 收 / 接 / 行 / 知道」之一
  4. 找最近 24hr 內 task_create_auto activities,assignee_id == sender_id 且 channel_id 相同
  5. 對每筆候選用 Haiku 判斷「這訊息算對該任務的接受嗎」(避免「不接」「沒空接」誤判)
  6. 信心 ≥ 0.7 → 寫 task_accepted activity,extra 含 task_id, source_msg_id, confidence

判斷加速:
  - 沒候選任務 → 完全不打 Haiku
  - 多個候選一次給 Haiku 判斷
    )annotationsN)datetime	timedeltatimezone)UUID)select)settings)AsyncSessionLocal)AgentActivity)Message)Task)Userzcoba.task_acceptance)okOKOk   好u   好的u   好喔u   好阿u   好啊   收到   已收u   收下u	   已收到	   沒問題z
no problemu   沒事   了解u   懂了u   知道   可以   行   成u   可u   接u   接下u   我接u   我來   👌   ✅u   ✓)
u   不行u	   不可以u	   做不到u   沒空u	   沒辦法u   不接u   我不u   問u	   為什麼u   怎麼   c                      syt               dkD  ry j                         t         fdt        D              ryt        fdt        D              S )NF<   c              3  &   K   | ]  }|v  
 y wN ).0rhtexts     @/volume1/homes/robertsu/coba/app/app/services/task_acceptance.py	<genexpr>z#_has_accept_hint.<locals>.<genexpr>G   s     
."2:   c              3  B   K   | ]  }|j                         v   y wr    )lower)r"   hlows     r%   r&   z#_has_accept_hint.<locals>.<genexpr>I   s     7AqwwyCs   )lenr)   any_REJECT_HINTS_ACCEPT_HINTS)r$   r+   s   `@r%   _has_accept_hintr0   A   sD    
4y2~
**,C

.
..7777    c           
       K   t         j                  syt               4 d{   }|j                  t	        t
              j                  t
        j                  | k(               d{   j                         }||j                  	 ddd      d{    y|j                  xs dj                         t              s	 ddd      d{    y|j                  }|j                  }|j                  }t        j                   t"        j$                        t'        t(              z
  }|j                  t	        t*              j                  t*        j,                  dk(  t*        j                  |k\  t*        j                  |k        j/                  t*        j                  j1                                      d{   j3                         j5                         }g }|D ]  }	|	j6                  xs i }
t9        |
j;                  d            t9        |      k7  r;|
j;                  d      }|sO	 t=        |      }|j                  t	        tB              j                  tB        j                  |k(  tB        jD                  jG                  d                   d{   j                         }||jH                  |k7  r|j                  t	        t*              j                  t*        j,                  dk(        j/                  t*        j                  j1                               jK                  d             d{   j3                         j5                         }|D 	ch c]!  }	|	j6                  xs i j;                  d      # }}	t9        |      |v r|jM                  |	f        |s	 ddd      d{    y|j                  t	        tN              j                  tN        j                  |k(               d{   j                         }|r|jP                  nd	}ddd      d{    d
}tS              dk  r|tU        fd|D              rhd   \  }}	tW        |j                  | |	j                  d       d{    tX        j[                  d|j\                  dd tS        |      dd        ydj_                  d ta              D              }d d d| d}	 tc        jd                  t         j                        }|jf                  ji                  t         jj                  ddd|dg       d{   }dj_                  d |j                  D              }tm        jn                  d |tl        jp                  !      }|syts        jt                  |jw                  d            }|j;                  d"      syty        |j;                  d#      xs d$      }|j;                  d%      }t{        |t|              rd&|cxk  rtS        |      k  sy y|d'k  ry||d&z
     \  }}	tW        |j                  | |	j                  |       d{    tX        j[                  d(||j\                  dd |dd        y7 l7 )7 7 7 # t>        t@        f$ r Y w xY w7 7 xc c}	w 7  7 7 # 1 d{  7  sw Y   xY w7 Q7 7 # t~        $ r tX        j                  d)       Y yw xY ww)*uI   背景任務:看這則訊息是否認領了某個自動建出的任務。N hourstask_create_auto
channel_idtask_idtask_accepted2   u   (未知))r   r   r   r   r   r   r   r   r   r   r   r      c              3  &   K   | ]  }|v  
 y wr    r!   )r"   kwcontents     r%   r&   z&try_link_acceptance.<locals>.<genexpr>   s     !N:MB"-:Mr'   r   g?)source_act_idconfzAtask_accepted (fast-path): user=%s task=%s n_candidates=%d msg=%s(      
c              3  T   K   | ]   \  }\  }}d |dz    d|j                    d " yw)u     - 任務    u   : 《u   》N)title)r"   it_s       r%   r&   z&try_link_acceptance.<locals>.<genexpr>   s2     cMb	6AqQqSEqwwis;Mbs   &(u
   訊息:「u   」
傳訊者:u'   
傳訊者最近 24hr 被派的任務:
u   

判斷:這則訊息是否表達『接受/承接』這些任務?
嚴格 JSON 回應(不要說明、不要 markdown):
{"is_acceptance": true/false, "confidence": 0.0-1.0, "task_index": 1-based 的任務編號 或 null(若不接受或無法判斷)})api_keyx   uE   你是「任務認領判斷員」。只回嚴格 JSON,沒其他字。user)roler>   )model
max_tokenssystemmessagesc              3  T   K   | ]   }|j                   d k(  s|j                   " yw)r$   N)typer$   )r"   bs     r%   r&   z&try_link_acceptance.<locals>.<genexpr>   s      Glaff6Faffls   ((z\{.*\})flagsis_acceptance
confidenceg        
task_indexrE   g?z/task_accepted: user=%s task=%s conf=%.2f msg=%su"   task_acceptance Haiku 判斷失敗)Ar	   ANTHROPIC_API_KEYr
   executer   r   whereidscalar_one_or_noneuser_idr>   stripr0   r7   
created_atr   nowr   utcr   ACCEPT_WINDOW_HOURSr   activity_typeorder_bydescscalarsallextrastrgetr   
ValueError	TypeErrorr   
deleted_atis_assignee_idlimitappendr   display_namer,   r-   _record_acceptanceloggerinforF   join	enumerate	anthropicAsyncAnthropicrQ   createCLAUDE_MODEL_HAIKUresearchDOTALLjsonloadsgroupfloat
isinstanceint	Exception	exception)
message_iddbmsg	sender_idr7   msg_created_atcutoffacts
candidatesari   tid_strtidrH   alreadyalready_task_idssendersender_nameSHORT_CONFIRM_HINTStitlespromptclientresprawmresultr@   idxr>   s                               @r%   try_link_acceptancer   L   s    %% ""bZZw 5 5gjjJ6N OPPddf;#++- #"" ;;$"++-( #"" KK	^^
 hll+i>Q.RRjj=!''++/AA((F2((N: h}//4467
 
 79SSU 	 
AGGMrE599\*+s:>ii	*G7m zzt""477c>4??3F3Ft3LM !!#  y}}	)ZZ}%++!//?B(=3388:;EE"I  wy	 
 IPP1B 3 3I >P3x++q!f%7 : i #""j 

6$<#5#5dgg6J#KLL``b-3f))m #"t @
7|rc!N:M!NN!}1 z9gUVUYUY`cdddW"s:	N YYcYWaMbccF
WI  M "228 :X	Y ?))(2L2LM__++--Z%&9:	 , 
 
 ggGdllGGIIiBII6AGGAJ'zz/*VZZ-45jj&#s#A,GJ,G -H$;#'"1 z9g/0tt$@ 	@ 	@E"tWSb\	CK #P #"
$ 	* 
  Q] #j Mk #"""z 	e$
(	@  ?=>?s   \
Z\
A[)Z*![\
Z\
*[\
Z\
C([?Z A3[4Z?A[Z4B [?Z7 $[$&Z:
*[4\
?Z? \
A[["[)\
4[5A\
[A$\
3A[& [!A[& \
5[& \
A[& $\
%[& &\
'[& ,\
-3[&  [$!-[& \
[\
\
[Z1-[0Z11[7[:[?\
[\
[[[	\
![& $[& &\\
\\
c                J  K   t               4 d {   }|j                  t        d|d|dd|d d  dt        |       t        |      t        |      |d             |j	                          d {    d d d       d {    y 7 v7 7 	# 1 d {  7  sw Y   y xY ww)	Nr9   u   認領任務(信心 z.2fz):r   T)r8   acceptance_msg_idsource_create_act_idrW   )rd   r^   summarysuccessri   )r
   addr   rj   commit)r8   msg_idr   r>   r?   r@   r   s          r%   rt   rt      s      ""b
})*4*Bws|nEw<%([(+M(:"	
 	 iik #"" 	 #"""sW   B#BB#AB2B
3B7B#BB#
BB#B BB B#set_PENDING_TASKSc                    ddl }	 |j                         }|j                  t	        |             }t
        j                  |       |j                  t
        j                         y# t        $ r Y yw xY w)u9   fire-and-forget background task — 強引用避免 GC。r   N)	asyncioget_running_loopRuntimeErrorcreate_taskr   r   r   add_done_callbackdiscard)r   r   looptasks       r%   trigger_acceptancer      sf    '') /
;<Dt>112	  s   A% %	A10A1c                (
  K   t        j                  t        j                        t	        |      z
  }t               4 d{   }|j                  t        t              j                  t        j                  dk(  t        j                  |k\        j                  t        j                  j                                      d{   j                         j                         }| r;|D cg c]0  }|j                   xs i j#                  d      t%        |       k(  s/|2 }}g }|D ]?  }|j                   xs i j#                  d      }|s%	 |j'                  t)        |             A |sg cddd      d{    S i }|j                  t        t.              j                  t.        j0                  j3                  |                   d{   j                         j                         }	|	D ]  }
|
||
j0                  <    |j                  t        t              j                  t        j                  dk(  t        j                  |k\               d{   j                         j                         }i }|D ])  }|j                   xs i j#                  d      }|s%|||<   + t5               }|j7                         D ]*  }
|
j8                  s|j;                  |
j8                         , i }|r|j                  t        t<              j                  t<        j0                  j3                  |                   d{   j                         j                         }|D ]  }|||j0                  <    g }|D ]'  }|j                   xs i j#                  d      }|s&	 t)        |      }|j#                  |      }
|
|
j>                  rRd}|
j8                  r)|j#                  |
j8                        }|r|j@                  }|j#                  |      }|j'                  t%        |      |
jB                  |
jD                  ||j                  jG                         |du|r|j                  jG                         nd|r|j                   xs i j#                  d      ndd	       * |cddd      d{    S 7 w7 c c}w # t*        t,        f$ r Y w xY w7 b7 7 7 # t*        t,        f$ r Y }w xY w7 H# 1 d{  7  sw Y   yxY ww)
u   列最近 N 小時自動建出的任務 + 認領狀態。

    回傳 list:每筆 {task_id, title, assignee_name, status, accepted, accepted_at, accepted_msg_preview}
    r4   Nr6   r7   r8   r9   u   (未指派)rW   )r8   rF   statusassignee_namer`   acceptedaccepted_ataccepted_confidence)$r   ra   r   rb   r   r
   rZ   r   r   r[   rd   r`   re   rf   rg   rh   ri   rk   rj   rr   r   rl   rm   r   r\   in_r   valuesrp   r   r   rn   rs   rF   r   	isoformat)r7   r5   r   r   createsr   task_idsr   	tasks_maptsrH   accepts
accept_mapr   users_needed	users_mapusur   r   acks                        r%   list_assignment_statusr      s%     \\(,,')%*@@F ""b=!''++/AA((F2 h}//4467	
 

 79SSU 	 ")b'Qaggm-@-@-NRUV`Ra-aq'Gb A77=b%%i0COODI.	  + #"". 	JJvd|11$''++h2GHIIRRTXXZAIaddO  =!''++>((F2
 

 79SSU 	 
Aww}")))4G&'
7# 
 u!!#A}}  / $ 	

6$<#5#5dggkk,6O#PQQZZ\``bB"#	!$$  Aww}")))4G7m c"AyALL)M}}MM!--0$%NNM..)CMMs8((!.ll446tO=@s~~779dNQ		R'<'<\'JW[	 	# 6 _ #""
 c #I. % #0 J

$ R 	* u #"""s   ?TR8TA?S=R;&S=+0R>R> *S=S&S=+T7S8T=AS=SB	S=SAS= 0S=A.S=?S" A S=!S%,C:S=&T2S;3T;S=>S=SS=SS=TS=S="S=%S84S=7S88S=;T=TTTT)r$   rj   returnbool)r   r   r   None)r8   r   r   r   r   r   r>   rj   r?   r   r@   r   r   r   )Nr   )r7   zUUID | Noner5   r   r   z
list[dict])(__doc__
__future__r   r   loggingosr}   r   r   r   uuidr   ry   
sqlalchemyr   
app.configr	   app.databaser
   app.models.agent_activityr   app.models.channelr   app.models.taskr   app.models.userr   	getLoggerru   r/   r.   rc   r0   r   rt   r   r   __annotations__r   r   r!   r1   r%   <module>r      s   * #   	 	 2 2     * 3 &     
		1	2	
  8m?`'*;?GLQU& e 	3" <@/1V),V6@Vr1   