
    	]j~L                     0   d Z ddlZddlmZmZ ddlmZmZmZ ddl	m
Z
 ddlmZmZ ddlmZmZmZ ddlmZmZ dd	lmZ dd
lmZ ddlmZmZmZ ddlmZ ddl m!Z!m"Z" ddl#m$Z$m%Z%  ejL                  e'      Z(dZ) ee
jT                        Z+ G d dejX                        Z- G d dejX                        Z. G d dej^                        Z0 G d dej^                        Z1 G d dej^                        Z2d Z3 eee0      d        Z4 eee0      d        Z5y)zThis file and its contents are licensed under the Apache License 2.0. Please see the included NOTICE for copyright information and LICENSE for a copy of the license.
    N)DictList)conditional_atomicdb_is_not_sqlite	load_func)settings)modelstransaction)Count	JSONFieldQ)	post_save
pre_delete)receiver)gettext_lazy)PREDICT_URLTIMEOUT_PREDICTMLApi)Project)PredictionSerializerTaskSimpleSerializer)WebhookWebhookSerializer   c                   p    e Zd Zd ed      fZd ed      fZd ed      fZd ed      fZd	 ed
      fZy)MLBackendStateCO	ConnectedDIDisconnectedERErrorTRTrainingPR
PredictingN)	__name__
__module____qualname___	CONNECTEDDISCONNECTEDERRORTRAINING
PREDICTING     @/root/env/lib/python3.12/site-packages/label_studio/ml/models.pyr   r      sI    an$I>**L!G*EQz]"Hq&Jr1   r   c                   4    e Zd Zd ed      fZd ed      fZy)MLBackendAuthNONENone
BASIC_AUTHz
Basic AuthN)r'   r(   r)   r*   r5   r7   r0   r1   r2   r4   r4   !   s    1V9Dq.Jr1   r4   c                       e Zd ZdZ ej
                  dej                  ej                        Z	 ej                   ed      dd      Z ej                   ed      d	
      Z ej                   ed      ddd      Z ej                   ed      dddd      Z ej
                  dej                  ej$                        Z ej                   ed      dddd      Z ej                   ed      dddd      Z ej                   ed      dddd      Z e ed      dd      Z ej                   ed      dddd      Z ej4                   ed       dd!d"#      Z ej8                  eej<                  d$%      Z ej@                   ed&      d'      Z! ej@                   ed(      d)      Z" ej                   ed*      dd+      Z#d, Z$ fd-Z% fd.Z&e'dBd/       Z(d0 Z)e'dBd1       Z*d2 Z+d3 Z,e-d4        Z.e-d5        Z/d6 Z0d7 Z1d8 Z2d9e3e4   d:e3e4   d;e3e4   fd<Z5d9e3e4   d;e3e4   fd=Z6d> Z7dCd?Z8e'd@        Z9dA Z: xZ;S )D	MLBackend    )
max_lengthchoicesdefaultis_interactiveFzRUsed to interactively annotate tasks. If true, model returns one list with resultsr>   	help_texturlz)URL for the machine learning model server)rA   error_messageTzError message in error stateblanknullrA   titler>   z$Name of the machine learning backend)rE   rF   r>   rA      zbasic auth user zHTTP Basic Auth userzbasic auth passwordzHTTP Basic Auth passworddescriptionz,Description for the machine learning backendzextra paramsz>Any extra parameters passed to the ML Backend during the setup)rF   rA   model versionzCCurrent model version associated with this machine learning backendtimeoutg      Y@zResponse model timeout)rE   r>   rA   ml_backends)	on_deleterelated_name
created atauto_now_add
updated atauto_nowauto_updatezZIf false, model version is set by the user, if true - getting latest version from backend.c                 T    | j                    d| j                   d| j                   dS )Nz (id=z, url=))rG   idrB   selfs    r2   __str__zMLBackend.__str__   s'    **U477)6$((1==r1   c                 N    t        t        | 
  |i | | j                  | _        y N)superr9   __init__rG   _MLBackend__original_title)r[   argskwargs	__class__s      r2   r`   zMLBackend.__init__   s#    i'88 $

r1   c                 |   | j                   }| j                  | j                  k7  r{|j                  | j                  k(  rbt	        j
                         5  | j                  |_        |j                  dg       t        |   |i | | j                  | _        ddd       yt        |   |i | y# 1 sw Y   yxY w)a>  
        Overrides the save() method to update the associated project's model_version field.
        If the title of the model instance is changed and the model_version
        of the related project is currently the same as the original title,
        the project's model_version is updated to the new title.
        model_versionupdate_fieldsN)projectrG   ra   rf   r
   atomicsaver_   )r[   rb   rc   prd   s       r2   rk   zMLBackend.save   s     LL::...1??dF[F[3[##% 3"&**o%67d-f-(,

%3 3 GL$)&)3 3s   AB22B;c                 :    t        d| |d|j                         S )NrB   auth_methodr0   )r   health)rB   ro   rc   s      r2   healthcheck_zMLBackend.healthcheck_   s     @+@@GGIIr1   c                 Z    | j                   |_         | j                   j                  |      S r^   )ri   has_permission)r[   users     r2   rs   zMLBackend.has_permission   s"    ||||**400r1   c                     t        d| |d|}t        |t              s t        j                  j	                  |      } |j
                  |fi |S Nrn   )pkr0   )r   
isinstancer   objectsgetsetuprB   ri   ro   rc   apis        r2   setup_zMLBackend.setup_   sM    ???'7+oo))W)5Gsyy+F++r1   c                 |    | j                  | j                  | j                  | j                  | j                        S N)basic_auth_userbasic_auth_pass)rq   rB   ro   r   r   rZ   s    r2   healthcheckzMLBackend.healthcheck   s:      HHd&&8L8L^b^r^r ! 
 	
r1   c                     | j                  | j                  | j                  | j                  | j                  | j
                  | j                        S )N)extra_paramsr   r   )r~   rB   ri   ro   r   r   r   rZ   s    r2   r{   zMLBackend.setup   sJ    {{HHLL** 00 00  
 	
r1   c                     t        | j                  | j                  | j                  | j                  | j
                        S )N)rB   rL   ro   r   r   )r   rB   rL   ro   r   r   rZ   s    r2   r}   zMLBackend.api   s8    LL(( 00 00
 	
r1   c                 Z    | j                   t        j                  t        j                  fv S r^   )stater   r,   r-   rZ   s    r2   	not_readyzMLBackend.not_ready   s"    zzn99>;O;OPPPr1   c                 v   d }| j                         j                  rt        j                  | _        n| j                         }|j                  rIt        j                  d|j                          t        j                  | _        |j                  | _        nt        j                  | _        |j                  j                  d      }t        j                  d|j                          | j                  r,t        j                  d| j                   d|        || _        d | _        | j!                          |S )Nz ML backend responds with error: rf   z"ML backend responds with success: zChanging model version: z -> )r   is_errorr   r,   r   r{   loggerinforC   r-   r+   responserz   rV   debugrf   rk   )r[   rf   setup_responses      r2   update_statezMLBackend.update_state   s    &&'44DJ!ZZ\N&&>~?[?[>\]^+11
%3%A%A"+55
 . 7 7 ; ;O L@AXAX@YZ[##LL#;D<N<N;OtTaSb!cd)6D&%)"		r1   c                 z   | j                   j                  | j                        }|j                  r't        j
                  | _        |j                  | _        nSt        j                  | _        |j                  j                  d      }|r!t        j                  j                  ||        | j                          y )Njob)job_id
ml_backend)r}   trainri   r   r   r-   r   rC   r.   r   rz   MLBackendTrainJobry   createrk   )r[   train_responsecurrent_train_jobs      r2   r   zMLBackend.train   s    5""'--DJ!/!=!=D'00DJ . 7 7 ; ;E B !))008IVZ0[		r1   c                    | j                   }t        |      j                  }|j                  |g| j                        }|j                  t        |dt              }|j                  r&t        j                  d|  d|j                          y|j                  j                  dd      }d|j                  |j                  |j                  t              |||dd	S )
z>This is low level prediction method that is used for debuggingF)verboserL   #Prediction not created for project : Nresults   )statusrC   rB   taskrequestr   )r   data)r}   r   r   _prep_prediction_reqri   _requestr   r   r   r   r   rC   r   rz   status_code_get_url)r[   r   ml_apitask_serrequest_paramsml_api_resultr   s          r2   _predictzMLBackend._predict   s    '-2244hZN^U\kl!!KK=dV2mFaFaEbcd((,,Y= '33!.!<!<{3 )#

 
	
r1   serialized_taskscurrent_responsesreturnc                    t        |      dk(  rOt        j                  d| j                   d       g }|D ]#  }|j	                  | j                  |g             % |S t        j                  dt        |       dt        |       d       g S )z
        This is helper method to get predictions from ML backend one by one
        in case when tasks length doesn't match responses length
        Note: don't use this function outside of this class
        r   z'ML backend 'zR' doesn't support batch processing of tasks, switched to one-by-one task retrievalz-Number of tasks and responses are not equal: z
 tasks != z( responses. Returning empty predictions.)lenr   warningrG   extend _get_predictions_from_ml_backenderror)r[   r   r   predictionsserialized_tasks        r2   +_get_predictions_from_ml_backend_one_by_onez5MLBackend._get_predictions_from_ml_backend_one_by_one  s      !Q& NN

| ,8 9 K#3 ]""4#H#H/IZ#[\]  LL?'()C8I4J3K L/0
 Ir1   c                    | j                   j                  || j                        }|j                  r$t        j                  d|j                          g S t        |j                  t              rd|j                  vr$t        j                  d|j                          g S t        |j                  d   t              rt        |j                  d         dk(  rt        j                  d       g S |j                  d   }g }t        |      t        |      k7  r| j                  ||      S t        ||      D ]  \  }}t        |t              r|g}|D ]h  }d|vrt        j                  d|         |j                  |d   |d   |j                  d	      |j                  d
| j                         |d   d       j  |S )NzError occurred: r   =ML backend returns an incorrect response, it must be a dict: r   z]ML backend returns an incorrect response, results field must be a list with at least one itemresultzYML backend returns an incorrect prediction, it should be a dict with the 'result' field: rY   scorerf   ri   )r   r   r   rf   ri   )r}   make_predictionsri   r   r   r   rC   rx   r   dictlistr   r   zipappendrz   rf   )r[   r   r   	responsesr   r   r   rs           r2   r   z*MLBackend._get_predictions_from_ml_backend$  s   **+;T\\J ??LL+F,@,@+ABCIFOOT2iv6VLLXY_YhYhXijkIFOOI6=V__U^E_A`deAeLLo IOOI.	 C	N2 CCDTV_`` ""2I> 	ND((D)$:  1$LL3  "" $T
"#H+!"w)*@R@R)S#'	?	, r1   c                    | j                         }| j                  rt        j                  d|  d       y t	        |t
              r:ddlm} |j                  j                  |D cg c]  }|j                   c}      }|j                  t        d            j                  t        d      t        |	      z        }|j                         s$t        j                  d
| j                           |S t#        |d      j$                  }| j'                  |      }t)        t*              5  t-        |d      }|j/                  d       |j1                         }d d d        |S c c}w # 1 sw Y   S xY w)NzML backend z is not readyr   )Task)id__inr   )predictions_count)predictions_count__gt)predictions__model_versionz5All tasks already have prediction from model version=T)many)	predicate)r   r   )raise_exception)r   r   r   r   rx   r   tasks.modelsr   ry   filterrY   annotater   excluder   existsrf   r   r   r   r   r   r   is_validrk   )	r[   tasksrf   r   r   	tasks_serr   prediction_ser	instancess	            r2   predict_taskszMLBackend.predict_tasksV  s?   ))+>>LL;tfM:;eT")LL''E/JD/J'KE }1EFNNA&m)TT
 ||~LLPQUQcQcPdef  (T:??	;;IF*:; 	.1{NN##D#9&++-I	.  0K	. s   &E(.0E--E7c                    i }i }|rd|i}| j                   sdg|d<   |S t        |gdg d|      j                  }| j                  j	                  || j
                  |      }|j                  r7t        j                  d|  d	|j                          |j                  g|d<   |S t        |j                  t              rd
|j                  v s*t        j                  d|j                          dg|d<   |S |j                  j                  d
d g      }t        |t              rt        |      dk  r)t        j!                  dt#        |              dg|d<   |S |d   |d<   |S )Nrt   z:Model is not set to be used for interactive preannotationserrorsT)draftsr   annotations)r   expandcontext)r   ri   r   r   r   r   r   z`Incorrect response from ML service: ML backend returns an incorrect response, it must be a dict.r   zAML backend has to return list with 1 annotation but it returned: zZIncorrect response from ML service: ML backend has to return list with more than 1 result.r   r   )r?   #InteractiveAnnotatingDataSerializerr   r}   r   ri   r   r   r   rC   rx   r   r   rz   r   r   r   type)	r[   r   r   rt   r   optionsr   r   
ml_resultss	            r2   interactive_annotatingz MLBackend.interactive_annotatingp  s   tnG"" \]F8M7F&NX_

$ 	 11LL 2 

 !!KK=dV2mFaFaEbcd - ; ;<F8M=1148Y-J`J`=`KKWXeXnXnWopqu F8 M"++//

 *d+s:/BNN^_cdn_o^pqro F8 M#Avr1   c                     t        d| |d|}t        |t              s t        j                  j	                  |      }|j                  |      S rv   )r   rx   r   ry   rz   get_versionsr|   s        r2   get_versions_zMLBackend.get_versions_  sH    ???'7+oo))W)5G((r1   c                     | j                  | j                  | j                  | j                  | j                  | j
                        S r   )r   rB   ri   ro   r   r   rZ   s    r2   r   zMLBackend.get_versions  sC    !!HHLL 00 00 " 
 	
r1   r^   )NN)<r'   r(   r)   __doc__r	   	CharFieldr   r=   r,   r   BooleanFieldr*   r?   	TextFieldrB   rC   rG   r4   r5   ro   r   r   rJ   r   r   rf   
FloatFieldrL   
ForeignKeyr   CASCADEri   DateTimeField
created_at
updated_atrV   r\   r`   rk   staticmethodrq   rs   r~   r   r{   propertyr}   r   r   r   r   r   r   r   r   r   r   r   r   __classcell__)rd   s   @r2   r9   r9   &   s#   F&&++E
 )V((	
jN
 &

	%=C %F$$	/0	M F	'
8E #&""%%""K 'f&&	
(O 'f&&	
 ,O #&""	-@K 	.RL %F$$	/WM  f	)*	G  f.."G
 &%%aoDIJ%%%aoEJ%&%%	-nK>+*& J J1 , ,


 
 
 Q Q*

4 $T
?CDz	d>0d 0PTUYPZ 0d4*X ) )
r1   r9   c                   F   e Zd Z ej                  d      Z ej                  edej                        Z	 ej                   ed      ddd      Z ej                   ed	      d
d      Z ej                   ed      d      Z ej                   ed      d      Zy)MLBackendPredictionJob   r<   prediction_jobsrO   rN   rK   T)Model version this job is associated withrD   z
batch sized   z#Number of tasks processed per batchr@   rP   rQ   rS   rT   N)r'   r(   r)   r	   r   r   r   r9   r   r   r   r*   rf   PositiveSmallIntegerField
batch_sizer   r   r   r0   r1   r2   r   r     s    V-F"""9;LX^XfXfgJ$F$$	/$T=hM 211	,0UJ &%%aoDIJ%%%aoEJr1   r   c                   &   e Zd Z ej                  d      Z ej                  edej                        Z	 ej                   ed      ddd      Z ej                   ed	      d
      Z ej                   ed      d      Zd Zed        Zy)r   r   r   
train_jobsr   rK   Tr   rD   rP   rQ   rS   rT   c                    | j                   j                  }|j                         }|s1t        j	                  d| j
                   d|j
                   d       y |j                  |       }|j                  rF|j                  dk(  rddiS t        j                  d| j
                   d| d|j                          y |j                  S )NzTraining job z*: Can't collect training jobs for project z: ML API is nulli  
job_statusremovedz: ML API returns error )r   ri   
get_ml_apir   r   rY   get_train_job_statusr   r   r   rC   r   )r[   ri   r   r   s       r2   
get_statuszMLBackendTrainJob.get_status  s    //))##%LLy(RSZS]S]R^^no 33D9!!((C/$i00KKy(RSZR[ \((5(C(C'DF %%%r1   c                 0    | j                         }|d   dv S )Nr  )queuedstarted)r  )r[   r   s     r2   
is_runningzMLBackendTrainJob.is_running  s    "l#'<<<r1   N)r'   r(   r)   r	   r   r   r   r9   r   r   r   r*   rf   r   r   r   r  r   r
  r0   r1   r2   r   r     s    V-F"""9<SYSaSabJ$F$$	/=	M &%%aoDIJ%%%aoEJ&& = =r1   r   c                    | j                   r|j                  | j                         y| j                  d   }t	        |t
              rt        |      t        |      k7  r&|j                  dt        |      t        |             yy)NFr   z3Num input tasks is %d but ML API returns %d resultsT)r   r   rC   r   rx   r   r   r   )r   r   curr_loggerr   s       r2   _validate_ml_api_resultr    sr    445$$Y/Ggt$GE
(BQSVW\S]_bcj_klr1   )senderc                     |j                   }|j                  |j                  k(  rd|_        |j                  dg       y y )NrI   rf   rg   )ri   rf   rG   rk   )r  instancerc   ri   s       r2   modify_project_model_versionr    s=    G. "O#45 /r1   c                    |sy |}|j                   j                  d      dz   }|j                  }t        j                  j                  ||      j                         rt        j                  d| d| d       y t        j                  d|        t        t        |j                  |dd	      
      }|j                         r|j                  |j                         y y )N/z/webhook)ri   rB   zWebhook z already exists for project z: skip creating new one.zCreate ML backend webhook T)ri   rB   send_payloadsend_for_all_actions)r   )organization)rB   rstripri   r   ry   r   r   r   r   r   r   rY   r   rk   r  )r  r  createdrc   r   webhook_urlri   sers           r2   create_ml_webhookr    s    J..'',z9K  Gg;?FFHh{m+GyPhij
KK,[M:;
'**+D_cdC ||~g223 r1   )6r   loggingtypingr   r   core.utils.commonr   r   r   django.confr   	django.dbr	   r
   django.db.modelsr   r   r   django.db.models.signalsr   r   django.dispatchr   django.utils.translationr   r*   ml.api_connectorr   r   r   projects.modelsr   tasks.serializersr   r   webhooks.serializersr   r   	getLoggerr'   r   MAX_JOBS_PER_PROJECTINTERACTIVE_DATA_SERIALIZERr   TextChoicesr   r4   Modelr9   r   r   r  r  r  r0   r1   r2   <module>r.     s      M M   ) 0 0 : $ 6 @ @ # H ;			8	$ &/0T0T&U #'V'' '/F&& /
D
 D
NFV\\ F#= #=L
 
*Y'6 (6 
)I&4 '4r1   